sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<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>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * @children Roo.bootstrap.Component
656  * Bootstrap Element class (basically a DIV used to make random stuff )
657  * 
658  * @cfg {String} html contents of the element
659  * @cfg {String} tag tag of the element
660  * @cfg {String} cls class of the element
661  * @cfg {Boolean} preventDefault (true|false) default false
662  * @cfg {Boolean} clickable (true|false) default false
663  * @cfg {String} role default blank - set to button to force cursor pointer
664  
665  * 
666  * @constructor
667  * Create a new Element
668  * @param {Object} config The config object
669  */
670
671 Roo.bootstrap.Element = function(config){
672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
673     
674     this.addEvents({
675         // raw events
676         /**
677          * @event click
678          * When a element is chick
679          * @param {Roo.bootstrap.Element} this
680          * @param {Roo.EventObject} e
681          */
682         "click" : true 
683         
684       
685     });
686 };
687
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
689     
690     tag: 'div',
691     cls: '',
692     html: '',
693     preventDefault: false, 
694     clickable: false,
695     tapedTwice : false,
696     role : false,
697     
698     getAutoCreate : function(){
699         
700         var cfg = {
701             tag: this.tag,
702             // cls: this.cls, double assign in parent class Component.js :: onRender
703             html: this.html
704         };
705         if (this.role !== false) {
706             cfg.role = this.role;
707         }
708         
709         return cfg;
710     },
711     
712     initEvents: function() 
713     {
714         Roo.bootstrap.Element.superclass.initEvents.call(this);
715         
716         if(this.clickable){
717             this.el.on('click', this.onClick, this);
718         }
719         
720         
721     },
722     
723     onClick : function(e)
724     {
725         if(this.preventDefault){
726             e.preventDefault();
727         }
728         
729         this.fireEvent('click', this, e); // why was this double click before?
730     },
731     
732     
733     
734
735     
736     
737     getValue : function()
738     {
739         return this.el.dom.innerHTML;
740     },
741     
742     setValue : function(value)
743     {
744         this.el.dom.innerHTML = value;
745     }
746    
747 });
748
749  
750
751  /*
752  * - LGPL
753  *
754  * dropable area
755  * 
756  */
757
758 /**
759  * @class Roo.bootstrap.DropTarget
760  * @extends Roo.bootstrap.Element
761  * Bootstrap DropTarget class
762  
763  * @cfg {string} name dropable name
764  * 
765  * @constructor
766  * Create a new Dropable Area
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.DropTarget = function(config){
771     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772     
773     this.addEvents({
774         // raw events
775         /**
776          * @event click
777          * When a element is chick
778          * @param {Roo.bootstrap.Element} this
779          * @param {Roo.EventObject} e
780          */
781         "drop" : true
782     });
783 };
784
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
786     
787     
788     getAutoCreate : function(){
789         
790          
791     },
792     
793     initEvents: function() 
794     {
795         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797             ddGroup: this.name,
798             listeners : {
799                 drop : this.dragDrop.createDelegate(this),
800                 enter : this.dragEnter.createDelegate(this),
801                 out : this.dragOut.createDelegate(this),
802                 over : this.dragOver.createDelegate(this)
803             }
804             
805         });
806         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807     },
808     
809     dragDrop : function(source,e,data)
810     {
811         // user has to decide how to impliment this.
812         Roo.log('drop');
813         Roo.log(this);
814         //this.fireEvent('drop', this, source, e ,data);
815         return false;
816     },
817     
818     dragEnter : function(n, dd, e, data)
819     {
820         // probably want to resize the element to match the dropped element..
821         Roo.log("enter");
822         this.originalSize = this.el.getSize();
823         this.el.setSize( n.el.getSize());
824         this.dropZone.DDM.refreshCache(this.name);
825         Roo.log([n, dd, e, data]);
826     },
827     
828     dragOut : function(value)
829     {
830         // resize back to normal
831         Roo.log("out");
832         this.el.setSize(this.originalSize);
833         this.dropZone.resetConstraints();
834     },
835     
836     dragOver : function()
837     {
838         // ??? do nothing?
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * Body
849  *
850  */
851
852 /**
853  * @class Roo.bootstrap.Body
854  * @extends Roo.bootstrap.Component
855  * @builder-top
856  * @children Roo.bootstrap.Component
857  * @parent none
858  * Bootstrap Body class
859  *
860  * @constructor
861  * Create a new body
862  * @param {Object} config The config object
863  */
864
865 Roo.bootstrap.Body = function(config){
866
867     config = config || {};
868
869     Roo.bootstrap.Body.superclass.constructor.call(this, config);
870     this.el = Roo.get(config.el ? config.el : document.body );
871     if (this.cls && this.cls.length) {
872         Roo.get(document.body).addClass(this.cls);
873     }
874 };
875
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
877
878     is_body : true,// just to make sure it's constructed?
879
880         autoCreate : {
881         cls: 'container'
882     },
883     onRender : function(ct, position)
884     {
885        /* Roo.log("Roo.bootstrap.Body - onRender");
886         if (this.cls && this.cls.length) {
887             Roo.get(document.body).addClass(this.cls);
888         }
889         // style??? xttr???
890         */
891     }
892
893
894
895
896 });
897 /*
898  * - LGPL
899  *
900  * button group
901  * 
902  */
903
904
905 /**
906  * @class Roo.bootstrap.ButtonGroup
907  * @extends Roo.bootstrap.Component
908  * Bootstrap ButtonGroup class
909  * @cfg {String} size lg | sm | xs (default empty normal)
910  * @cfg {String} align vertical | justified  (default none)
911  * @cfg {String} direction up | down (default down)
912  * @cfg {Boolean} toolbar false | true
913  * @cfg {Boolean} btn true | false
914  * 
915  * 
916  * @constructor
917  * Create a new Input
918  * @param {Object} config The config object
919  */
920
921 Roo.bootstrap.ButtonGroup = function(config){
922     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
923 };
924
925 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
926     
927     size: '',
928     align: '',
929     direction: '',
930     toolbar: false,
931     btn: true,
932
933     getAutoCreate : function(){
934         var cfg = {
935             cls: 'btn-group',
936             html : null
937         };
938         
939         cfg.html = this.html || cfg.html;
940         
941         if (this.toolbar) {
942             cfg = {
943                 cls: 'btn-toolbar',
944                 html: null
945             };
946             
947             return cfg;
948         }
949         
950         if (['vertical','justified'].indexOf(this.align)!==-1) {
951             cfg.cls = 'btn-group-' + this.align;
952             
953             if (this.align == 'justified') {
954                 console.log(this.items);
955             }
956         }
957         
958         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
959             cfg.cls += ' btn-group-' + this.size;
960         }
961         
962         if (this.direction == 'up') {
963             cfg.cls += ' dropup' ;
964         }
965         
966         return cfg;
967     },
968     /**
969      * Add a button to the group (similar to NavItem API.)
970      */
971     addItem : function(cfg)
972     {
973         var cn = new Roo.bootstrap.Button(cfg);
974         //this.register(cn);
975         cn.parentId = this.id;
976         cn.onRender(this.el, null);
977         return cn;
978     }
979    
980 });
981
982  /*
983  * - LGPL
984  *
985  * button
986  * 
987  */
988
989 /**
990  * @class Roo.bootstrap.Button
991  * @extends Roo.bootstrap.Component
992  * Bootstrap Button class
993  * @cfg {String} html The button content
994  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
995  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
996  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
997  * @cfg {String} size (lg|sm|xs)
998  * @cfg {String} tag (a|input|submit)
999  * @cfg {String} href empty or href
1000  * @cfg {Boolean} disabled default false;
1001  * @cfg {Boolean} isClose default false;
1002  * @cfg {String} glyphicon depricated - use fa
1003  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1004  * @cfg {String} badge text for badge
1005  * @cfg {String} theme (default|glow)  
1006  * @cfg {Boolean} inverse dark themed version
1007  * @cfg {Boolean} toggle is it a slidy toggle button
1008  * @cfg {Boolean} pressed   default null - if the button ahs active state
1009  * @cfg {String} ontext text for on slidy toggle state
1010  * @cfg {String} offtext text for off slidy toggle state
1011  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1012  * @cfg {Boolean} removeClass remove the standard class..
1013  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1014  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1015  * 
1016  * @constructor
1017  * Create a new button
1018  * @param {Object} config The config object
1019  */
1020
1021
1022 Roo.bootstrap.Button = function(config){
1023     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1024     
1025     this.addEvents({
1026         // raw events
1027         /**
1028          * @event click
1029          * When a button is pressed
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "click" : true,
1034         /**
1035          * @event dblclick
1036          * When a button is double clicked
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          */
1040         "dblclick" : true,
1041          /**
1042          * @event toggle
1043          * After the button has been toggles
1044          * @param {Roo.bootstrap.Button} btn
1045          * @param {Roo.EventObject} e
1046          * @param {boolean} pressed (also available as button.pressed)
1047          */
1048         "toggle" : true
1049     });
1050 };
1051
1052 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1053     html: false,
1054     active: false,
1055     weight: '',
1056     badge_weight: '',
1057     outline : false,
1058     size: '',
1059     tag: 'button',
1060     href: '',
1061     disabled: false,
1062     isClose: false,
1063     glyphicon: '',
1064     fa: '',
1065     badge: '',
1066     theme: 'default',
1067     inverse: false,
1068     
1069     toggle: false,
1070     ontext: 'ON',
1071     offtext: 'OFF',
1072     defaulton: true,
1073     preventDefault: true,
1074     removeClass: false,
1075     name: false,
1076     target: false,
1077     group : false,
1078      
1079     pressed : null,
1080      
1081     
1082     getAutoCreate : function(){
1083         
1084         var cfg = {
1085             tag : 'button',
1086             cls : 'roo-button',
1087             html: ''
1088         };
1089         
1090         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1091             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1092             this.tag = 'button';
1093         } else {
1094             cfg.tag = this.tag;
1095         }
1096         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1097         
1098         if (this.toggle == true) {
1099             cfg={
1100                 tag: 'div',
1101                 cls: 'slider-frame roo-button',
1102                 cn: [
1103                     {
1104                         tag: 'span',
1105                         'data-on-text':'ON',
1106                         'data-off-text':'OFF',
1107                         cls: 'slider-button',
1108                         html: this.offtext
1109                     }
1110                 ]
1111             };
1112             // why are we validating the weights?
1113             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114                 cfg.cls +=  ' ' + this.weight;
1115             }
1116             
1117             return cfg;
1118         }
1119         
1120         if (this.isClose) {
1121             cfg.cls += ' close';
1122             
1123             cfg["aria-hidden"] = true;
1124             
1125             cfg.html = "&times;";
1126             
1127             return cfg;
1128         }
1129              
1130         
1131         if (this.theme==='default') {
1132             cfg.cls = 'btn roo-button';
1133             
1134             //if (this.parentType != 'Navbar') {
1135             this.weight = this.weight.length ?  this.weight : 'default';
1136             //}
1137             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1138                 
1139                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1140                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1141                 cfg.cls += ' btn-' + outline + weight;
1142                 if (this.weight == 'default') {
1143                     // BC
1144                     cfg.cls += ' btn-' + this.weight;
1145                 }
1146             }
1147         } else if (this.theme==='glow') {
1148             
1149             cfg.tag = 'a';
1150             cfg.cls = 'btn-glow roo-button';
1151             
1152             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1153                 
1154                 cfg.cls += ' ' + this.weight;
1155             }
1156         }
1157    
1158         
1159         if (this.inverse) {
1160             this.cls += ' inverse';
1161         }
1162         
1163         
1164         if (this.active || this.pressed === true) {
1165             cfg.cls += ' active';
1166         }
1167         
1168         if (this.disabled) {
1169             cfg.disabled = 'disabled';
1170         }
1171         
1172         if (this.items) {
1173             Roo.log('changing to ul' );
1174             cfg.tag = 'ul';
1175             this.glyphicon = 'caret';
1176             if (Roo.bootstrap.version == 4) {
1177                 this.fa = 'caret-down';
1178             }
1179             
1180         }
1181         
1182         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1183          
1184         //gsRoo.log(this.parentType);
1185         if (this.parentType === 'Navbar' && !this.parent().bar) {
1186             Roo.log('changing to li?');
1187             
1188             cfg.tag = 'li';
1189             
1190             cfg.cls = '';
1191             cfg.cn =  [{
1192                 tag : 'a',
1193                 cls : 'roo-button',
1194                 html : this.html,
1195                 href : this.href || '#'
1196             }];
1197             if (this.menu) {
1198                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1199                 cfg.cls += ' dropdown';
1200             }   
1201             
1202             delete cfg.html;
1203             
1204         }
1205         
1206        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1207         
1208         if (this.glyphicon) {
1209             cfg.html = ' ' + cfg.html;
1210             
1211             cfg.cn = [
1212                 {
1213                     tag: 'span',
1214                     cls: 'glyphicon glyphicon-' + this.glyphicon
1215                 }
1216             ];
1217         }
1218         if (this.fa) {
1219             cfg.html = ' ' + cfg.html;
1220             
1221             cfg.cn = [
1222                 {
1223                     tag: 'i',
1224                     cls: 'fa fas fa-' + this.fa
1225                 }
1226             ];
1227         }
1228         
1229         if (this.badge) {
1230             cfg.html += ' ';
1231             
1232             cfg.tag = 'a';
1233             
1234 //            cfg.cls='btn roo-button';
1235             
1236             cfg.href=this.href;
1237             
1238             var value = cfg.html;
1239             
1240             if(this.glyphicon){
1241                 value = {
1242                     tag: 'span',
1243                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1244                     html: this.html
1245                 };
1246             }
1247             if(this.fa){
1248                 value = {
1249                     tag: 'i',
1250                     cls: 'fa fas fa-' + this.fa,
1251                     html: this.html
1252                 };
1253             }
1254             
1255             var bw = this.badge_weight.length ? this.badge_weight :
1256                 (this.weight.length ? this.weight : 'secondary');
1257             bw = bw == 'default' ? 'secondary' : bw;
1258             
1259             cfg.cn = [
1260                 value,
1261                 {
1262                     tag: 'span',
1263                     cls: 'badge badge-' + bw,
1264                     html: this.badge
1265                 }
1266             ];
1267             
1268             cfg.html='';
1269         }
1270         
1271         if (this.menu) {
1272             cfg.cls += ' dropdown';
1273             cfg.html = typeof(cfg.html) != 'undefined' ?
1274                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1275         }
1276         
1277         if (cfg.tag !== 'a' && this.href !== '') {
1278             throw "Tag must be a to set href.";
1279         } else if (this.href.length > 0) {
1280             cfg.href = this.href;
1281         }
1282         
1283         if(this.removeClass){
1284             cfg.cls = '';
1285         }
1286         
1287         if(this.target){
1288             cfg.target = this.target;
1289         }
1290         
1291         return cfg;
1292     },
1293     initEvents: function() {
1294        // Roo.log('init events?');
1295 //        Roo.log(this.el.dom);
1296         // add the menu...
1297         
1298         if (typeof (this.menu) != 'undefined') {
1299             this.menu.parentType = this.xtype;
1300             this.menu.triggerEl = this.el;
1301             this.addxtype(Roo.apply({}, this.menu));
1302         }
1303
1304
1305         if (this.el.hasClass('roo-button')) {
1306              this.el.on('click', this.onClick, this);
1307              this.el.on('dblclick', this.onDblClick, this);
1308         } else {
1309              this.el.select('.roo-button').on('click', this.onClick, this);
1310              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1311              
1312         }
1313         // why?
1314         if(this.removeClass){
1315             this.el.on('click', this.onClick, this);
1316         }
1317         
1318         if (this.group === true) {
1319              if (this.pressed === false || this.pressed === true) {
1320                 // nothing
1321             } else {
1322                 this.pressed = false;
1323                 this.setActive(this.pressed);
1324             }
1325             
1326         }
1327         
1328         this.el.enableDisplayMode();
1329         
1330     },
1331     onClick : function(e)
1332     {
1333         if (this.disabled) {
1334             return;
1335         }
1336         
1337         Roo.log('button on click ');
1338         if(this.preventDefault){
1339             e.preventDefault();
1340         }
1341         
1342         if (this.group) {
1343             if (this.pressed) {
1344                 // do nothing -
1345                 return;
1346             }
1347             this.setActive(true);
1348             var pi = this.parent().items;
1349             for (var i = 0;i < pi.length;i++) {
1350                 if (this == pi[i]) {
1351                     continue;
1352                 }
1353                 if (pi[i].el.hasClass('roo-button')) {
1354                     pi[i].setActive(false);
1355                 }
1356             }
1357             this.fireEvent('click', this, e);            
1358             return;
1359         }
1360         
1361         if (this.pressed === true || this.pressed === false) {
1362             this.toggleActive(e);
1363         }
1364         
1365         
1366         this.fireEvent('click', this, e);
1367     },
1368     onDblClick: function(e)
1369     {
1370         if (this.disabled) {
1371             return;
1372         }
1373         if(this.preventDefault){
1374             e.preventDefault();
1375         }
1376         this.fireEvent('dblclick', this, e);
1377     },
1378     /**
1379      * Enables this button
1380      */
1381     enable : function()
1382     {
1383         this.disabled = false;
1384         this.el.removeClass('disabled');
1385         this.el.dom.removeAttribute("disabled");
1386     },
1387     
1388     /**
1389      * Disable this button
1390      */
1391     disable : function()
1392     {
1393         this.disabled = true;
1394         this.el.addClass('disabled');
1395         this.el.attr("disabled", "disabled")
1396     },
1397      /**
1398      * sets the active state on/off, 
1399      * @param {Boolean} state (optional) Force a particular state
1400      */
1401     setActive : function(v) {
1402         
1403         this.el[v ? 'addClass' : 'removeClass']('active');
1404         this.pressed = v;
1405     },
1406      /**
1407      * toggles the current active state 
1408      */
1409     toggleActive : function(e)
1410     {
1411         this.setActive(!this.pressed); // this modifies pressed...
1412         this.fireEvent('toggle', this, e, this.pressed);
1413     },
1414      /**
1415      * get the current active state
1416      * @return {boolean} true if it's active
1417      */
1418     isActive : function()
1419     {
1420         return this.el.hasClass('active');
1421     },
1422     /**
1423      * set the text of the first selected button
1424      */
1425     setText : function(str)
1426     {
1427         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1428     },
1429     /**
1430      * get the text of the first selected button
1431      */
1432     getText : function()
1433     {
1434         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1435     },
1436     
1437     setWeight : function(str)
1438     {
1439         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1440         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1441         this.weight = str;
1442         var outline = this.outline ? 'outline-' : '';
1443         if (str == 'default') {
1444             this.el.addClass('btn-default btn-outline-secondary');        
1445             return;
1446         }
1447         this.el.addClass('btn-' + outline + str);        
1448     }
1449     
1450     
1451 });
1452 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1453
1454 Roo.bootstrap.Button.weights = [
1455     'default',
1456     'secondary' ,
1457     'primary',
1458     'success',
1459     'info',
1460     'warning',
1461     'danger',
1462     'link',
1463     'light',
1464     'dark'              
1465    
1466 ];/*
1467  * - LGPL
1468  *
1469  * column
1470  * 
1471  */
1472
1473 /**
1474  * @class Roo.bootstrap.Column
1475  * @extends Roo.bootstrap.Component
1476  * @children Roo.bootstrap.Component
1477  * Bootstrap Column class
1478  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1479  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1480  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1481  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1482  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1483  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1484  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1485  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1486  *
1487  * 
1488  * @cfg {Boolean} hidden (true|false) hide the element
1489  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1490  * @cfg {String} fa (ban|check|...) font awesome icon
1491  * @cfg {Number} fasize (1|2|....) font awsome size
1492
1493  * @cfg {String} icon (info-sign|check|...) glyphicon name
1494
1495  * @cfg {String} html content of column.
1496  * 
1497  * @constructor
1498  * Create a new Column
1499  * @param {Object} config The config object
1500  */
1501
1502 Roo.bootstrap.Column = function(config){
1503     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1504 };
1505
1506 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1507     
1508     xs: false,
1509     sm: false,
1510     md: false,
1511     lg: false,
1512     xsoff: false,
1513     smoff: false,
1514     mdoff: false,
1515     lgoff: false,
1516     html: '',
1517     offset: 0,
1518     alert: false,
1519     fa: false,
1520     icon : false,
1521     hidden : false,
1522     fasize : 1,
1523     
1524     getAutoCreate : function(){
1525         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1526         
1527         cfg = {
1528             tag: 'div',
1529             cls: 'column'
1530         };
1531         
1532         var settings=this;
1533         var sizes =   ['xs','sm','md','lg'];
1534         sizes.map(function(size ,ix){
1535             //Roo.log( size + ':' + settings[size]);
1536             
1537             if (settings[size+'off'] !== false) {
1538                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1539             }
1540             
1541             if (settings[size] === false) {
1542                 return;
1543             }
1544             
1545             if (!settings[size]) { // 0 = hidden
1546                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1547                 // bootsrap4
1548                 for (var i = ix; i > -1; i--) {
1549                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1550                 }
1551                 
1552                 
1553                 return;
1554             }
1555             cfg.cls += ' col-' + size + '-' + settings[size] + (
1556                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1557             );
1558             
1559         });
1560         
1561         if (this.hidden) {
1562             cfg.cls += ' hidden';
1563         }
1564         
1565         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1566             cfg.cls +=' alert alert-' + this.alert;
1567         }
1568         
1569         
1570         if (this.html.length) {
1571             cfg.html = this.html;
1572         }
1573         if (this.fa) {
1574             var fasize = '';
1575             if (this.fasize > 1) {
1576                 fasize = ' fa-' + this.fasize + 'x';
1577             }
1578             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1579             
1580             
1581         }
1582         if (this.icon) {
1583             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1584         }
1585         
1586         return cfg;
1587     }
1588    
1589 });
1590
1591  
1592
1593  /*
1594  * - LGPL
1595  *
1596  * page container.
1597  * 
1598  */
1599
1600
1601 /**
1602  * @class Roo.bootstrap.Container
1603  * @extends Roo.bootstrap.Component
1604  * @builder-top
1605  * @children Roo.bootstrap.Component
1606  * Bootstrap Container class
1607  * @cfg {Boolean} jumbotron is it a jumbotron element
1608  * @cfg {String} html content of element
1609  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1610  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1611  * @cfg {String} header content of header (for panel)
1612  * @cfg {String} footer content of footer (for panel)
1613  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1614  * @cfg {String} tag (header|aside|section) type of HTML tag.
1615  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1616  * @cfg {String} fa font awesome icon
1617  * @cfg {String} icon (info-sign|check|...) glyphicon name
1618  * @cfg {Boolean} hidden (true|false) hide the element
1619  * @cfg {Boolean} expandable (true|false) default false
1620  * @cfg {Boolean} expanded (true|false) default true
1621  * @cfg {String} rheader contet on the right of header
1622  * @cfg {Boolean} clickable (true|false) default false
1623
1624  *     
1625  * @constructor
1626  * Create a new Container
1627  * @param {Object} config The config object
1628  */
1629
1630 Roo.bootstrap.Container = function(config){
1631     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1632     
1633     this.addEvents({
1634         // raw events
1635          /**
1636          * @event expand
1637          * After the panel has been expand
1638          * 
1639          * @param {Roo.bootstrap.Container} this
1640          */
1641         "expand" : true,
1642         /**
1643          * @event collapse
1644          * After the panel has been collapsed
1645          * 
1646          * @param {Roo.bootstrap.Container} this
1647          */
1648         "collapse" : true,
1649         /**
1650          * @event click
1651          * When a element is chick
1652          * @param {Roo.bootstrap.Container} this
1653          * @param {Roo.EventObject} e
1654          */
1655         "click" : true
1656     });
1657 };
1658
1659 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1660     
1661     jumbotron : false,
1662     well: '',
1663     panel : '',
1664     header: '',
1665     footer : '',
1666     sticky: '',
1667     tag : false,
1668     alert : false,
1669     fa: false,
1670     icon : false,
1671     expandable : false,
1672     rheader : '',
1673     expanded : true,
1674     clickable: false,
1675   
1676      
1677     getChildContainer : function() {
1678         
1679         if(!this.el){
1680             return false;
1681         }
1682         
1683         if (this.panel.length) {
1684             return this.el.select('.panel-body',true).first();
1685         }
1686         
1687         return this.el;
1688     },
1689     
1690     
1691     getAutoCreate : function(){
1692         
1693         var cfg = {
1694             tag : this.tag || 'div',
1695             html : '',
1696             cls : ''
1697         };
1698         if (this.jumbotron) {
1699             cfg.cls = 'jumbotron';
1700         }
1701         
1702         
1703         
1704         // - this is applied by the parent..
1705         //if (this.cls) {
1706         //    cfg.cls = this.cls + '';
1707         //}
1708         
1709         if (this.sticky.length) {
1710             
1711             var bd = Roo.get(document.body);
1712             if (!bd.hasClass('bootstrap-sticky')) {
1713                 bd.addClass('bootstrap-sticky');
1714                 Roo.select('html',true).setStyle('height', '100%');
1715             }
1716              
1717             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1718         }
1719         
1720         
1721         if (this.well.length) {
1722             switch (this.well) {
1723                 case 'lg':
1724                 case 'sm':
1725                     cfg.cls +=' well well-' +this.well;
1726                     break;
1727                 default:
1728                     cfg.cls +=' well';
1729                     break;
1730             }
1731         }
1732         
1733         if (this.hidden) {
1734             cfg.cls += ' hidden';
1735         }
1736         
1737         
1738         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1739             cfg.cls +=' alert alert-' + this.alert;
1740         }
1741         
1742         var body = cfg;
1743         
1744         if (this.panel.length) {
1745             cfg.cls += ' panel panel-' + this.panel;
1746             cfg.cn = [];
1747             if (this.header.length) {
1748                 
1749                 var h = [];
1750                 
1751                 if(this.expandable){
1752                     
1753                     cfg.cls = cfg.cls + ' expandable';
1754                     
1755                     h.push({
1756                         tag: 'i',
1757                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1758                     });
1759                     
1760                 }
1761                 
1762                 h.push(
1763                     {
1764                         tag: 'span',
1765                         cls : 'panel-title',
1766                         html : (this.expandable ? '&nbsp;' : '') + this.header
1767                     },
1768                     {
1769                         tag: 'span',
1770                         cls: 'panel-header-right',
1771                         html: this.rheader
1772                     }
1773                 );
1774                 
1775                 cfg.cn.push({
1776                     cls : 'panel-heading',
1777                     style : this.expandable ? 'cursor: pointer' : '',
1778                     cn : h
1779                 });
1780                 
1781             }
1782             
1783             body = false;
1784             cfg.cn.push({
1785                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1786                 html : this.html
1787             });
1788             
1789             
1790             if (this.footer.length) {
1791                 cfg.cn.push({
1792                     cls : 'panel-footer',
1793                     html : this.footer
1794                     
1795                 });
1796             }
1797             
1798         }
1799         
1800         if (body) {
1801             body.html = this.html || cfg.html;
1802             // prefix with the icons..
1803             if (this.fa) {
1804                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1805             }
1806             if (this.icon) {
1807                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1808             }
1809             
1810             
1811         }
1812         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1813             cfg.cls =  'container';
1814         }
1815         
1816         return cfg;
1817     },
1818     
1819     initEvents: function() 
1820     {
1821         if(this.expandable){
1822             var headerEl = this.headerEl();
1823         
1824             if(headerEl){
1825                 headerEl.on('click', this.onToggleClick, this);
1826             }
1827         }
1828         
1829         if(this.clickable){
1830             this.el.on('click', this.onClick, this);
1831         }
1832         
1833     },
1834     
1835     onToggleClick : function()
1836     {
1837         var headerEl = this.headerEl();
1838         
1839         if(!headerEl){
1840             return;
1841         }
1842         
1843         if(this.expanded){
1844             this.collapse();
1845             return;
1846         }
1847         
1848         this.expand();
1849     },
1850     
1851     expand : function()
1852     {
1853         if(this.fireEvent('expand', this)) {
1854             
1855             this.expanded = true;
1856             
1857             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1858             
1859             this.el.select('.panel-body',true).first().removeClass('hide');
1860             
1861             var toggleEl = this.toggleEl();
1862
1863             if(!toggleEl){
1864                 return;
1865             }
1866
1867             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1868         }
1869         
1870     },
1871     
1872     collapse : function()
1873     {
1874         if(this.fireEvent('collapse', this)) {
1875             
1876             this.expanded = false;
1877             
1878             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1879             this.el.select('.panel-body',true).first().addClass('hide');
1880         
1881             var toggleEl = this.toggleEl();
1882
1883             if(!toggleEl){
1884                 return;
1885             }
1886
1887             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1888         }
1889     },
1890     
1891     toggleEl : function()
1892     {
1893         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1894             return;
1895         }
1896         
1897         return this.el.select('.panel-heading .fa',true).first();
1898     },
1899     
1900     headerEl : function()
1901     {
1902         if(!this.el || !this.panel.length || !this.header.length){
1903             return;
1904         }
1905         
1906         return this.el.select('.panel-heading',true).first()
1907     },
1908     
1909     bodyEl : function()
1910     {
1911         if(!this.el || !this.panel.length){
1912             return;
1913         }
1914         
1915         return this.el.select('.panel-body',true).first()
1916     },
1917     
1918     titleEl : function()
1919     {
1920         if(!this.el || !this.panel.length || !this.header.length){
1921             return;
1922         }
1923         
1924         return this.el.select('.panel-title',true).first();
1925     },
1926     
1927     setTitle : function(v)
1928     {
1929         var titleEl = this.titleEl();
1930         
1931         if(!titleEl){
1932             return;
1933         }
1934         
1935         titleEl.dom.innerHTML = v;
1936     },
1937     
1938     getTitle : function()
1939     {
1940         
1941         var titleEl = this.titleEl();
1942         
1943         if(!titleEl){
1944             return '';
1945         }
1946         
1947         return titleEl.dom.innerHTML;
1948     },
1949     
1950     setRightTitle : function(v)
1951     {
1952         var t = this.el.select('.panel-header-right',true).first();
1953         
1954         if(!t){
1955             return;
1956         }
1957         
1958         t.dom.innerHTML = v;
1959     },
1960     
1961     onClick : function(e)
1962     {
1963         e.preventDefault();
1964         
1965         this.fireEvent('click', this, e);
1966     }
1967 });
1968
1969  /*
1970  *  - LGPL
1971  *
1972  *  This is BS4's Card element.. - similar to our containers probably..
1973  * 
1974  */
1975 /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * Bootstrap Card class
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <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>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * Bootstrap CardHeader class
2781  * @constructor
2782  * Create a new Card Header - that you can embed children into
2783  * @param {Object} config The config object
2784  */
2785
2786 Roo.bootstrap.CardHeader = function(config){
2787     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2791     
2792     
2793     container_method : 'getCardHeader' 
2794     
2795      
2796     
2797     
2798    
2799 });
2800
2801  
2802
2803  /*
2804  * - LGPL
2805  *
2806  * Card footer - holder for the card footer elements.
2807  * 
2808  */
2809
2810 /**
2811  * @class Roo.bootstrap.CardFooter
2812  * @extends Roo.bootstrap.Element
2813  * Bootstrap CardFooter class
2814  * @constructor
2815  * Create a new Card Footer - that you can embed children into
2816  * @param {Object} config The config object
2817  */
2818
2819 Roo.bootstrap.CardFooter = function(config){
2820     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2821 };
2822
2823 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2824     
2825     
2826     container_method : 'getCardFooter' 
2827     
2828      
2829     
2830     
2831    
2832 });
2833
2834  
2835
2836  /*
2837  * - LGPL
2838  *
2839  * Card header - holder for the card header elements.
2840  * 
2841  */
2842
2843 /**
2844  * @class Roo.bootstrap.CardImageTop
2845  * @extends Roo.bootstrap.Element
2846  * Bootstrap CardImageTop class
2847  * @constructor
2848  * Create a new Card Image Top container
2849  * @param {Object} config The config object
2850  */
2851
2852 Roo.bootstrap.CardImageTop = function(config){
2853     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2854 };
2855
2856 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2857     
2858    
2859     container_method : 'getCardImageTop' 
2860     
2861      
2862     
2863    
2864 });
2865
2866  
2867
2868  
2869 /*
2870 * Licence: LGPL
2871 */
2872
2873 /**
2874  * @class Roo.bootstrap.ButtonUploader
2875  * @extends Roo.bootstrap.Button
2876  * Bootstrap Button Uploader class - it's a button which when you add files to it
2877  *
2878  * 
2879  * @cfg {Number} errorTimeout default 3000
2880  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2881  * @cfg {Array}  html The button text.
2882  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2883  *
2884  * @constructor
2885  * Create a new CardUploader
2886  * @param {Object} config The config object
2887  */
2888
2889 Roo.bootstrap.ButtonUploader = function(config){
2890     
2891  
2892     
2893     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2894     
2895      
2896      this.addEvents({
2897          // raw events
2898         /**
2899          * @event beforeselect
2900          * When button is pressed, before show upload files dialog is shown
2901          * @param {Roo.bootstrap.UploaderButton} this
2902          *
2903          */
2904         'beforeselect' : true,
2905          /**
2906          * @event fired when files have been selected, 
2907          * When a the download link is clicked
2908          * @param {Roo.bootstrap.UploaderButton} this
2909          * @param {Array} Array of files that have been uploaded
2910          */
2911         'uploaded' : true
2912         
2913     });
2914 };
2915  
2916 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2917     
2918      
2919     errorTimeout : 3000,
2920      
2921     images : false,
2922    
2923     fileCollection : false,
2924     allowBlank : true,
2925     
2926     multiple : true,
2927     
2928     getAutoCreate : function()
2929     {
2930         var im = {
2931             tag: 'input',
2932             type : 'file',
2933             cls : 'd-none  roo-card-upload-selector' 
2934           
2935         };
2936         if (this.multiple) {
2937             im.multiple = 'multiple';
2938         }
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2944                 im
2945
2946             ]
2947         };
2948            
2949          
2950     },
2951      
2952    
2953     initEvents : function()
2954     {
2955         
2956         Roo.bootstrap.Button.prototype.initEvents.call(this);
2957         
2958         
2959         
2960         
2961         
2962         this.urlAPI = (window.createObjectURL && window) || 
2963                                 (window.URL && URL.revokeObjectURL && URL) || 
2964                                 (window.webkitURL && webkitURL);
2965                         
2966          
2967          
2968          
2969         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2970         
2971         this.selectorEl.on('change', this.onFileSelected, this);
2972          
2973          
2974        
2975     },
2976     
2977    
2978     onClick : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if ( this.fireEvent('beforeselect', this) === false) {
2983             return;
2984         }
2985          
2986         this.selectorEl.dom.click();
2987          
2988     },
2989     
2990     onFileSelected : function(e)
2991     {
2992         e.preventDefault();
2993         
2994         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2995             return;
2996         }
2997         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2998         this.selectorEl.dom.value  = '';// hopefully reset..
2999         
3000         this.fireEvent('uploaded', this,  files );
3001         
3002     },
3003     
3004        
3005    
3006     
3007     /**
3008      * addCard - add an Attachment to the uploader
3009      * @param data - the data about the image to upload
3010      *
3011      * {
3012           id : 123
3013           title : "Title of file",
3014           is_uploaded : false,
3015           src : "http://.....",
3016           srcfile : { the File upload object },
3017           mimetype : file.type,
3018           preview : false,
3019           is_deleted : 0
3020           .. any other data...
3021         }
3022      *
3023      * 
3024     */
3025      
3026     reset: function()
3027     {
3028          
3029          this.selectorEl
3030     } 
3031     
3032     
3033     
3034     
3035 });
3036  /*
3037  * - LGPL
3038  *
3039  * image
3040  * 
3041  */
3042
3043
3044 /**
3045  * @class Roo.bootstrap.Img
3046  * @extends Roo.bootstrap.Component
3047  * Bootstrap Img class
3048  * @cfg {Boolean} imgResponsive false | true
3049  * @cfg {String} border rounded | circle | thumbnail
3050  * @cfg {String} src image source
3051  * @cfg {String} alt image alternative text
3052  * @cfg {String} href a tag href
3053  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3054  * @cfg {String} xsUrl xs image source
3055  * @cfg {String} smUrl sm image source
3056  * @cfg {String} mdUrl md image source
3057  * @cfg {String} lgUrl lg image source
3058  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3059  * 
3060  * @constructor
3061  * Create a new Input
3062  * @param {Object} config The config object
3063  */
3064
3065 Roo.bootstrap.Img = function(config){
3066     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3067     
3068     this.addEvents({
3069         // img events
3070         /**
3071          * @event click
3072          * The img click event for the img.
3073          * @param {Roo.EventObject} e
3074          */
3075         "click" : true,
3076         /**
3077          * @event load
3078          * The when any image loads
3079          * @param {Roo.EventObject} e
3080          */
3081         "load" : true
3082     });
3083 };
3084
3085 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3086     
3087     imgResponsive: true,
3088     border: '',
3089     src: 'about:blank',
3090     href: false,
3091     target: false,
3092     xsUrl: '',
3093     smUrl: '',
3094     mdUrl: '',
3095     lgUrl: '',
3096     backgroundContain : false,
3097
3098     getAutoCreate : function()
3099     {   
3100         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3101             return this.createSingleImg();
3102         }
3103         
3104         var cfg = {
3105             tag: 'div',
3106             cls: 'roo-image-responsive-group',
3107             cn: []
3108         };
3109         var _this = this;
3110         
3111         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3112             
3113             if(!_this[size + 'Url']){
3114                 return;
3115             }
3116             
3117             var img = {
3118                 tag: 'img',
3119                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3120                 html: _this.html || cfg.html,
3121                 src: _this[size + 'Url']
3122             };
3123             
3124             img.cls += ' roo-image-responsive-' + size;
3125             
3126             var s = ['xs', 'sm', 'md', 'lg'];
3127             
3128             s.splice(s.indexOf(size), 1);
3129             
3130             Roo.each(s, function(ss){
3131                 img.cls += ' hidden-' + ss;
3132             });
3133             
3134             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3135                 cfg.cls += ' img-' + _this.border;
3136             }
3137             
3138             if(_this.alt){
3139                 cfg.alt = _this.alt;
3140             }
3141             
3142             if(_this.href){
3143                 var a = {
3144                     tag: 'a',
3145                     href: _this.href,
3146                     cn: [
3147                         img
3148                     ]
3149                 };
3150
3151                 if(this.target){
3152                     a.target = _this.target;
3153                 }
3154             }
3155             
3156             cfg.cn.push((_this.href) ? a : img);
3157             
3158         });
3159         
3160         return cfg;
3161     },
3162     
3163     createSingleImg : function()
3164     {
3165         var cfg = {
3166             tag: 'img',
3167             cls: (this.imgResponsive) ? 'img-responsive' : '',
3168             html : null,
3169             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3170         };
3171         
3172         if (this.backgroundContain) {
3173             cfg.cls += ' background-contain';
3174         }
3175         
3176         cfg.html = this.html || cfg.html;
3177         
3178         if (this.backgroundContain) {
3179             cfg.style="background-image: url(" + this.src + ')';
3180         } else {
3181             cfg.src = this.src || cfg.src;
3182         }
3183         
3184         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3185             cfg.cls += ' img-' + this.border;
3186         }
3187         
3188         if(this.alt){
3189             cfg.alt = this.alt;
3190         }
3191         
3192         if(this.href){
3193             var a = {
3194                 tag: 'a',
3195                 href: this.href,
3196                 cn: [
3197                     cfg
3198                 ]
3199             };
3200             
3201             if(this.target){
3202                 a.target = this.target;
3203             }
3204             
3205         }
3206         
3207         return (this.href) ? a : cfg;
3208     },
3209     
3210     initEvents: function() 
3211     {
3212         if(!this.href){
3213             this.el.on('click', this.onClick, this);
3214         }
3215         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3216             this.el.on('load', this.onImageLoad, this);
3217         } else {
3218             // not sure if this works.. not tested
3219             this.el.select('img', true).on('load', this.onImageLoad, this);
3220         }
3221         
3222     },
3223     
3224     onClick : function(e)
3225     {
3226         Roo.log('img onclick');
3227         this.fireEvent('click', this, e);
3228     },
3229     onImageLoad: function(e)
3230     {
3231         Roo.log('img load');
3232         this.fireEvent('load', this, e);
3233     },
3234     
3235     /**
3236      * Sets the url of the image - used to update it
3237      * @param {String} url the url of the image
3238      */
3239     
3240     setSrc : function(url)
3241     {
3242         this.src =  url;
3243         
3244         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3245             if (this.backgroundContain) {
3246                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3247             } else {
3248                 this.el.dom.src =  url;
3249             }
3250             return;
3251         }
3252         
3253         this.el.select('img', true).first().dom.src =  url;
3254     }
3255     
3256     
3257    
3258 });
3259
3260  /*
3261  * - LGPL
3262  *
3263  * image
3264  * 
3265  */
3266
3267
3268 /**
3269  * @class Roo.bootstrap.Link
3270  * @extends Roo.bootstrap.Component
3271  * @children Roo.bootstrap.Component
3272  * Bootstrap Link Class (eg. '<a href>')
3273  
3274  * @cfg {String} alt image alternative text
3275  * @cfg {String} href a tag href
3276  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3277  * @cfg {String} html the content of the link.
3278  * @cfg {String} anchor name for the anchor link
3279  * @cfg {String} fa - favicon
3280
3281  * @cfg {Boolean} preventDefault (true | false) default false
3282
3283  * 
3284  * @constructor
3285  * Create a new Input
3286  * @param {Object} config The config object
3287  */
3288
3289 Roo.bootstrap.Link = function(config){
3290     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3291     
3292     this.addEvents({
3293         // img events
3294         /**
3295          * @event click
3296          * The img click event for the img.
3297          * @param {Roo.EventObject} e
3298          */
3299         "click" : true
3300     });
3301 };
3302
3303 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3304     
3305     href: false,
3306     target: false,
3307     preventDefault: false,
3308     anchor : false,
3309     alt : false,
3310     fa: false,
3311
3312
3313     getAutoCreate : function()
3314     {
3315         var html = this.html || '';
3316         
3317         if (this.fa !== false) {
3318             html = '<i class="fa fa-' + this.fa + '"></i>';
3319         }
3320         var cfg = {
3321             tag: 'a'
3322         };
3323         // anchor's do not require html/href...
3324         if (this.anchor === false) {
3325             cfg.html = html;
3326             cfg.href = this.href || '#';
3327         } else {
3328             cfg.name = this.anchor;
3329             if (this.html !== false || this.fa !== false) {
3330                 cfg.html = html;
3331             }
3332             if (this.href !== false) {
3333                 cfg.href = this.href;
3334             }
3335         }
3336         
3337         if(this.alt !== false){
3338             cfg.alt = this.alt;
3339         }
3340         
3341         
3342         if(this.target !== false) {
3343             cfg.target = this.target;
3344         }
3345         
3346         return cfg;
3347     },
3348     
3349     initEvents: function() {
3350         
3351         if(!this.href || this.preventDefault){
3352             this.el.on('click', this.onClick, this);
3353         }
3354     },
3355     
3356     onClick : function(e)
3357     {
3358         if(this.preventDefault){
3359             e.preventDefault();
3360         }
3361         //Roo.log('img onclick');
3362         this.fireEvent('click', this, e);
3363     }
3364    
3365 });
3366
3367  /*
3368  * - LGPL
3369  *
3370  * header
3371  * 
3372  */
3373
3374 /**
3375  * @class Roo.bootstrap.Header
3376  * @extends Roo.bootstrap.Component
3377  * Bootstrap Header class
3378  * @cfg {String} html content of header
3379  * @cfg {Number} level (1|2|3|4|5|6) default 1
3380  * 
3381  * @constructor
3382  * Create a new Header
3383  * @param {Object} config The config object
3384  */
3385
3386
3387 Roo.bootstrap.Header  = function(config){
3388     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3389 };
3390
3391 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3392     
3393     //href : false,
3394     html : false,
3395     level : 1,
3396     
3397     
3398     
3399     getAutoCreate : function(){
3400         
3401         
3402         
3403         var cfg = {
3404             tag: 'h' + (1 *this.level),
3405             html: this.html || ''
3406         } ;
3407         
3408         return cfg;
3409     }
3410    
3411 });
3412
3413  
3414
3415  /*
3416  * Based on:
3417  * Ext JS Library 1.1.1
3418  * Copyright(c) 2006-2007, Ext JS, LLC.
3419  *
3420  * Originally Released Under LGPL - original licence link has changed is not relivant.
3421  *
3422  * Fork - LGPL
3423  * <script type="text/javascript">
3424  */
3425  
3426 /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3429  * @singleton
3430  */
3431 Roo.bootstrap.MenuMgr = function(){
3432    var menus, active, groups = {}, attached = false, lastShow = new Date();
3433
3434    // private - called when first menu is created
3435    function init(){
3436        menus = {};
3437        active = new Roo.util.MixedCollection();
3438        Roo.get(document).addKeyListener(27, function(){
3439            if(active.length > 0){
3440                hideAll();
3441            }
3442        });
3443    }
3444
3445    // private
3446    function hideAll(){
3447        if(active && active.length > 0){
3448            var c = active.clone();
3449            c.each(function(m){
3450                m.hide();
3451            });
3452        }
3453    }
3454
3455    // private
3456    function onHide(m){
3457        active.remove(m);
3458        if(active.length < 1){
3459            Roo.get(document).un("mouseup", onMouseDown);
3460             
3461            attached = false;
3462        }
3463    }
3464
3465    // private
3466    function onShow(m){
3467        var last = active.last();
3468        lastShow = new Date();
3469        active.add(m);
3470        if(!attached){
3471           Roo.get(document).on("mouseup", onMouseDown);
3472            
3473            attached = true;
3474        }
3475        if(m.parentMenu){
3476           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477           m.parentMenu.activeChild = m;
3478        }else if(last && last.isVisible()){
3479           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3480        }
3481    }
3482
3483    // private
3484    function onBeforeHide(m){
3485        if(m.activeChild){
3486            m.activeChild.hide();
3487        }
3488        if(m.autoHideTimer){
3489            clearTimeout(m.autoHideTimer);
3490            delete m.autoHideTimer;
3491        }
3492    }
3493
3494    // private
3495    function onBeforeShow(m){
3496        var pm = m.parentMenu;
3497        if(!pm && !m.allowOtherMenus){
3498            hideAll();
3499        }else if(pm && pm.activeChild && active != m){
3500            pm.activeChild.hide();
3501        }
3502    }
3503
3504    // private this should really trigger on mouseup..
3505    function onMouseDown(e){
3506         Roo.log("on Mouse Up");
3507         
3508         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509             Roo.log("MenuManager hideAll");
3510             hideAll();
3511             e.stopEvent();
3512         }
3513         
3514         
3515    }
3516
3517    // private
3518    function onBeforeCheck(mi, state){
3519        if(state){
3520            var g = groups[mi.group];
3521            for(var i = 0, l = g.length; i < l; i++){
3522                if(g[i] != mi){
3523                    g[i].setChecked(false);
3524                }
3525            }
3526        }
3527    }
3528
3529    return {
3530
3531        /**
3532         * Hides all menus that are currently visible
3533         */
3534        hideAll : function(){
3535             hideAll();  
3536        },
3537
3538        // private
3539        register : function(menu){
3540            if(!menus){
3541                init();
3542            }
3543            menus[menu.id] = menu;
3544            menu.on("beforehide", onBeforeHide);
3545            menu.on("hide", onHide);
3546            menu.on("beforeshow", onBeforeShow);
3547            menu.on("show", onShow);
3548            var g = menu.group;
3549            if(g && menu.events["checkchange"]){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menu);
3554                menu.on("checkchange", onCheck);
3555            }
3556        },
3557
3558         /**
3559          * Returns a {@link Roo.menu.Menu} object
3560          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561          * be used to generate and return a new Menu instance.
3562          */
3563        get : function(menu){
3564            if(typeof menu == "string"){ // menu id
3565                return menus[menu];
3566            }else if(menu.events){  // menu instance
3567                return menu;
3568            }
3569            /*else if(typeof menu.length == 'number'){ // array of menu items?
3570                return new Roo.bootstrap.Menu({items:menu});
3571            }else{ // otherwise, must be a config
3572                return new Roo.bootstrap.Menu(menu);
3573            }
3574            */
3575            return false;
3576        },
3577
3578        // private
3579        unregister : function(menu){
3580            delete menus[menu.id];
3581            menu.un("beforehide", onBeforeHide);
3582            menu.un("hide", onHide);
3583            menu.un("beforeshow", onBeforeShow);
3584            menu.un("show", onShow);
3585            var g = menu.group;
3586            if(g && menu.events["checkchange"]){
3587                groups[g].remove(menu);
3588                menu.un("checkchange", onCheck);
3589            }
3590        },
3591
3592        // private
3593        registerCheckable : function(menuItem){
3594            var g = menuItem.group;
3595            if(g){
3596                if(!groups[g]){
3597                    groups[g] = [];
3598                }
3599                groups[g].push(menuItem);
3600                menuItem.on("beforecheckchange", onBeforeCheck);
3601            }
3602        },
3603
3604        // private
3605        unregisterCheckable : function(menuItem){
3606            var g = menuItem.group;
3607            if(g){
3608                groups[g].remove(menuItem);
3609                menuItem.un("beforecheckchange", onBeforeCheck);
3610            }
3611        }
3612    };
3613 }();/*
3614  * - LGPL
3615  *
3616  * menu
3617  * 
3618  */
3619
3620 /**
3621  * @class Roo.bootstrap.Menu
3622  * @extends Roo.bootstrap.Component
3623  * Bootstrap Menu class - container for MenuItems
3624  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3626  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3627  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3628   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3629   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3630  
3631  * @constructor
3632  * Create a new Menu
3633  * @param {Object} config The config object
3634  */
3635
3636
3637 Roo.bootstrap.Menu = function(config){
3638     
3639     if (config.type == 'treeview') {
3640         // normally menu's are drawn attached to the document to handle layering etc..
3641         // however treeview (used by the docs menu is drawn into the parent element)
3642         this.container_method = 'getChildContainer'; 
3643     }
3644     
3645     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3646     if (this.registerMenu && this.type != 'treeview')  {
3647         Roo.bootstrap.MenuMgr.register(this);
3648     }
3649     
3650     
3651     this.addEvents({
3652         /**
3653          * @event beforeshow
3654          * Fires before this menu is displayed (return false to block)
3655          * @param {Roo.menu.Menu} this
3656          */
3657         beforeshow : true,
3658         /**
3659          * @event beforehide
3660          * Fires before this menu is hidden (return false to block)
3661          * @param {Roo.menu.Menu} this
3662          */
3663         beforehide : true,
3664         /**
3665          * @event show
3666          * Fires after this menu is displayed
3667          * @param {Roo.menu.Menu} this
3668          */
3669         show : true,
3670         /**
3671          * @event hide
3672          * Fires after this menu is hidden
3673          * @param {Roo.menu.Menu} this
3674          */
3675         hide : true,
3676         /**
3677          * @event click
3678          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679          * @param {Roo.menu.Menu} this
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          * @param {Roo.EventObject} e
3682          */
3683         click : true,
3684         /**
3685          * @event mouseover
3686          * Fires when the mouse is hovering over this menu
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.EventObject} e
3689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690          */
3691         mouseover : true,
3692         /**
3693          * @event mouseout
3694          * Fires when the mouse exits this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseout : true,
3700         /**
3701          * @event itemclick
3702          * Fires when a menu item contained in this menu is clicked
3703          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704          * @param {Roo.EventObject} e
3705          */
3706         itemclick: true
3707     });
3708     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3709 };
3710
3711 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3712     
3713    /// html : false,
3714    
3715     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3716     type: false,
3717     /**
3718      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719      */
3720     registerMenu : true,
3721     
3722     menuItems :false, // stores the menu items..
3723     
3724     hidden:true,
3725         
3726     parentMenu : false,
3727     
3728     stopEvent : true,
3729     
3730     isLink : false,
3731     
3732     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733     
3734     hideTrigger : false,
3735     
3736     align : 'tl-bl?',
3737     
3738     
3739     getChildContainer : function() {
3740         return this.el;  
3741     },
3742     
3743     getAutoCreate : function(){
3744          
3745         //if (['right'].indexOf(this.align)!==-1) {
3746         //    cfg.cn[1].cls += ' pull-right'
3747         //}
3748          
3749         var cfg = {
3750             tag : 'ul',
3751             cls : 'dropdown-menu shadow' ,
3752             style : 'z-index:1000'
3753             
3754         };
3755         
3756         if (this.type === 'submenu') {
3757             cfg.cls = 'submenu active';
3758         }
3759         if (this.type === 'treeview') {
3760             cfg.cls = 'treeview-menu';
3761         }
3762         
3763         return cfg;
3764     },
3765     initEvents : function() {
3766         
3767        // Roo.log("ADD event");
3768        // Roo.log(this.triggerEl.dom);
3769         if (this.triggerEl) {
3770             
3771             this.triggerEl.on('click', this.onTriggerClick, this);
3772             
3773             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774             
3775             if (!this.hideTrigger) {
3776                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777                     // dropdown toggle on the 'a' in BS4?
3778                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779                 } else {
3780                     this.triggerEl.addClass('dropdown-toggle');
3781                 }
3782             }
3783         }
3784         
3785         if (Roo.isTouch) {
3786             this.el.on('touchstart'  , this.onTouch, this);
3787         }
3788         this.el.on('click' , this.onClick, this);
3789
3790         this.el.on("mouseover", this.onMouseOver, this);
3791         this.el.on("mouseout", this.onMouseOut, this);
3792         
3793     },
3794     
3795     findTargetItem : function(e)
3796     {
3797         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3798         if(!t){
3799             return false;
3800         }
3801         //Roo.log(t);         Roo.log(t.id);
3802         if(t && t.id){
3803             //Roo.log(this.menuitems);
3804             return this.menuitems.get(t.id);
3805             
3806             //return this.items.get(t.menuItemId);
3807         }
3808         
3809         return false;
3810     },
3811     
3812     onTouch : function(e) 
3813     {
3814         Roo.log("menu.onTouch");
3815         //e.stopEvent(); this make the user popdown broken
3816         this.onClick(e);
3817     },
3818     
3819     onClick : function(e)
3820     {
3821         Roo.log("menu.onClick");
3822         
3823         var t = this.findTargetItem(e);
3824         if(!t || t.isContainer){
3825             return;
3826         }
3827         Roo.log(e);
3828         /*
3829         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3830             if(t == this.activeItem && t.shouldDeactivate(e)){
3831                 this.activeItem.deactivate();
3832                 delete this.activeItem;
3833                 return;
3834             }
3835             if(t.canActivate){
3836                 this.setActiveItem(t, true);
3837             }
3838             return;
3839             
3840             
3841         }
3842         */
3843        
3844         Roo.log('pass click event');
3845         
3846         t.onClick(e);
3847         
3848         this.fireEvent("click", this, t, e);
3849         
3850         var _this = this;
3851         
3852         if(!t.href.length || t.href == '#'){
3853             (function() { _this.hide(); }).defer(100);
3854         }
3855         
3856     },
3857     
3858     onMouseOver : function(e){
3859         var t  = this.findTargetItem(e);
3860         //Roo.log(t);
3861         //if(t){
3862         //    if(t.canActivate && !t.disabled){
3863         //        this.setActiveItem(t, true);
3864         //    }
3865         //}
3866         
3867         this.fireEvent("mouseover", this, e, t);
3868     },
3869     isVisible : function(){
3870         return !this.hidden;
3871     },
3872     onMouseOut : function(e){
3873         var t  = this.findTargetItem(e);
3874         
3875         //if(t ){
3876         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3877         //        this.activeItem.deactivate();
3878         //        delete this.activeItem;
3879         //    }
3880         //}
3881         this.fireEvent("mouseout", this, e, t);
3882     },
3883     
3884     
3885     /**
3886      * Displays this menu relative to another element
3887      * @param {String/HTMLElement/Roo.Element} element The element to align to
3888      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889      * the element (defaults to this.defaultAlign)
3890      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891      */
3892     show : function(el, pos, parentMenu)
3893     {
3894         if (false === this.fireEvent("beforeshow", this)) {
3895             Roo.log("show canceled");
3896             return;
3897         }
3898         this.parentMenu = parentMenu;
3899         if(!this.el){
3900             this.render();
3901         }
3902         this.el.addClass('show'); // show otherwise we do not know how big we are..
3903          
3904         var xy = this.el.getAlignToXY(el, pos);
3905         
3906         // bl-tl << left align  below
3907         // tl-bl << left align 
3908         
3909         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910             // if it goes to far to the right.. -> align left.
3911             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3912         }
3913         if(xy[0] < 0){
3914             // was left align - go right?
3915             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3916         }
3917         
3918         // goes down the bottom
3919         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920            xy[1]  < 0 ){
3921             var a = this.align.replace('?', '').split('-');
3922             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3923             
3924         }
3925         
3926         this.showAt(  xy , parentMenu, false);
3927     },
3928      /**
3929      * Displays this menu at a specific xy position
3930      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932      */
3933     showAt : function(xy, parentMenu, /* private: */_e){
3934         this.parentMenu = parentMenu;
3935         if(!this.el){
3936             this.render();
3937         }
3938         if(_e !== false){
3939             this.fireEvent("beforeshow", this);
3940             //xy = this.el.adjustForConstraints(xy);
3941         }
3942         
3943         //this.el.show();
3944         this.hideMenuItems();
3945         this.hidden = false;
3946         if (this.triggerEl) {
3947             this.triggerEl.addClass('open');
3948         }
3949         
3950         this.el.addClass('show');
3951         
3952         
3953         
3954         // reassign x when hitting right
3955         
3956         // reassign y when hitting bottom
3957         
3958         // but the list may align on trigger left or trigger top... should it be a properity?
3959         
3960         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3961             this.el.setXY(xy);
3962         }
3963         
3964         this.focus();
3965         this.fireEvent("show", this);
3966     },
3967     
3968     focus : function(){
3969         return;
3970         if(!this.hidden){
3971             this.doFocus.defer(50, this);
3972         }
3973     },
3974
3975     doFocus : function(){
3976         if(!this.hidden){
3977             this.focusEl.focus();
3978         }
3979     },
3980
3981     /**
3982      * Hides this menu and optionally all parent menus
3983      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984      */
3985     hide : function(deep)
3986     {
3987         if (false === this.fireEvent("beforehide", this)) {
3988             Roo.log("hide canceled");
3989             return;
3990         }
3991         this.hideMenuItems();
3992         if(this.el && this.isVisible()){
3993            
3994             if(this.activeItem){
3995                 this.activeItem.deactivate();
3996                 this.activeItem = null;
3997             }
3998             if (this.triggerEl) {
3999                 this.triggerEl.removeClass('open');
4000             }
4001             
4002             this.el.removeClass('show');
4003             this.hidden = true;
4004             this.fireEvent("hide", this);
4005         }
4006         if(deep === true && this.parentMenu){
4007             this.parentMenu.hide(true);
4008         }
4009     },
4010     
4011     onTriggerClick : function(e)
4012     {
4013         Roo.log('trigger click');
4014         
4015         var target = e.getTarget();
4016         
4017         Roo.log(target.nodeName.toLowerCase());
4018         
4019         if(target.nodeName.toLowerCase() === 'i'){
4020             e.preventDefault();
4021         }
4022         
4023     },
4024     
4025     onTriggerPress  : function(e)
4026     {
4027         Roo.log('trigger press');
4028         //Roo.log(e.getTarget());
4029        // Roo.log(this.triggerEl.dom);
4030        
4031         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032         var pel = Roo.get(e.getTarget());
4033         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034             Roo.log('is treeview or dropdown?');
4035             return;
4036         }
4037         
4038         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039             return;
4040         }
4041         
4042         if (this.isVisible()) {
4043             Roo.log('hide');
4044             this.hide();
4045         } else {
4046             Roo.log('show');
4047             
4048             this.show(this.triggerEl, this.align, false);
4049         }
4050         
4051         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4052             e.stopEvent();
4053         }
4054         
4055     },
4056        
4057     
4058     hideMenuItems : function()
4059     {
4060         Roo.log("hide Menu Items");
4061         if (!this.el) { 
4062             return;
4063         }
4064         
4065         this.el.select('.open',true).each(function(aa) {
4066             
4067             aa.removeClass('open');
4068          
4069         });
4070     },
4071     addxtypeChild : function (tree, cntr) {
4072         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073           
4074         this.menuitems.add(comp);
4075         return comp;
4076
4077     },
4078     getEl : function()
4079     {
4080         Roo.log(this.el);
4081         return this.el;
4082     },
4083     
4084     clear : function()
4085     {
4086         this.getEl().dom.innerHTML = '';
4087         this.menuitems.clear();
4088     }
4089 });
4090
4091  
4092  /*
4093  * - LGPL
4094  *
4095  * menu item
4096  * 
4097  */
4098
4099
4100 /**
4101  * @class Roo.bootstrap.MenuItem
4102  * @extends Roo.bootstrap.Component
4103  * Bootstrap MenuItem class
4104  * @cfg {String} html the menu label
4105  * @cfg {String} href the link
4106  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4107  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4108  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4109  * @cfg {String} fa favicon to show on left of menu item.
4110  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4111  * 
4112  * 
4113  * @constructor
4114  * Create a new MenuItem
4115  * @param {Object} config The config object
4116  */
4117
4118
4119 Roo.bootstrap.MenuItem = function(config){
4120     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4121     this.addEvents({
4122         // raw events
4123         /**
4124          * @event click
4125          * The raw click event for the entire grid.
4126          * @param {Roo.bootstrap.MenuItem} this
4127          * @param {Roo.EventObject} e
4128          */
4129         "click" : true
4130     });
4131 };
4132
4133 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4134     
4135     href : false,
4136     html : false,
4137     preventDefault: false,
4138     isContainer : false,
4139     active : false,
4140     fa: false,
4141     
4142     getAutoCreate : function(){
4143         
4144         if(this.isContainer){
4145             return {
4146                 tag: 'li',
4147                 cls: 'dropdown-menu-item '
4148             };
4149         }
4150         var ctag = {
4151             tag: 'span',
4152             html: 'Link'
4153         };
4154         
4155         var anc = {
4156             tag : 'a',
4157             cls : 'dropdown-item',
4158             href : '#',
4159             cn : [  ]
4160         };
4161         
4162         if (this.fa !== false) {
4163             anc.cn.push({
4164                 tag : 'i',
4165                 cls : 'fa fa-' + this.fa
4166             });
4167         }
4168         
4169         anc.cn.push(ctag);
4170         
4171         
4172         var cfg= {
4173             tag: 'li',
4174             cls: 'dropdown-menu-item',
4175             cn: [ anc ]
4176         };
4177         if (this.parent().type == 'treeview') {
4178             cfg.cls = 'treeview-menu';
4179         }
4180         if (this.active) {
4181             cfg.cls += ' active';
4182         }
4183         
4184         
4185         
4186         anc.href = this.href || cfg.cn[0].href ;
4187         ctag.html = this.html || cfg.cn[0].html ;
4188         return cfg;
4189     },
4190     
4191     initEvents: function()
4192     {
4193         if (this.parent().type == 'treeview') {
4194             this.el.select('a').on('click', this.onClick, this);
4195         }
4196         
4197         if (this.menu) {
4198             this.menu.parentType = this.xtype;
4199             this.menu.triggerEl = this.el;
4200             this.menu = this.addxtype(Roo.apply({}, this.menu));
4201         }
4202         
4203     },
4204     onClick : function(e)
4205     {
4206         Roo.log('item on click ');
4207         
4208         if(this.preventDefault){
4209             e.preventDefault();
4210         }
4211         //this.parent().hideMenuItems();
4212         
4213         this.fireEvent('click', this, e);
4214     },
4215     getEl : function()
4216     {
4217         return this.el;
4218     } 
4219 });
4220
4221  
4222
4223  /*
4224  * - LGPL
4225  *
4226  * menu separator
4227  * 
4228  */
4229
4230
4231 /**
4232  * @class Roo.bootstrap.MenuSeparator
4233  * @extends Roo.bootstrap.Component
4234  * Bootstrap MenuSeparator class
4235  * 
4236  * @constructor
4237  * Create a new MenuItem
4238  * @param {Object} config The config object
4239  */
4240
4241
4242 Roo.bootstrap.MenuSeparator = function(config){
4243     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4244 };
4245
4246 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4247     
4248     getAutoCreate : function(){
4249         var cfg = {
4250             cls: 'divider',
4251             tag : 'li'
4252         };
4253         
4254         return cfg;
4255     }
4256    
4257 });
4258
4259  
4260
4261  
4262 /*
4263 * Licence: LGPL
4264 */
4265
4266 /**
4267  * @class Roo.bootstrap.Modal
4268  * @extends Roo.bootstrap.Component
4269  * @builder-top
4270  * @parent none
4271  * @children Roo.bootstrap.Component
4272  * Bootstrap Modal class
4273  * @cfg {String} title Title of dialog
4274  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4275  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4276  * @cfg {Boolean} specificTitle default false
4277  * @cfg {Array} buttons Array of buttons or standard button set..
4278  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4279  * @cfg {Boolean} animate default true
4280  * @cfg {Boolean} allow_close default true
4281  * @cfg {Boolean} fitwindow default false
4282  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4283  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4284  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4285  * @cfg {String} size (sm|lg|xl) default empty
4286  * @cfg {Number} max_width set the max width of modal
4287  * @cfg {Boolean} editableTitle can the title be edited
4288
4289  *
4290  *
4291  * @constructor
4292  * Create a new Modal Dialog
4293  * @param {Object} config The config object
4294  */
4295
4296 Roo.bootstrap.Modal = function(config){
4297     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4298     this.addEvents({
4299         // raw events
4300         /**
4301          * @event btnclick
4302          * The raw btnclick event for the button
4303          * @param {Roo.EventObject} e
4304          */
4305         "btnclick" : true,
4306         /**
4307          * @event resize
4308          * Fire when dialog resize
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} e
4311          */
4312         "resize" : true,
4313         /**
4314          * @event titlechanged
4315          * Fire when the editable title has been changed
4316          * @param {Roo.bootstrap.Modal} this
4317          * @param {Roo.EventObject} value
4318          */
4319         "titlechanged" : true 
4320         
4321     });
4322     this.buttons = this.buttons || [];
4323
4324     if (this.tmpl) {
4325         this.tmpl = Roo.factory(this.tmpl);
4326     }
4327
4328 };
4329
4330 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4331
4332     title : 'test dialog',
4333
4334     buttons : false,
4335
4336     // set on load...
4337
4338     html: false,
4339
4340     tmp: false,
4341
4342     specificTitle: false,
4343
4344     buttonPosition: 'right',
4345
4346     allow_close : true,
4347
4348     animate : true,
4349
4350     fitwindow: false,
4351     
4352      // private
4353     dialogEl: false,
4354     bodyEl:  false,
4355     footerEl:  false,
4356     titleEl:  false,
4357     closeEl:  false,
4358
4359     size: '',
4360     
4361     max_width: 0,
4362     
4363     max_height: 0,
4364     
4365     fit_content: false,
4366     editableTitle  : false,
4367
4368     onRender : function(ct, position)
4369     {
4370         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4371
4372         if(!this.el){
4373             var cfg = Roo.apply({},  this.getAutoCreate());
4374             cfg.id = Roo.id();
4375             //if(!cfg.name){
4376             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4377             //}
4378             //if (!cfg.name.length) {
4379             //    delete cfg.name;
4380            // }
4381             if (this.cls) {
4382                 cfg.cls += ' ' + this.cls;
4383             }
4384             if (this.style) {
4385                 cfg.style = this.style;
4386             }
4387             this.el = Roo.get(document.body).createChild(cfg, position);
4388         }
4389         //var type = this.el.dom.type;
4390
4391
4392         if(this.tabIndex !== undefined){
4393             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4394         }
4395
4396         this.dialogEl = this.el.select('.modal-dialog',true).first();
4397         this.bodyEl = this.el.select('.modal-body',true).first();
4398         this.closeEl = this.el.select('.modal-header .close', true).first();
4399         this.headerEl = this.el.select('.modal-header',true).first();
4400         this.titleEl = this.el.select('.modal-title',true).first();
4401         this.footerEl = this.el.select('.modal-footer',true).first();
4402
4403         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4404         
4405         //this.el.addClass("x-dlg-modal");
4406
4407         if (this.buttons.length) {
4408             Roo.each(this.buttons, function(bb) {
4409                 var b = Roo.apply({}, bb);
4410                 b.xns = b.xns || Roo.bootstrap;
4411                 b.xtype = b.xtype || 'Button';
4412                 if (typeof(b.listeners) == 'undefined') {
4413                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4414                 }
4415
4416                 var btn = Roo.factory(b);
4417
4418                 btn.render(this.getButtonContainer());
4419
4420             },this);
4421         }
4422         // render the children.
4423         var nitems = [];
4424
4425         if(typeof(this.items) != 'undefined'){
4426             var items = this.items;
4427             delete this.items;
4428
4429             for(var i =0;i < items.length;i++) {
4430                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4431             }
4432         }
4433
4434         this.items = nitems;
4435
4436         // where are these used - they used to be body/close/footer
4437
4438
4439         this.initEvents();
4440         //this.el.addClass([this.fieldClass, this.cls]);
4441
4442     },
4443
4444     getAutoCreate : function()
4445     {
4446         // we will default to modal-body-overflow - might need to remove or make optional later.
4447         var bdy = {
4448                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4449                 html : this.html || ''
4450         };
4451
4452         var title = {
4453             tag: 'h5',
4454             cls : 'modal-title',
4455             html : this.title
4456         };
4457
4458         if(this.specificTitle){ // WTF is this?
4459             title = this.title;
4460         }
4461
4462         var header = [];
4463         if (this.allow_close && Roo.bootstrap.version == 3) {
4464             header.push({
4465                 tag: 'button',
4466                 cls : 'close',
4467                 html : '&times'
4468             });
4469         }
4470
4471         header.push(title);
4472
4473         if (this.editableTitle) {
4474             header.push({
4475                 cls: 'form-control roo-editable-title d-none',
4476                 tag: 'input',
4477                 type: 'text'
4478             });
4479         }
4480         
4481         if (this.allow_close && Roo.bootstrap.version == 4) {
4482             header.push({
4483                 tag: 'button',
4484                 cls : 'close',
4485                 html : '&times'
4486             });
4487         }
4488         
4489         var size = '';
4490
4491         if(this.size.length){
4492             size = 'modal-' + this.size;
4493         }
4494         
4495         var footer = Roo.bootstrap.version == 3 ?
4496             {
4497                 cls : 'modal-footer',
4498                 cn : [
4499                     {
4500                         tag: 'div',
4501                         cls: 'btn-' + this.buttonPosition
4502                     }
4503                 ]
4504
4505             } :
4506             {  // BS4 uses mr-auto on left buttons....
4507                 cls : 'modal-footer'
4508             };
4509
4510             
4511
4512         
4513         
4514         var modal = {
4515             cls: "modal",
4516              cn : [
4517                 {
4518                     cls: "modal-dialog " + size,
4519                     cn : [
4520                         {
4521                             cls : "modal-content",
4522                             cn : [
4523                                 {
4524                                     cls : 'modal-header',
4525                                     cn : header
4526                                 },
4527                                 bdy,
4528                                 footer
4529                             ]
4530
4531                         }
4532                     ]
4533
4534                 }
4535             ]
4536         };
4537
4538         if(this.animate){
4539             modal.cls += ' fade';
4540         }
4541
4542         return modal;
4543
4544     },
4545     getChildContainer : function() {
4546
4547          return this.bodyEl;
4548
4549     },
4550     getButtonContainer : function() {
4551         
4552          return Roo.bootstrap.version == 4 ?
4553             this.el.select('.modal-footer',true).first()
4554             : this.el.select('.modal-footer div',true).first();
4555
4556     },
4557     initEvents : function()
4558     {
4559         if (this.allow_close) {
4560             this.closeEl.on('click', this.hide, this);
4561         }
4562         Roo.EventManager.onWindowResize(this.resize, this, true);
4563         if (this.editableTitle) {
4564             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4565             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4566             this.headerEditEl.on('keyup', function(e) {
4567                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4568                         this.toggleHeaderInput(false)
4569                     }
4570                 }, this);
4571             this.headerEditEl.on('blur', function(e) {
4572                 this.toggleHeaderInput(false)
4573             },this);
4574         }
4575
4576     },
4577   
4578
4579     resize : function()
4580     {
4581         this.maskEl.setSize(
4582             Roo.lib.Dom.getViewWidth(true),
4583             Roo.lib.Dom.getViewHeight(true)
4584         );
4585         
4586         if (this.fitwindow) {
4587             
4588            this.dialogEl.setStyle( { 'max-width' : '100%' });
4589             this.setSize(
4590                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4591                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4592             );
4593             return;
4594         }
4595         
4596         if(this.max_width !== 0) {
4597             
4598             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4599             
4600             if(this.height) {
4601                 this.setSize(w, this.height);
4602                 return;
4603             }
4604             
4605             if(this.max_height) {
4606                 this.setSize(w,Math.min(
4607                     this.max_height,
4608                     Roo.lib.Dom.getViewportHeight(true) - 60
4609                 ));
4610                 
4611                 return;
4612             }
4613             
4614             if(!this.fit_content) {
4615                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4616                 return;
4617             }
4618             
4619             this.setSize(w, Math.min(
4620                 60 +
4621                 this.headerEl.getHeight() + 
4622                 this.footerEl.getHeight() + 
4623                 this.getChildHeight(this.bodyEl.dom.childNodes),
4624                 Roo.lib.Dom.getViewportHeight(true) - 60)
4625             );
4626         }
4627         
4628     },
4629
4630     setSize : function(w,h)
4631     {
4632         if (!w && !h) {
4633             return;
4634         }
4635         
4636         this.resizeTo(w,h);
4637     },
4638
4639     show : function() {
4640
4641         if (!this.rendered) {
4642             this.render();
4643         }
4644         this.toggleHeaderInput(false);
4645         //this.el.setStyle('display', 'block');
4646         this.el.removeClass('hideing');
4647         this.el.dom.style.display='block';
4648         
4649         Roo.get(document.body).addClass('modal-open');
4650  
4651         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4652             
4653             (function(){
4654                 this.el.addClass('show');
4655                 this.el.addClass('in');
4656             }).defer(50, this);
4657         }else{
4658             this.el.addClass('show');
4659             this.el.addClass('in');
4660         }
4661
4662         // not sure how we can show data in here..
4663         //if (this.tmpl) {
4664         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4665         //}
4666
4667         Roo.get(document.body).addClass("x-body-masked");
4668         
4669         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4670         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4671         this.maskEl.dom.style.display = 'block';
4672         this.maskEl.addClass('show');
4673         
4674         
4675         this.resize();
4676         
4677         this.fireEvent('show', this);
4678
4679         // set zindex here - otherwise it appears to be ignored...
4680         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4681
4682         (function () {
4683             this.items.forEach( function(e) {
4684                 e.layout ? e.layout() : false;
4685
4686             });
4687         }).defer(100,this);
4688
4689     },
4690     hide : function()
4691     {
4692         if(this.fireEvent("beforehide", this) !== false){
4693             
4694             this.maskEl.removeClass('show');
4695             
4696             this.maskEl.dom.style.display = '';
4697             Roo.get(document.body).removeClass("x-body-masked");
4698             this.el.removeClass('in');
4699             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4700
4701             if(this.animate){ // why
4702                 this.el.addClass('hideing');
4703                 this.el.removeClass('show');
4704                 (function(){
4705                     if (!this.el.hasClass('hideing')) {
4706                         return; // it's been shown again...
4707                     }
4708                     
4709                     this.el.dom.style.display='';
4710
4711                     Roo.get(document.body).removeClass('modal-open');
4712                     this.el.removeClass('hideing');
4713                 }).defer(150,this);
4714                 
4715             }else{
4716                 this.el.removeClass('show');
4717                 this.el.dom.style.display='';
4718                 Roo.get(document.body).removeClass('modal-open');
4719
4720             }
4721             this.fireEvent('hide', this);
4722         }
4723     },
4724     isVisible : function()
4725     {
4726         
4727         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4728         
4729     },
4730
4731     addButton : function(str, cb)
4732     {
4733
4734
4735         var b = Roo.apply({}, { html : str } );
4736         b.xns = b.xns || Roo.bootstrap;
4737         b.xtype = b.xtype || 'Button';
4738         if (typeof(b.listeners) == 'undefined') {
4739             b.listeners = { click : cb.createDelegate(this)  };
4740         }
4741
4742         var btn = Roo.factory(b);
4743
4744         btn.render(this.getButtonContainer());
4745
4746         return btn;
4747
4748     },
4749
4750     setDefaultButton : function(btn)
4751     {
4752         //this.el.select('.modal-footer').()
4753     },
4754
4755     resizeTo: function(w,h)
4756     {
4757         this.dialogEl.setWidth(w);
4758         
4759         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4760
4761         this.bodyEl.setHeight(h - diff);
4762         
4763         this.fireEvent('resize', this);
4764     },
4765     
4766     setContentSize  : function(w, h)
4767     {
4768
4769     },
4770     onButtonClick: function(btn,e)
4771     {
4772         //Roo.log([a,b,c]);
4773         this.fireEvent('btnclick', btn.name, e);
4774     },
4775      /**
4776      * Set the title of the Dialog
4777      * @param {String} str new Title
4778      */
4779     setTitle: function(str) {
4780         this.titleEl.dom.innerHTML = str;
4781         this.title = str;
4782     },
4783     /**
4784      * Set the body of the Dialog
4785      * @param {String} str new Title
4786      */
4787     setBody: function(str) {
4788         this.bodyEl.dom.innerHTML = str;
4789     },
4790     /**
4791      * Set the body of the Dialog using the template
4792      * @param {Obj} data - apply this data to the template and replace the body contents.
4793      */
4794     applyBody: function(obj)
4795     {
4796         if (!this.tmpl) {
4797             Roo.log("Error - using apply Body without a template");
4798             //code
4799         }
4800         this.tmpl.overwrite(this.bodyEl, obj);
4801     },
4802     
4803     getChildHeight : function(child_nodes)
4804     {
4805         if(
4806             !child_nodes ||
4807             child_nodes.length == 0
4808         ) {
4809             return 0;
4810         }
4811         
4812         var child_height = 0;
4813         
4814         for(var i = 0; i < child_nodes.length; i++) {
4815             
4816             /*
4817             * for modal with tabs...
4818             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4819                 
4820                 var layout_childs = child_nodes[i].childNodes;
4821                 
4822                 for(var j = 0; j < layout_childs.length; j++) {
4823                     
4824                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4825                         
4826                         var layout_body_childs = layout_childs[j].childNodes;
4827                         
4828                         for(var k = 0; k < layout_body_childs.length; k++) {
4829                             
4830                             if(layout_body_childs[k].classList.contains('navbar')) {
4831                                 child_height += layout_body_childs[k].offsetHeight;
4832                                 continue;
4833                             }
4834                             
4835                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4836                                 
4837                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4838                                 
4839                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4840                                     
4841                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4842                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4843                                         continue;
4844                                     }
4845                                     
4846                                 }
4847                                 
4848                             }
4849                             
4850                         }
4851                     }
4852                 }
4853                 continue;
4854             }
4855             */
4856             
4857             child_height += child_nodes[i].offsetHeight;
4858             // Roo.log(child_nodes[i].offsetHeight);
4859         }
4860         
4861         return child_height;
4862     },
4863     toggleHeaderInput : function(is_edit)
4864     {
4865         if (!this.editableTitle) {
4866             return; // not editable.
4867         }
4868         if (is_edit && this.is_header_editing) {
4869             return; // already editing..
4870         }
4871         if (is_edit) {
4872     
4873             this.headerEditEl.dom.value = this.title;
4874             this.headerEditEl.removeClass('d-none');
4875             this.headerEditEl.dom.focus();
4876             this.titleEl.addClass('d-none');
4877             
4878             this.is_header_editing = true;
4879             return
4880         }
4881         // flip back to not editing.
4882         this.title = this.headerEditEl.dom.value;
4883         this.headerEditEl.addClass('d-none');
4884         this.titleEl.removeClass('d-none');
4885         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4886         this.is_header_editing = false;
4887         this.fireEvent('titlechanged', this, this.title);
4888     
4889             
4890         
4891     }
4892
4893 });
4894
4895
4896 Roo.apply(Roo.bootstrap.Modal,  {
4897     /**
4898          * Button config that displays a single OK button
4899          * @type Object
4900          */
4901         OK :  [{
4902             name : 'ok',
4903             weight : 'primary',
4904             html : 'OK'
4905         }],
4906         /**
4907          * Button config that displays Yes and No buttons
4908          * @type Object
4909          */
4910         YESNO : [
4911             {
4912                 name  : 'no',
4913                 html : 'No'
4914             },
4915             {
4916                 name  :'yes',
4917                 weight : 'primary',
4918                 html : 'Yes'
4919             }
4920         ],
4921
4922         /**
4923          * Button config that displays OK and Cancel buttons
4924          * @type Object
4925          */
4926         OKCANCEL : [
4927             {
4928                name : 'cancel',
4929                 html : 'Cancel'
4930             },
4931             {
4932                 name : 'ok',
4933                 weight : 'primary',
4934                 html : 'OK'
4935             }
4936         ],
4937         /**
4938          * Button config that displays Yes, No and Cancel buttons
4939          * @type Object
4940          */
4941         YESNOCANCEL : [
4942             {
4943                 name : 'yes',
4944                 weight : 'primary',
4945                 html : 'Yes'
4946             },
4947             {
4948                 name : 'no',
4949                 html : 'No'
4950             },
4951             {
4952                 name : 'cancel',
4953                 html : 'Cancel'
4954             }
4955         ],
4956         
4957         zIndex : 10001
4958 });
4959
4960 /*
4961  * - LGPL
4962  *
4963  * messagebox - can be used as a replace
4964  * 
4965  */
4966 /**
4967  * @class Roo.MessageBox
4968  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4969  * Example usage:
4970  *<pre><code>
4971 // Basic alert:
4972 Roo.Msg.alert('Status', 'Changes saved successfully.');
4973
4974 // Prompt for user data:
4975 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4976     if (btn == 'ok'){
4977         // process text value...
4978     }
4979 });
4980
4981 // Show a dialog using config options:
4982 Roo.Msg.show({
4983    title:'Save Changes?',
4984    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4985    buttons: Roo.Msg.YESNOCANCEL,
4986    fn: processResult,
4987    animEl: 'elId'
4988 });
4989 </code></pre>
4990  * @singleton
4991  */
4992 Roo.bootstrap.MessageBox = function(){
4993     var dlg, opt, mask, waitTimer;
4994     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4995     var buttons, activeTextEl, bwidth;
4996
4997     
4998     // private
4999     var handleButton = function(button){
5000         dlg.hide();
5001         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5002     };
5003
5004     // private
5005     var handleHide = function(){
5006         if(opt && opt.cls){
5007             dlg.el.removeClass(opt.cls);
5008         }
5009         //if(waitTimer){
5010         //    Roo.TaskMgr.stop(waitTimer);
5011         //    waitTimer = null;
5012         //}
5013     };
5014
5015     // private
5016     var updateButtons = function(b){
5017         var width = 0;
5018         if(!b){
5019             buttons["ok"].hide();
5020             buttons["cancel"].hide();
5021             buttons["yes"].hide();
5022             buttons["no"].hide();
5023             dlg.footerEl.hide();
5024             
5025             return width;
5026         }
5027         dlg.footerEl.show();
5028         for(var k in buttons){
5029             if(typeof buttons[k] != "function"){
5030                 if(b[k]){
5031                     buttons[k].show();
5032                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5033                     width += buttons[k].el.getWidth()+15;
5034                 }else{
5035                     buttons[k].hide();
5036                 }
5037             }
5038         }
5039         return width;
5040     };
5041
5042     // private
5043     var handleEsc = function(d, k, e){
5044         if(opt && opt.closable !== false){
5045             dlg.hide();
5046         }
5047         if(e){
5048             e.stopEvent();
5049         }
5050     };
5051
5052     return {
5053         /**
5054          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5055          * @return {Roo.BasicDialog} The BasicDialog element
5056          */
5057         getDialog : function(){
5058            if(!dlg){
5059                 dlg = new Roo.bootstrap.Modal( {
5060                     //draggable: true,
5061                     //resizable:false,
5062                     //constraintoviewport:false,
5063                     //fixedcenter:true,
5064                     //collapsible : false,
5065                     //shim:true,
5066                     //modal: true,
5067                 //    width: 'auto',
5068                   //  height:100,
5069                     //buttonAlign:"center",
5070                     closeClick : function(){
5071                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5072                             handleButton("no");
5073                         }else{
5074                             handleButton("cancel");
5075                         }
5076                     }
5077                 });
5078                 dlg.render();
5079                 dlg.on("hide", handleHide);
5080                 mask = dlg.mask;
5081                 //dlg.addKeyListener(27, handleEsc);
5082                 buttons = {};
5083                 this.buttons = buttons;
5084                 var bt = this.buttonText;
5085                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5086                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5087                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5088                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5089                 //Roo.log(buttons);
5090                 bodyEl = dlg.bodyEl.createChild({
5091
5092                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5093                         '<textarea class="roo-mb-textarea"></textarea>' +
5094                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5095                 });
5096                 msgEl = bodyEl.dom.firstChild;
5097                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5098                 textboxEl.enableDisplayMode();
5099                 textboxEl.addKeyListener([10,13], function(){
5100                     if(dlg.isVisible() && opt && opt.buttons){
5101                         if(opt.buttons.ok){
5102                             handleButton("ok");
5103                         }else if(opt.buttons.yes){
5104                             handleButton("yes");
5105                         }
5106                     }
5107                 });
5108                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5109                 textareaEl.enableDisplayMode();
5110                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5111                 progressEl.enableDisplayMode();
5112                 
5113                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5114                 var pf = progressEl.dom.firstChild;
5115                 if (pf) {
5116                     pp = Roo.get(pf.firstChild);
5117                     pp.setHeight(pf.offsetHeight);
5118                 }
5119                 
5120             }
5121             return dlg;
5122         },
5123
5124         /**
5125          * Updates the message box body text
5126          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5127          * the XHTML-compliant non-breaking space character '&amp;#160;')
5128          * @return {Roo.MessageBox} This message box
5129          */
5130         updateText : function(text)
5131         {
5132             if(!dlg.isVisible() && !opt.width){
5133                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5134                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5135             }
5136             msgEl.innerHTML = text || '&#160;';
5137       
5138             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5139             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5140             var w = Math.max(
5141                     Math.min(opt.width || cw , this.maxWidth), 
5142                     Math.max(opt.minWidth || this.minWidth, bwidth)
5143             );
5144             if(opt.prompt){
5145                 activeTextEl.setWidth(w);
5146             }
5147             if(dlg.isVisible()){
5148                 dlg.fixedcenter = false;
5149             }
5150             // to big, make it scroll. = But as usual stupid IE does not support
5151             // !important..
5152             
5153             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5154                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5155                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5156             } else {
5157                 bodyEl.dom.style.height = '';
5158                 bodyEl.dom.style.overflowY = '';
5159             }
5160             if (cw > w) {
5161                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5162             } else {
5163                 bodyEl.dom.style.overflowX = '';
5164             }
5165             
5166             dlg.setContentSize(w, bodyEl.getHeight());
5167             if(dlg.isVisible()){
5168                 dlg.fixedcenter = true;
5169             }
5170             return this;
5171         },
5172
5173         /**
5174          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5175          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5176          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5177          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5178          * @return {Roo.MessageBox} This message box
5179          */
5180         updateProgress : function(value, text){
5181             if(text){
5182                 this.updateText(text);
5183             }
5184             
5185             if (pp) { // weird bug on my firefox - for some reason this is not defined
5186                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5187                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5188             }
5189             return this;
5190         },        
5191
5192         /**
5193          * Returns true if the message box is currently displayed
5194          * @return {Boolean} True if the message box is visible, else false
5195          */
5196         isVisible : function(){
5197             return dlg && dlg.isVisible();  
5198         },
5199
5200         /**
5201          * Hides the message box if it is displayed
5202          */
5203         hide : function(){
5204             if(this.isVisible()){
5205                 dlg.hide();
5206             }  
5207         },
5208
5209         /**
5210          * Displays a new message box, or reinitializes an existing message box, based on the config options
5211          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5212          * The following config object properties are supported:
5213          * <pre>
5214 Property    Type             Description
5215 ----------  ---------------  ------------------------------------------------------------------------------------
5216 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5217                                    closes (defaults to undefined)
5218 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5219                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5220 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5221                                    progress and wait dialogs will ignore this property and always hide the
5222                                    close button as they can only be closed programmatically.
5223 cls               String           A custom CSS class to apply to the message box element
5224 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5225                                    displayed (defaults to 75)
5226 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5227                                    function will be btn (the name of the button that was clicked, if applicable,
5228                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5229                                    Progress and wait dialogs will ignore this option since they do not respond to
5230                                    user actions and can only be closed programmatically, so any required function
5231                                    should be called by the same code after it closes the dialog.
5232 icon              String           A CSS class that provides a background image to be used as an icon for
5233                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5234 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5235 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5236 modal             Boolean          False to allow user interaction with the page while the message box is
5237                                    displayed (defaults to true)
5238 msg               String           A string that will replace the existing message box body text (defaults
5239                                    to the XHTML-compliant non-breaking space character '&#160;')
5240 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5241 progress          Boolean          True to display a progress bar (defaults to false)
5242 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5243 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5244 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5245 title             String           The title text
5246 value             String           The string value to set into the active textbox element if displayed
5247 wait              Boolean          True to display a progress bar (defaults to false)
5248 width             Number           The width of the dialog in pixels
5249 </pre>
5250          *
5251          * Example usage:
5252          * <pre><code>
5253 Roo.Msg.show({
5254    title: 'Address',
5255    msg: 'Please enter your address:',
5256    width: 300,
5257    buttons: Roo.MessageBox.OKCANCEL,
5258    multiline: true,
5259    fn: saveAddress,
5260    animEl: 'addAddressBtn'
5261 });
5262 </code></pre>
5263          * @param {Object} config Configuration options
5264          * @return {Roo.MessageBox} This message box
5265          */
5266         show : function(options)
5267         {
5268             
5269             // this causes nightmares if you show one dialog after another
5270             // especially on callbacks..
5271              
5272             if(this.isVisible()){
5273                 
5274                 this.hide();
5275                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5276                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5277                 Roo.log("New Dialog Message:" +  options.msg )
5278                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5279                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5280                 
5281             }
5282             var d = this.getDialog();
5283             opt = options;
5284             d.setTitle(opt.title || "&#160;");
5285             d.closeEl.setDisplayed(opt.closable !== false);
5286             activeTextEl = textboxEl;
5287             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5288             if(opt.prompt){
5289                 if(opt.multiline){
5290                     textboxEl.hide();
5291                     textareaEl.show();
5292                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5293                         opt.multiline : this.defaultTextHeight);
5294                     activeTextEl = textareaEl;
5295                 }else{
5296                     textboxEl.show();
5297                     textareaEl.hide();
5298                 }
5299             }else{
5300                 textboxEl.hide();
5301                 textareaEl.hide();
5302             }
5303             progressEl.setDisplayed(opt.progress === true);
5304             if (opt.progress) {
5305                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5306             }
5307             this.updateProgress(0);
5308             activeTextEl.dom.value = opt.value || "";
5309             if(opt.prompt){
5310                 dlg.setDefaultButton(activeTextEl);
5311             }else{
5312                 var bs = opt.buttons;
5313                 var db = null;
5314                 if(bs && bs.ok){
5315                     db = buttons["ok"];
5316                 }else if(bs && bs.yes){
5317                     db = buttons["yes"];
5318                 }
5319                 dlg.setDefaultButton(db);
5320             }
5321             bwidth = updateButtons(opt.buttons);
5322             this.updateText(opt.msg);
5323             if(opt.cls){
5324                 d.el.addClass(opt.cls);
5325             }
5326             d.proxyDrag = opt.proxyDrag === true;
5327             d.modal = opt.modal !== false;
5328             d.mask = opt.modal !== false ? mask : false;
5329             if(!d.isVisible()){
5330                 // force it to the end of the z-index stack so it gets a cursor in FF
5331                 document.body.appendChild(dlg.el.dom);
5332                 d.animateTarget = null;
5333                 d.show(options.animEl);
5334             }
5335             return this;
5336         },
5337
5338         /**
5339          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5340          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5341          * and closing the message box when the process is complete.
5342          * @param {String} title The title bar text
5343          * @param {String} msg The message box body text
5344          * @return {Roo.MessageBox} This message box
5345          */
5346         progress : function(title, msg){
5347             this.show({
5348                 title : title,
5349                 msg : msg,
5350                 buttons: false,
5351                 progress:true,
5352                 closable:false,
5353                 minWidth: this.minProgressWidth,
5354                 modal : true
5355             });
5356             return this;
5357         },
5358
5359         /**
5360          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5361          * If a callback function is passed it will be called after the user clicks the button, and the
5362          * id of the button that was clicked will be passed as the only parameter to the callback
5363          * (could also be the top-right close button).
5364          * @param {String} title The title bar text
5365          * @param {String} msg The message box body text
5366          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5367          * @param {Object} scope (optional) The scope of the callback function
5368          * @return {Roo.MessageBox} This message box
5369          */
5370         alert : function(title, msg, fn, scope)
5371         {
5372             this.show({
5373                 title : title,
5374                 msg : msg,
5375                 buttons: this.OK,
5376                 fn: fn,
5377                 closable : false,
5378                 scope : scope,
5379                 modal : true
5380             });
5381             return this;
5382         },
5383
5384         /**
5385          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5386          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5387          * You are responsible for closing the message box when the process is complete.
5388          * @param {String} msg The message box body text
5389          * @param {String} title (optional) The title bar text
5390          * @return {Roo.MessageBox} This message box
5391          */
5392         wait : function(msg, title){
5393             this.show({
5394                 title : title,
5395                 msg : msg,
5396                 buttons: false,
5397                 closable:false,
5398                 progress:true,
5399                 modal:true,
5400                 width:300,
5401                 wait:true
5402             });
5403             waitTimer = Roo.TaskMgr.start({
5404                 run: function(i){
5405                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5406                 },
5407                 interval: 1000
5408             });
5409             return this;
5410         },
5411
5412         /**
5413          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5414          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5415          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5416          * @param {String} title The title bar text
5417          * @param {String} msg The message box body text
5418          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5419          * @param {Object} scope (optional) The scope of the callback function
5420          * @return {Roo.MessageBox} This message box
5421          */
5422         confirm : function(title, msg, fn, scope){
5423             this.show({
5424                 title : title,
5425                 msg : msg,
5426                 buttons: this.YESNO,
5427                 fn: fn,
5428                 scope : scope,
5429                 modal : true
5430             });
5431             return this;
5432         },
5433
5434         /**
5435          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5436          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5437          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5438          * (could also be the top-right close button) and the text that was entered will be passed as the two
5439          * parameters to the callback.
5440          * @param {String} title The title bar text
5441          * @param {String} msg The message box body text
5442          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5443          * @param {Object} scope (optional) The scope of the callback function
5444          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5445          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5446          * @return {Roo.MessageBox} This message box
5447          */
5448         prompt : function(title, msg, fn, scope, multiline){
5449             this.show({
5450                 title : title,
5451                 msg : msg,
5452                 buttons: this.OKCANCEL,
5453                 fn: fn,
5454                 minWidth:250,
5455                 scope : scope,
5456                 prompt:true,
5457                 multiline: multiline,
5458                 modal : true
5459             });
5460             return this;
5461         },
5462
5463         /**
5464          * Button config that displays a single OK button
5465          * @type Object
5466          */
5467         OK : {ok:true},
5468         /**
5469          * Button config that displays Yes and No buttons
5470          * @type Object
5471          */
5472         YESNO : {yes:true, no:true},
5473         /**
5474          * Button config that displays OK and Cancel buttons
5475          * @type Object
5476          */
5477         OKCANCEL : {ok:true, cancel:true},
5478         /**
5479          * Button config that displays Yes, No and Cancel buttons
5480          * @type Object
5481          */
5482         YESNOCANCEL : {yes:true, no:true, cancel:true},
5483
5484         /**
5485          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5486          * @type Number
5487          */
5488         defaultTextHeight : 75,
5489         /**
5490          * The maximum width in pixels of the message box (defaults to 600)
5491          * @type Number
5492          */
5493         maxWidth : 600,
5494         /**
5495          * The minimum width in pixels of the message box (defaults to 100)
5496          * @type Number
5497          */
5498         minWidth : 100,
5499         /**
5500          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5501          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5502          * @type Number
5503          */
5504         minProgressWidth : 250,
5505         /**
5506          * An object containing the default button text strings that can be overriden for localized language support.
5507          * Supported properties are: ok, cancel, yes and no.
5508          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5509          * @type Object
5510          */
5511         buttonText : {
5512             ok : "OK",
5513             cancel : "Cancel",
5514             yes : "Yes",
5515             no : "No"
5516         }
5517     };
5518 }();
5519
5520 /**
5521  * Shorthand for {@link Roo.MessageBox}
5522  */
5523 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5524 Roo.Msg = Roo.Msg || Roo.MessageBox;
5525 /*
5526  * - LGPL
5527  *
5528  * navbar
5529  * 
5530  */
5531
5532 /**
5533  * @class Roo.bootstrap.Navbar
5534  * @extends Roo.bootstrap.Component
5535  * Bootstrap Navbar class
5536
5537  * @constructor
5538  * Create a new Navbar
5539  * @param {Object} config The config object
5540  */
5541
5542
5543 Roo.bootstrap.Navbar = function(config){
5544     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5545     this.addEvents({
5546         // raw events
5547         /**
5548          * @event beforetoggle
5549          * Fire before toggle the menu
5550          * @param {Roo.EventObject} e
5551          */
5552         "beforetoggle" : true
5553     });
5554 };
5555
5556 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5557     
5558     
5559    
5560     // private
5561     navItems : false,
5562     loadMask : false,
5563     
5564     
5565     getAutoCreate : function(){
5566         
5567         
5568         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5569         
5570     },
5571     
5572     initEvents :function ()
5573     {
5574         //Roo.log(this.el.select('.navbar-toggle',true));
5575         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5576         
5577         var mark = {
5578             tag: "div",
5579             cls:"x-dlg-mask"
5580         };
5581         
5582         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5583         
5584         var size = this.el.getSize();
5585         this.maskEl.setSize(size.width, size.height);
5586         this.maskEl.enableDisplayMode("block");
5587         this.maskEl.hide();
5588         
5589         if(this.loadMask){
5590             this.maskEl.show();
5591         }
5592     },
5593     
5594     
5595     getChildContainer : function()
5596     {
5597         if (this.el && this.el.select('.collapse').getCount()) {
5598             return this.el.select('.collapse',true).first();
5599         }
5600         
5601         return this.el;
5602     },
5603     
5604     mask : function()
5605     {
5606         this.maskEl.show();
5607     },
5608     
5609     unmask : function()
5610     {
5611         this.maskEl.hide();
5612     },
5613     onToggle : function()
5614     {
5615         
5616         if(this.fireEvent('beforetoggle', this) === false){
5617             return;
5618         }
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620       
5621         if (!ce.hasClass('show')) {
5622            this.expand();
5623         } else {
5624             this.collapse();
5625         }
5626         
5627         
5628     
5629     },
5630     /**
5631      * Expand the navbar pulldown 
5632      */
5633     expand : function ()
5634     {
5635        
5636         var ce = this.el.select('.navbar-collapse',true).first();
5637         if (ce.hasClass('collapsing')) {
5638             return;
5639         }
5640         ce.dom.style.height = '';
5641                // show it...
5642         ce.addClass('in'); // old...
5643         ce.removeClass('collapse');
5644         ce.addClass('show');
5645         var h = ce.getHeight();
5646         Roo.log(h);
5647         ce.removeClass('show');
5648         // at this point we should be able to see it..
5649         ce.addClass('collapsing');
5650         
5651         ce.setHeight(0); // resize it ...
5652         ce.on('transitionend', function() {
5653             //Roo.log('done transition');
5654             ce.removeClass('collapsing');
5655             ce.addClass('show');
5656             ce.removeClass('collapse');
5657
5658             ce.dom.style.height = '';
5659         }, this, { single: true} );
5660         ce.setHeight(h);
5661         ce.dom.scrollTop = 0;
5662     },
5663     /**
5664      * Collapse the navbar pulldown 
5665      */
5666     collapse : function()
5667     {
5668          var ce = this.el.select('.navbar-collapse',true).first();
5669        
5670         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5671             // it's collapsed or collapsing..
5672             return;
5673         }
5674         ce.removeClass('in'); // old...
5675         ce.setHeight(ce.getHeight());
5676         ce.removeClass('show');
5677         ce.addClass('collapsing');
5678         
5679         ce.on('transitionend', function() {
5680             ce.dom.style.height = '';
5681             ce.removeClass('collapsing');
5682             ce.addClass('collapse');
5683         }, this, { single: true} );
5684         ce.setHeight(0);
5685     }
5686     
5687     
5688     
5689 });
5690
5691
5692
5693  
5694
5695  /*
5696  * - LGPL
5697  *
5698  * navbar
5699  * 
5700  */
5701
5702 /**
5703  * @class Roo.bootstrap.NavSimplebar
5704  * @extends Roo.bootstrap.Navbar
5705  * Bootstrap Sidebar class
5706  *
5707  * @cfg {Boolean} inverse is inverted color
5708  * 
5709  * @cfg {String} type (nav | pills | tabs)
5710  * @cfg {Boolean} arrangement stacked | justified
5711  * @cfg {String} align (left | right) alignment
5712  * 
5713  * @cfg {Boolean} main (true|false) main nav bar? default false
5714  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5715  * 
5716  * @cfg {String} tag (header|footer|nav|div) default is nav 
5717
5718  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5719  * 
5720  * 
5721  * @constructor
5722  * Create a new Sidebar
5723  * @param {Object} config The config object
5724  */
5725
5726
5727 Roo.bootstrap.NavSimplebar = function(config){
5728     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5729 };
5730
5731 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5732     
5733     inverse: false,
5734     
5735     type: false,
5736     arrangement: '',
5737     align : false,
5738     
5739     weight : 'light',
5740     
5741     main : false,
5742     
5743     
5744     tag : false,
5745     
5746     
5747     getAutoCreate : function(){
5748         
5749         
5750         var cfg = {
5751             tag : this.tag || 'div',
5752             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5753         };
5754         if (['light','white'].indexOf(this.weight) > -1) {
5755             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5756         }
5757         cfg.cls += ' bg-' + this.weight;
5758         
5759         if (this.inverse) {
5760             cfg.cls += ' navbar-inverse';
5761             
5762         }
5763         
5764         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5765         
5766         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5767             return cfg;
5768         }
5769         
5770         
5771     
5772         
5773         cfg.cn = [
5774             {
5775                 cls: 'nav nav-' + this.xtype,
5776                 tag : 'ul'
5777             }
5778         ];
5779         
5780          
5781         this.type = this.type || 'nav';
5782         if (['tabs','pills'].indexOf(this.type) != -1) {
5783             cfg.cn[0].cls += ' nav-' + this.type
5784         
5785         
5786         } else {
5787             if (this.type!=='nav') {
5788                 Roo.log('nav type must be nav/tabs/pills')
5789             }
5790             cfg.cn[0].cls += ' navbar-nav'
5791         }
5792         
5793         
5794         
5795         
5796         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5797             cfg.cn[0].cls += ' nav-' + this.arrangement;
5798         }
5799         
5800         
5801         if (this.align === 'right') {
5802             cfg.cn[0].cls += ' navbar-right';
5803         }
5804         
5805         
5806         
5807         
5808         return cfg;
5809     
5810         
5811     }
5812     
5813     
5814     
5815 });
5816
5817
5818
5819  
5820
5821  
5822        /*
5823  * - LGPL
5824  *
5825  * navbar
5826  * navbar-fixed-top
5827  * navbar-expand-md  fixed-top 
5828  */
5829
5830 /**
5831  * @class Roo.bootstrap.NavHeaderbar
5832  * @extends Roo.bootstrap.NavSimplebar
5833  * Bootstrap Sidebar class
5834  *
5835  * @cfg {String} brand what is brand
5836  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5837  * @cfg {String} brand_href href of the brand
5838  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5839  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5840  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5841  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5842  * 
5843  * @constructor
5844  * Create a new Sidebar
5845  * @param {Object} config The config object
5846  */
5847
5848
5849 Roo.bootstrap.NavHeaderbar = function(config){
5850     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5851       
5852 };
5853
5854 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5855     
5856     position: '',
5857     brand: '',
5858     brand_href: false,
5859     srButton : true,
5860     autohide : false,
5861     desktopCenter : false,
5862    
5863     
5864     getAutoCreate : function(){
5865         
5866         var   cfg = {
5867             tag: this.nav || 'nav',
5868             cls: 'navbar navbar-expand-md',
5869             role: 'navigation',
5870             cn: []
5871         };
5872         
5873         var cn = cfg.cn;
5874         if (this.desktopCenter) {
5875             cn.push({cls : 'container', cn : []});
5876             cn = cn[0].cn;
5877         }
5878         
5879         if(this.srButton){
5880             var btn = {
5881                 tag: 'button',
5882                 type: 'button',
5883                 cls: 'navbar-toggle navbar-toggler',
5884                 'data-toggle': 'collapse',
5885                 cn: [
5886                     {
5887                         tag: 'span',
5888                         cls: 'sr-only',
5889                         html: 'Toggle navigation'
5890                     },
5891                     {
5892                         tag: 'span',
5893                         cls: 'icon-bar navbar-toggler-icon'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     }
5903                 ]
5904             };
5905             
5906             cn.push( Roo.bootstrap.version == 4 ? btn : {
5907                 tag: 'div',
5908                 cls: 'navbar-header',
5909                 cn: [
5910                     btn
5911                 ]
5912             });
5913         }
5914         
5915         cn.push({
5916             tag: 'div',
5917             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5918             cn : []
5919         });
5920         
5921         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5922         
5923         if (['light','white'].indexOf(this.weight) > -1) {
5924             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5925         }
5926         cfg.cls += ' bg-' + this.weight;
5927         
5928         
5929         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5930             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5931             
5932             // tag can override this..
5933             
5934             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5935         }
5936         
5937         if (this.brand !== '') {
5938             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5939             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5940                 tag: 'a',
5941                 href: this.brand_href ? this.brand_href : '#',
5942                 cls: 'navbar-brand',
5943                 cn: [
5944                 this.brand
5945                 ]
5946             });
5947         }
5948         
5949         if(this.main){
5950             cfg.cls += ' main-nav';
5951         }
5952         
5953         
5954         return cfg;
5955
5956         
5957     },
5958     getHeaderChildContainer : function()
5959     {
5960         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5961             return this.el.select('.navbar-header',true).first();
5962         }
5963         
5964         return this.getChildContainer();
5965     },
5966     
5967     getChildContainer : function()
5968     {
5969          
5970         return this.el.select('.roo-navbar-collapse',true).first();
5971          
5972         
5973     },
5974     
5975     initEvents : function()
5976     {
5977         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5978         
5979         if (this.autohide) {
5980             
5981             var prevScroll = 0;
5982             var ft = this.el;
5983             
5984             Roo.get(document).on('scroll',function(e) {
5985                 var ns = Roo.get(document).getScroll().top;
5986                 var os = prevScroll;
5987                 prevScroll = ns;
5988                 
5989                 if(ns > os){
5990                     ft.removeClass('slideDown');
5991                     ft.addClass('slideUp');
5992                     return;
5993                 }
5994                 ft.removeClass('slideUp');
5995                 ft.addClass('slideDown');
5996                  
5997               
5998           },this);
5999         }
6000     }    
6001     
6002 });
6003
6004
6005
6006  
6007
6008  /*
6009  * - LGPL
6010  *
6011  * navbar
6012  * 
6013  */
6014
6015 /**
6016  * @class Roo.bootstrap.NavSidebar
6017  * @extends Roo.bootstrap.Navbar
6018  * Bootstrap Sidebar class
6019  * 
6020  * @constructor
6021  * Create a new Sidebar
6022  * @param {Object} config The config object
6023  */
6024
6025
6026 Roo.bootstrap.NavSidebar = function(config){
6027     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6028 };
6029
6030 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6031     
6032     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6033     
6034     getAutoCreate : function(){
6035         
6036         
6037         return  {
6038             tag: 'div',
6039             cls: 'sidebar sidebar-nav'
6040         };
6041     
6042         
6043     }
6044     
6045     
6046     
6047 });
6048
6049
6050
6051  
6052
6053  /*
6054  * - LGPL
6055  *
6056  * nav group
6057  * 
6058  */
6059
6060 /**
6061  * @class Roo.bootstrap.NavGroup
6062  * @extends Roo.bootstrap.Component
6063  * Bootstrap NavGroup class
6064  * @cfg {String} align (left|right)
6065  * @cfg {Boolean} inverse
6066  * @cfg {String} type (nav|pills|tab) default nav
6067  * @cfg {String} navId - reference Id for navbar.
6068  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6069  * 
6070  * @constructor
6071  * Create a new nav group
6072  * @param {Object} config The config object
6073  */
6074
6075 Roo.bootstrap.NavGroup = function(config){
6076     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6077     this.navItems = [];
6078    
6079     Roo.bootstrap.NavGroup.register(this);
6080      this.addEvents({
6081         /**
6082              * @event changed
6083              * Fires when the active item changes
6084              * @param {Roo.bootstrap.NavGroup} this
6085              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6086              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6087          */
6088         'changed': true
6089      });
6090     
6091 };
6092
6093 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6094     
6095     align: '',
6096     inverse: false,
6097     form: false,
6098     type: 'nav',
6099     navId : '',
6100     // private
6101     pilltype : true,
6102     
6103     navItems : false, 
6104     
6105     getAutoCreate : function()
6106     {
6107         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6108         
6109         cfg = {
6110             tag : 'ul',
6111             cls: 'nav' 
6112         };
6113         if (Roo.bootstrap.version == 4) {
6114             if (['tabs','pills'].indexOf(this.type) != -1) {
6115                 cfg.cls += ' nav-' + this.type; 
6116             } else {
6117                 // trying to remove so header bar can right align top?
6118                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6119                     // do not use on header bar... 
6120                     cfg.cls += ' navbar-nav';
6121                 }
6122             }
6123             
6124         } else {
6125             if (['tabs','pills'].indexOf(this.type) != -1) {
6126                 cfg.cls += ' nav-' + this.type
6127             } else {
6128                 if (this.type !== 'nav') {
6129                     Roo.log('nav type must be nav/tabs/pills')
6130                 }
6131                 cfg.cls += ' navbar-nav'
6132             }
6133         }
6134         
6135         if (this.parent() && this.parent().sidebar) {
6136             cfg = {
6137                 tag: 'ul',
6138                 cls: 'dashboard-menu sidebar-menu'
6139             };
6140             
6141             return cfg;
6142         }
6143         
6144         if (this.form === true) {
6145             cfg = {
6146                 tag: 'form',
6147                 cls: 'navbar-form form-inline'
6148             };
6149             //nav navbar-right ml-md-auto
6150             if (this.align === 'right') {
6151                 cfg.cls += ' navbar-right ml-md-auto';
6152             } else {
6153                 cfg.cls += ' navbar-left';
6154             }
6155         }
6156         
6157         if (this.align === 'right') {
6158             cfg.cls += ' navbar-right ml-md-auto';
6159         } else {
6160             cfg.cls += ' mr-auto';
6161         }
6162         
6163         if (this.inverse) {
6164             cfg.cls += ' navbar-inverse';
6165             
6166         }
6167         
6168         
6169         return cfg;
6170     },
6171     /**
6172     * sets the active Navigation item
6173     * @param {Roo.bootstrap.NavItem} the new current navitem
6174     */
6175     setActiveItem : function(item)
6176     {
6177         var prev = false;
6178         Roo.each(this.navItems, function(v){
6179             if (v == item) {
6180                 return ;
6181             }
6182             if (v.isActive()) {
6183                 v.setActive(false, true);
6184                 prev = v;
6185                 
6186             }
6187             
6188         });
6189
6190         item.setActive(true, true);
6191         this.fireEvent('changed', this, item, prev);
6192         
6193         
6194     },
6195     /**
6196     * gets the active Navigation item
6197     * @return {Roo.bootstrap.NavItem} the current navitem
6198     */
6199     getActive : function()
6200     {
6201         
6202         var prev = false;
6203         Roo.each(this.navItems, function(v){
6204             
6205             if (v.isActive()) {
6206                 prev = v;
6207                 
6208             }
6209             
6210         });
6211         return prev;
6212     },
6213     
6214     indexOfNav : function()
6215     {
6216         
6217         var prev = false;
6218         Roo.each(this.navItems, function(v,i){
6219             
6220             if (v.isActive()) {
6221                 prev = i;
6222                 
6223             }
6224             
6225         });
6226         return prev;
6227     },
6228     /**
6229     * adds a Navigation item
6230     * @param {Roo.bootstrap.NavItem} the navitem to add
6231     */
6232     addItem : function(cfg)
6233     {
6234         if (this.form && Roo.bootstrap.version == 4) {
6235             cfg.tag = 'div';
6236         }
6237         var cn = new Roo.bootstrap.NavItem(cfg);
6238         this.register(cn);
6239         cn.parentId = this.id;
6240         cn.onRender(this.el, null);
6241         return cn;
6242     },
6243     /**
6244     * register a Navigation item
6245     * @param {Roo.bootstrap.NavItem} the navitem to add
6246     */
6247     register : function(item)
6248     {
6249         this.navItems.push( item);
6250         item.navId = this.navId;
6251     
6252     },
6253     
6254     /**
6255     * clear all the Navigation item
6256     */
6257    
6258     clearAll : function()
6259     {
6260         this.navItems = [];
6261         this.el.dom.innerHTML = '';
6262     },
6263     
6264     getNavItem: function(tabId)
6265     {
6266         var ret = false;
6267         Roo.each(this.navItems, function(e) {
6268             if (e.tabId == tabId) {
6269                ret =  e;
6270                return false;
6271             }
6272             return true;
6273             
6274         });
6275         return ret;
6276     },
6277     
6278     setActiveNext : function()
6279     {
6280         var i = this.indexOfNav(this.getActive());
6281         if (i > this.navItems.length) {
6282             return;
6283         }
6284         this.setActiveItem(this.navItems[i+1]);
6285     },
6286     setActivePrev : function()
6287     {
6288         var i = this.indexOfNav(this.getActive());
6289         if (i  < 1) {
6290             return;
6291         }
6292         this.setActiveItem(this.navItems[i-1]);
6293     },
6294     clearWasActive : function(except) {
6295         Roo.each(this.navItems, function(e) {
6296             if (e.tabId != except.tabId && e.was_active) {
6297                e.was_active = false;
6298                return false;
6299             }
6300             return true;
6301             
6302         });
6303     },
6304     getWasActive : function ()
6305     {
6306         var r = false;
6307         Roo.each(this.navItems, function(e) {
6308             if (e.was_active) {
6309                r = e;
6310                return false;
6311             }
6312             return true;
6313             
6314         });
6315         return r;
6316     }
6317     
6318     
6319 });
6320
6321  
6322 Roo.apply(Roo.bootstrap.NavGroup, {
6323     
6324     groups: {},
6325      /**
6326     * register a Navigation Group
6327     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6328     */
6329     register : function(navgrp)
6330     {
6331         this.groups[navgrp.navId] = navgrp;
6332         
6333     },
6334     /**
6335     * fetch a Navigation Group based on the navigation ID
6336     * @param {string} the navgroup to add
6337     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6338     */
6339     get: function(navId) {
6340         if (typeof(this.groups[navId]) == 'undefined') {
6341             return false;
6342             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6343         }
6344         return this.groups[navId] ;
6345     }
6346     
6347     
6348     
6349 });
6350
6351  /*
6352  * - LGPL
6353  *
6354  * row
6355  * 
6356  */
6357
6358 /**
6359  * @class Roo.bootstrap.NavItem
6360  * @extends Roo.bootstrap.Component
6361  * Bootstrap Navbar.NavItem class
6362  * @cfg {String} href  link to
6363  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6364  * @cfg {Boolean} button_outline show and outlined button
6365  * @cfg {String} html content of button
6366  * @cfg {String} badge text inside badge
6367  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6368  * @cfg {String} glyphicon DEPRICATED - use fa
6369  * @cfg {String} icon DEPRICATED - use fa
6370  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6371  * @cfg {Boolean} active Is item active
6372  * @cfg {Boolean} disabled Is item disabled
6373  * @cfg {String} linkcls  Link Class
6374  * @cfg {Boolean} preventDefault (true | false) default false
6375  * @cfg {String} tabId the tab that this item activates.
6376  * @cfg {String} tagtype (a|span) render as a href or span?
6377  * @cfg {Boolean} animateRef (true|false) link to element default false  
6378   
6379  * @constructor
6380  * Create a new Navbar Item
6381  * @param {Object} config The config object
6382  */
6383 Roo.bootstrap.NavItem = function(config){
6384     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6385     this.addEvents({
6386         // raw events
6387         /**
6388          * @event click
6389          * The raw click event for the entire grid.
6390          * @param {Roo.EventObject} e
6391          */
6392         "click" : true,
6393          /**
6394             * @event changed
6395             * Fires when the active item active state changes
6396             * @param {Roo.bootstrap.NavItem} this
6397             * @param {boolean} state the new state
6398              
6399          */
6400         'changed': true,
6401         /**
6402             * @event scrollto
6403             * Fires when scroll to element
6404             * @param {Roo.bootstrap.NavItem} this
6405             * @param {Object} options
6406             * @param {Roo.EventObject} e
6407              
6408          */
6409         'scrollto': true
6410     });
6411    
6412 };
6413
6414 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6415     
6416     href: false,
6417     html: '',
6418     badge: '',
6419     icon: false,
6420     fa : false,
6421     glyphicon: false,
6422     active: false,
6423     preventDefault : false,
6424     tabId : false,
6425     tagtype : 'a',
6426     tag: 'li',
6427     disabled : false,
6428     animateRef : false,
6429     was_active : false,
6430     button_weight : '',
6431     button_outline : false,
6432     linkcls : '',
6433     navLink: false,
6434     
6435     getAutoCreate : function(){
6436          
6437         var cfg = {
6438             tag: this.tag,
6439             cls: 'nav-item'
6440         };
6441         
6442         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6443         
6444         if (this.active) {
6445             cfg.cls +=  ' active' ;
6446         }
6447         if (this.disabled) {
6448             cfg.cls += ' disabled';
6449         }
6450         
6451         // BS4 only?
6452         if (this.button_weight.length) {
6453             cfg.tag = this.href ? 'a' : 'button';
6454             cfg.html = this.html || '';
6455             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6456             if (this.href) {
6457                 cfg.href = this.href;
6458             }
6459             if (this.fa) {
6460                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6461             } else {
6462                 cfg.cls += " nav-html";
6463             }
6464             
6465             // menu .. should add dropdown-menu class - so no need for carat..
6466             
6467             if (this.badge !== '') {
6468                  
6469                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6470             }
6471             return cfg;
6472         }
6473         
6474         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6475             cfg.cn = [
6476                 {
6477                     tag: this.tagtype,
6478                     href : this.href || "#",
6479                     html: this.html || '',
6480                     cls : ''
6481                 }
6482             ];
6483             if (this.tagtype == 'a') {
6484                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6485         
6486             }
6487             if (this.icon) {
6488                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6489             } else  if (this.fa) {
6490                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6491             } else if(this.glyphicon) {
6492                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6493             } else {
6494                 cfg.cn[0].cls += " nav-html";
6495             }
6496             
6497             if (this.menu) {
6498                 cfg.cn[0].html += " <span class='caret'></span>";
6499              
6500             }
6501             
6502             if (this.badge !== '') {
6503                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6504             }
6505         }
6506         
6507         
6508         
6509         return cfg;
6510     },
6511     onRender : function(ct, position)
6512     {
6513        // Roo.log("Call onRender: " + this.xtype);
6514         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6515             this.tag = 'div';
6516         }
6517         
6518         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6519         this.navLink = this.el.select('.nav-link',true).first();
6520         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6521         return ret;
6522     },
6523       
6524     
6525     initEvents: function() 
6526     {
6527         if (typeof (this.menu) != 'undefined') {
6528             this.menu.parentType = this.xtype;
6529             this.menu.triggerEl = this.el;
6530             this.menu = this.addxtype(Roo.apply({}, this.menu));
6531         }
6532         
6533         this.el.on('click', this.onClick, this);
6534         
6535         //if(this.tagtype == 'span'){
6536         //    this.el.select('span',true).on('click', this.onClick, this);
6537         //}
6538        
6539         // at this point parent should be available..
6540         this.parent().register(this);
6541     },
6542     
6543     onClick : function(e)
6544     {
6545         if (e.getTarget('.dropdown-menu-item')) {
6546             // did you click on a menu itemm.... - then don't trigger onclick..
6547             return;
6548         }
6549         
6550         if(
6551                 this.preventDefault || 
6552                 this.href == '#' 
6553         ){
6554             Roo.log("NavItem - prevent Default?");
6555             e.preventDefault();
6556         }
6557         
6558         if (this.disabled) {
6559             return;
6560         }
6561         
6562         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6563         if (tg && tg.transition) {
6564             Roo.log("waiting for the transitionend");
6565             return;
6566         }
6567         
6568         
6569         
6570         //Roo.log("fire event clicked");
6571         if(this.fireEvent('click', this, e) === false){
6572             return;
6573         };
6574         
6575         if(this.tagtype == 'span'){
6576             return;
6577         }
6578         
6579         //Roo.log(this.href);
6580         var ael = this.el.select('a',true).first();
6581         //Roo.log(ael);
6582         
6583         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6584             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6585             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6586                 return; // ignore... - it's a 'hash' to another page.
6587             }
6588             Roo.log("NavItem - prevent Default?");
6589             e.preventDefault();
6590             this.scrollToElement(e);
6591         }
6592         
6593         
6594         var p =  this.parent();
6595    
6596         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6597             if (typeof(p.setActiveItem) !== 'undefined') {
6598                 p.setActiveItem(this);
6599             }
6600         }
6601         
6602         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6603         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6604             // remove the collapsed menu expand...
6605             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6606         }
6607     },
6608     
6609     isActive: function () {
6610         return this.active
6611     },
6612     setActive : function(state, fire, is_was_active)
6613     {
6614         if (this.active && !state && this.navId) {
6615             this.was_active = true;
6616             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6617             if (nv) {
6618                 nv.clearWasActive(this);
6619             }
6620             
6621         }
6622         this.active = state;
6623         
6624         if (!state ) {
6625             this.el.removeClass('active');
6626             this.navLink ? this.navLink.removeClass('active') : false;
6627         } else if (!this.el.hasClass('active')) {
6628             
6629             this.el.addClass('active');
6630             if (Roo.bootstrap.version == 4 && this.navLink ) {
6631                 this.navLink.addClass('active');
6632             }
6633             
6634         }
6635         if (fire) {
6636             this.fireEvent('changed', this, state);
6637         }
6638         
6639         // show a panel if it's registered and related..
6640         
6641         if (!this.navId || !this.tabId || !state || is_was_active) {
6642             return;
6643         }
6644         
6645         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6646         if (!tg) {
6647             return;
6648         }
6649         var pan = tg.getPanelByName(this.tabId);
6650         if (!pan) {
6651             return;
6652         }
6653         // if we can not flip to new panel - go back to old nav highlight..
6654         if (false == tg.showPanel(pan)) {
6655             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6656             if (nv) {
6657                 var onav = nv.getWasActive();
6658                 if (onav) {
6659                     onav.setActive(true, false, true);
6660                 }
6661             }
6662             
6663         }
6664         
6665         
6666         
6667     },
6668      // this should not be here...
6669     setDisabled : function(state)
6670     {
6671         this.disabled = state;
6672         if (!state ) {
6673             this.el.removeClass('disabled');
6674         } else if (!this.el.hasClass('disabled')) {
6675             this.el.addClass('disabled');
6676         }
6677         
6678     },
6679     
6680     /**
6681      * Fetch the element to display the tooltip on.
6682      * @return {Roo.Element} defaults to this.el
6683      */
6684     tooltipEl : function()
6685     {
6686         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6687     },
6688     
6689     scrollToElement : function(e)
6690     {
6691         var c = document.body;
6692         
6693         /*
6694          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6695          */
6696         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6697             c = document.documentElement;
6698         }
6699         
6700         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6701         
6702         if(!target){
6703             return;
6704         }
6705
6706         var o = target.calcOffsetsTo(c);
6707         
6708         var options = {
6709             target : target,
6710             value : o[1]
6711         };
6712         
6713         this.fireEvent('scrollto', this, options, e);
6714         
6715         Roo.get(c).scrollTo('top', options.value, true);
6716         
6717         return;
6718     },
6719     /**
6720      * Set the HTML (text content) of the item
6721      * @param {string} html  content for the nav item
6722      */
6723     setHtml : function(html)
6724     {
6725         this.html = html;
6726         this.htmlEl.dom.innerHTML = html;
6727         
6728     } 
6729 });
6730  
6731
6732  /*
6733  * - LGPL
6734  *
6735  * sidebar item
6736  *
6737  *  li
6738  *    <span> icon </span>
6739  *    <span> text </span>
6740  *    <span>badge </span>
6741  */
6742
6743 /**
6744  * @class Roo.bootstrap.NavSidebarItem
6745  * @extends Roo.bootstrap.NavItem
6746  * Bootstrap Navbar.NavSidebarItem class
6747  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6748  * {Boolean} open is the menu open
6749  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6750  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6751  * {String} buttonSize (sm|md|lg)the extra classes for the button
6752  * {Boolean} showArrow show arrow next to the text (default true)
6753  * @constructor
6754  * Create a new Navbar Button
6755  * @param {Object} config The config object
6756  */
6757 Roo.bootstrap.NavSidebarItem = function(config){
6758     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6759     this.addEvents({
6760         // raw events
6761         /**
6762          * @event click
6763          * The raw click event for the entire grid.
6764          * @param {Roo.EventObject} e
6765          */
6766         "click" : true,
6767          /**
6768             * @event changed
6769             * Fires when the active item active state changes
6770             * @param {Roo.bootstrap.NavSidebarItem} this
6771             * @param {boolean} state the new state
6772              
6773          */
6774         'changed': true
6775     });
6776    
6777 };
6778
6779 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6780     
6781     badgeWeight : 'default',
6782     
6783     open: false,
6784     
6785     buttonView : false,
6786     
6787     buttonWeight : 'default',
6788     
6789     buttonSize : 'md',
6790     
6791     showArrow : true,
6792     
6793     getAutoCreate : function(){
6794         
6795         
6796         var a = {
6797                 tag: 'a',
6798                 href : this.href || '#',
6799                 cls: '',
6800                 html : '',
6801                 cn : []
6802         };
6803         
6804         if(this.buttonView){
6805             a = {
6806                 tag: 'button',
6807                 href : this.href || '#',
6808                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6809                 html : this.html,
6810                 cn : []
6811             };
6812         }
6813         
6814         var cfg = {
6815             tag: 'li',
6816             cls: '',
6817             cn: [ a ]
6818         };
6819         
6820         if (this.active) {
6821             cfg.cls += ' active';
6822         }
6823         
6824         if (this.disabled) {
6825             cfg.cls += ' disabled';
6826         }
6827         if (this.open) {
6828             cfg.cls += ' open x-open';
6829         }
6830         // left icon..
6831         if (this.glyphicon || this.icon) {
6832             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6833             a.cn.push({ tag : 'i', cls : c }) ;
6834         }
6835         
6836         if(!this.buttonView){
6837             var span = {
6838                 tag: 'span',
6839                 html : this.html || ''
6840             };
6841
6842             a.cn.push(span);
6843             
6844         }
6845         
6846         if (this.badge !== '') {
6847             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6848         }
6849         
6850         if (this.menu) {
6851             
6852             if(this.showArrow){
6853                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6854             }
6855             
6856             a.cls += ' dropdown-toggle treeview' ;
6857         }
6858         
6859         return cfg;
6860     },
6861     
6862     initEvents : function()
6863     { 
6864         if (typeof (this.menu) != 'undefined') {
6865             this.menu.parentType = this.xtype;
6866             this.menu.triggerEl = this.el;
6867             this.menu = this.addxtype(Roo.apply({}, this.menu));
6868         }
6869         
6870         this.el.on('click', this.onClick, this);
6871         
6872         if(this.badge !== ''){
6873             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6874         }
6875         
6876     },
6877     
6878     onClick : function(e)
6879     {
6880         if(this.disabled){
6881             e.preventDefault();
6882             return;
6883         }
6884         
6885         if(this.preventDefault){
6886             e.preventDefault();
6887         }
6888         
6889         this.fireEvent('click', this, e);
6890     },
6891     
6892     disable : function()
6893     {
6894         this.setDisabled(true);
6895     },
6896     
6897     enable : function()
6898     {
6899         this.setDisabled(false);
6900     },
6901     
6902     setDisabled : function(state)
6903     {
6904         if(this.disabled == state){
6905             return;
6906         }
6907         
6908         this.disabled = state;
6909         
6910         if (state) {
6911             this.el.addClass('disabled');
6912             return;
6913         }
6914         
6915         this.el.removeClass('disabled');
6916         
6917         return;
6918     },
6919     
6920     setActive : function(state)
6921     {
6922         if(this.active == state){
6923             return;
6924         }
6925         
6926         this.active = state;
6927         
6928         if (state) {
6929             this.el.addClass('active');
6930             return;
6931         }
6932         
6933         this.el.removeClass('active');
6934         
6935         return;
6936     },
6937     
6938     isActive: function () 
6939     {
6940         return this.active;
6941     },
6942     
6943     setBadge : function(str)
6944     {
6945         if(!this.badgeEl){
6946             return;
6947         }
6948         
6949         this.badgeEl.dom.innerHTML = str;
6950     }
6951     
6952    
6953      
6954  
6955 });
6956  
6957
6958  /*
6959  * - LGPL
6960  *
6961  *  Breadcrumb Nav
6962  * 
6963  */
6964 Roo.namespace('Roo.bootstrap.breadcrumb');
6965
6966
6967 /**
6968  * @class Roo.bootstrap.breadcrumb.Nav
6969  * @extends Roo.bootstrap.Component
6970  * Bootstrap Breadcrumb Nav Class
6971  *  
6972  * @children Roo.bootstrap.breadcrumb.Item
6973  * 
6974  * @constructor
6975  * Create a new breadcrumb.Nav
6976  * @param {Object} config The config object
6977  */
6978
6979
6980 Roo.bootstrap.breadcrumb.Nav = function(config){
6981     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6982     
6983     
6984 };
6985
6986 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6987     
6988     getAutoCreate : function()
6989     {
6990
6991         var cfg = {
6992             tag: 'nav',
6993             cn : [
6994                 {
6995                     tag : 'ol',
6996                     cls : 'breadcrumb'
6997                 }
6998             ]
6999             
7000         };
7001           
7002         return cfg;
7003     },
7004     
7005     initEvents: function()
7006     {
7007         this.olEl = this.el.select('ol',true).first();    
7008     },
7009     getChildContainer : function()
7010     {
7011         return this.olEl;  
7012     }
7013     
7014 });
7015
7016  /*
7017  * - LGPL
7018  *
7019  *  Breadcrumb Item
7020  * 
7021  */
7022
7023
7024 /**
7025  * @class Roo.bootstrap.breadcrumb.Nav
7026  * @extends Roo.bootstrap.Component
7027  * Bootstrap Breadcrumb Nav Class
7028  *  
7029  * @children Roo.bootstrap.breadcrumb.Component
7030  * @cfg {String} html the content of the link.
7031  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7032  * @cfg {Boolean} active is it active
7033
7034  * 
7035  * @constructor
7036  * Create a new breadcrumb.Nav
7037  * @param {Object} config The config object
7038  */
7039
7040 Roo.bootstrap.breadcrumb.Item = function(config){
7041     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7042     this.addEvents({
7043         // img events
7044         /**
7045          * @event click
7046          * The img click event for the img.
7047          * @param {Roo.EventObject} e
7048          */
7049         "click" : true
7050     });
7051     
7052 };
7053
7054 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7055     
7056     href: false,
7057     html : '',
7058     
7059     getAutoCreate : function()
7060     {
7061
7062         var cfg = {
7063             tag: 'li',
7064             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7065         };
7066         if (this.href !== false) {
7067             cfg.cn = [{
7068                 tag : 'a',
7069                 href : this.href,
7070                 html : this.html
7071             }];
7072         } else {
7073             cfg.html = this.html;
7074         }
7075         
7076         return cfg;
7077     },
7078     
7079     initEvents: function()
7080     {
7081         if (this.href) {
7082             this.el.select('a', true).first().on('click',this.onClick, this)
7083         }
7084         
7085     },
7086     onClick : function(e)
7087     {
7088         e.preventDefault();
7089         this.fireEvent('click',this,  e);
7090     }
7091     
7092 });
7093
7094  /*
7095  * - LGPL
7096  *
7097  * row
7098  * 
7099  */
7100
7101 /**
7102  * @class Roo.bootstrap.Row
7103  * @extends Roo.bootstrap.Component
7104  * @children Roo.bootstrap.Component
7105  * Bootstrap Row class (contains columns...)
7106  * 
7107  * @constructor
7108  * Create a new Row
7109  * @param {Object} config The config object
7110  */
7111
7112 Roo.bootstrap.Row = function(config){
7113     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7114 };
7115
7116 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7117     
7118     getAutoCreate : function(){
7119        return {
7120             cls: 'row clearfix'
7121        };
7122     }
7123     
7124     
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * pagination
7133  * 
7134  */
7135
7136 /**
7137  * @class Roo.bootstrap.Pagination
7138  * @extends Roo.bootstrap.Component
7139  * Bootstrap Pagination class
7140  * @cfg {String} size xs | sm | md | lg
7141  * @cfg {Boolean} inverse false | true
7142  * 
7143  * @constructor
7144  * Create a new Pagination
7145  * @param {Object} config The config object
7146  */
7147
7148 Roo.bootstrap.Pagination = function(config){
7149     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7150 };
7151
7152 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7153     
7154     cls: false,
7155     size: false,
7156     inverse: false,
7157     
7158     getAutoCreate : function(){
7159         var cfg = {
7160             tag: 'ul',
7161                 cls: 'pagination'
7162         };
7163         if (this.inverse) {
7164             cfg.cls += ' inverse';
7165         }
7166         if (this.html) {
7167             cfg.html=this.html;
7168         }
7169         if (this.cls) {
7170             cfg.cls += " " + this.cls;
7171         }
7172         return cfg;
7173     }
7174    
7175 });
7176
7177  
7178
7179  /*
7180  * - LGPL
7181  *
7182  * Pagination item
7183  * 
7184  */
7185
7186
7187 /**
7188  * @class Roo.bootstrap.PaginationItem
7189  * @extends Roo.bootstrap.Component
7190  * Bootstrap PaginationItem class
7191  * @cfg {String} html text
7192  * @cfg {String} href the link
7193  * @cfg {Boolean} preventDefault (true | false) default true
7194  * @cfg {Boolean} active (true | false) default false
7195  * @cfg {Boolean} disabled default false
7196  * 
7197  * 
7198  * @constructor
7199  * Create a new PaginationItem
7200  * @param {Object} config The config object
7201  */
7202
7203
7204 Roo.bootstrap.PaginationItem = function(config){
7205     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7206     this.addEvents({
7207         // raw events
7208         /**
7209          * @event click
7210          * The raw click event for the entire grid.
7211          * @param {Roo.EventObject} e
7212          */
7213         "click" : true
7214     });
7215 };
7216
7217 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7218     
7219     href : false,
7220     html : false,
7221     preventDefault: true,
7222     active : false,
7223     cls : false,
7224     disabled: false,
7225     
7226     getAutoCreate : function(){
7227         var cfg= {
7228             tag: 'li',
7229             cn: [
7230                 {
7231                     tag : 'a',
7232                     href : this.href ? this.href : '#',
7233                     html : this.html ? this.html : ''
7234                 }
7235             ]
7236         };
7237         
7238         if(this.cls){
7239             cfg.cls = this.cls;
7240         }
7241         
7242         if(this.disabled){
7243             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7244         }
7245         
7246         if(this.active){
7247             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7248         }
7249         
7250         return cfg;
7251     },
7252     
7253     initEvents: function() {
7254         
7255         this.el.on('click', this.onClick, this);
7256         
7257     },
7258     onClick : function(e)
7259     {
7260         Roo.log('PaginationItem on click ');
7261         if(this.preventDefault){
7262             e.preventDefault();
7263         }
7264         
7265         if(this.disabled){
7266             return;
7267         }
7268         
7269         this.fireEvent('click', this, e);
7270     }
7271    
7272 });
7273
7274  
7275
7276  /*
7277  * - LGPL
7278  *
7279  * slider
7280  * 
7281  */
7282
7283
7284 /**
7285  * @class Roo.bootstrap.Slider
7286  * @extends Roo.bootstrap.Component
7287  * Bootstrap Slider class
7288  *    
7289  * @constructor
7290  * Create a new Slider
7291  * @param {Object} config The config object
7292  */
7293
7294 Roo.bootstrap.Slider = function(config){
7295     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7296 };
7297
7298 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7299     
7300     getAutoCreate : function(){
7301         
7302         var cfg = {
7303             tag: 'div',
7304             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7305             cn: [
7306                 {
7307                     tag: 'a',
7308                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7309                 }
7310             ]
7311         };
7312         
7313         return cfg;
7314     }
7315    
7316 });
7317
7318  /*
7319  * Based on:
7320  * Ext JS Library 1.1.1
7321  * Copyright(c) 2006-2007, Ext JS, LLC.
7322  *
7323  * Originally Released Under LGPL - original licence link has changed is not relivant.
7324  *
7325  * Fork - LGPL
7326  * <script type="text/javascript">
7327  */
7328  /**
7329  * @extends Roo.dd.DDProxy
7330  * @class Roo.grid.SplitDragZone
7331  * Support for Column Header resizing
7332  * @constructor
7333  * @param {Object} config
7334  */
7335 // private
7336 // This is a support class used internally by the Grid components
7337 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7338     this.grid = grid;
7339     this.view = grid.getView();
7340     this.proxy = this.view.resizeProxy;
7341     Roo.grid.SplitDragZone.superclass.constructor.call(
7342         this,
7343         hd, // ID
7344         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7345         {  // CONFIG
7346             dragElId : Roo.id(this.proxy.dom),
7347             resizeFrame:false
7348         }
7349     );
7350     
7351     this.setHandleElId(Roo.id(hd));
7352     if (hd2 !== false) {
7353         this.setOuterHandleElId(Roo.id(hd2));
7354     }
7355     
7356     this.scroll = false;
7357 };
7358 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7359     fly: Roo.Element.fly,
7360
7361     b4StartDrag : function(x, y){
7362         this.view.headersDisabled = true;
7363         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7364                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7365         );
7366         this.proxy.setHeight(h);
7367         
7368         // for old system colWidth really stored the actual width?
7369         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7370         // which in reality did not work.. - it worked only for fixed sizes
7371         // for resizable we need to use actual sizes.
7372         var w = this.cm.getColumnWidth(this.cellIndex);
7373         if (!this.view.mainWrap) {
7374             // bootstrap.
7375             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7376         }
7377         
7378         
7379         
7380         // this was w-this.grid.minColumnWidth;
7381         // doesnt really make sense? - w = thie curren width or the rendered one?
7382         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7383         this.resetConstraints();
7384         this.setXConstraint(minw, 1000);
7385         this.setYConstraint(0, 0);
7386         this.minX = x - minw;
7387         this.maxX = x + 1000;
7388         this.startPos = x;
7389         if (!this.view.mainWrap) { // this is Bootstrap code..
7390             this.getDragEl().style.display='block';
7391         }
7392         
7393         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7394     },
7395
7396
7397     handleMouseDown : function(e){
7398         ev = Roo.EventObject.setEvent(e);
7399         var t = this.fly(ev.getTarget());
7400         if(t.hasClass("x-grid-split")){
7401             this.cellIndex = this.view.getCellIndex(t.dom);
7402             this.split = t.dom;
7403             this.cm = this.grid.colModel;
7404             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7405                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7406             }
7407         }
7408     },
7409
7410     endDrag : function(e){
7411         this.view.headersDisabled = false;
7412         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7413         var diff = endX - this.startPos;
7414         // 
7415         var w = this.cm.getColumnWidth(this.cellIndex);
7416         if (!this.view.mainWrap) {
7417             w = 0;
7418         }
7419         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7420     },
7421
7422     autoOffset : function(){
7423         this.setDelta(0,0);
7424     }
7425 });/*
7426  * Based on:
7427  * Ext JS Library 1.1.1
7428  * Copyright(c) 2006-2007, Ext JS, LLC.
7429  *
7430  * Originally Released Under LGPL - original licence link has changed is not relivant.
7431  *
7432  * Fork - LGPL
7433  * <script type="text/javascript">
7434  */
7435
7436 /**
7437  * @class Roo.grid.AbstractSelectionModel
7438  * @extends Roo.util.Observable
7439  * @abstract
7440  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7441  * implemented by descendant classes.  This class should not be directly instantiated.
7442  * @constructor
7443  */
7444 Roo.grid.AbstractSelectionModel = function(){
7445     this.locked = false;
7446     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7447 };
7448
7449 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7450     /** @ignore Called by the grid automatically. Do not call directly. */
7451     init : function(grid){
7452         this.grid = grid;
7453         this.initEvents();
7454     },
7455
7456     /**
7457      * Locks the selections.
7458      */
7459     lock : function(){
7460         this.locked = true;
7461     },
7462
7463     /**
7464      * Unlocks the selections.
7465      */
7466     unlock : function(){
7467         this.locked = false;
7468     },
7469
7470     /**
7471      * Returns true if the selections are locked.
7472      * @return {Boolean}
7473      */
7474     isLocked : function(){
7475         return this.locked;
7476     }
7477 });/*
7478  * Based on:
7479  * Ext JS Library 1.1.1
7480  * Copyright(c) 2006-2007, Ext JS, LLC.
7481  *
7482  * Originally Released Under LGPL - original licence link has changed is not relivant.
7483  *
7484  * Fork - LGPL
7485  * <script type="text/javascript">
7486  */
7487 /**
7488  * @extends Roo.grid.AbstractSelectionModel
7489  * @class Roo.grid.RowSelectionModel
7490  * The default SelectionModel used by {@link Roo.grid.Grid}.
7491  * It supports multiple selections and keyboard selection/navigation. 
7492  * @constructor
7493  * @param {Object} config
7494  */
7495 Roo.grid.RowSelectionModel = function(config){
7496     Roo.apply(this, config);
7497     this.selections = new Roo.util.MixedCollection(false, function(o){
7498         return o.id;
7499     });
7500
7501     this.last = false;
7502     this.lastActive = false;
7503
7504     this.addEvents({
7505         /**
7506         * @event selectionchange
7507         * Fires when the selection changes
7508         * @param {SelectionModel} this
7509         */
7510        "selectionchange" : true,
7511        /**
7512         * @event afterselectionchange
7513         * Fires after the selection changes (eg. by key press or clicking)
7514         * @param {SelectionModel} this
7515         */
7516        "afterselectionchange" : true,
7517        /**
7518         * @event beforerowselect
7519         * Fires when a row is selected being selected, return false to cancel.
7520         * @param {SelectionModel} this
7521         * @param {Number} rowIndex The selected index
7522         * @param {Boolean} keepExisting False if other selections will be cleared
7523         */
7524        "beforerowselect" : true,
7525        /**
7526         * @event rowselect
7527         * Fires when a row is selected.
7528         * @param {SelectionModel} this
7529         * @param {Number} rowIndex The selected index
7530         * @param {Roo.data.Record} r The record
7531         */
7532        "rowselect" : true,
7533        /**
7534         * @event rowdeselect
7535         * Fires when a row is deselected.
7536         * @param {SelectionModel} this
7537         * @param {Number} rowIndex The selected index
7538         */
7539         "rowdeselect" : true
7540     });
7541     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7542     this.locked = false;
7543 };
7544
7545 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7546     /**
7547      * @cfg {Boolean} singleSelect
7548      * True to allow selection of only one row at a time (defaults to false)
7549      */
7550     singleSelect : false,
7551
7552     // private
7553     initEvents : function(){
7554
7555         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7556             this.grid.on("mousedown", this.handleMouseDown, this);
7557         }else{ // allow click to work like normal
7558             this.grid.on("rowclick", this.handleDragableRowClick, this);
7559         }
7560         // bootstrap does not have a view..
7561         var view = this.grid.view ? this.grid.view : this.grid;
7562         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7563             "up" : function(e){
7564                 if(!e.shiftKey){
7565                     this.selectPrevious(e.shiftKey);
7566                 }else if(this.last !== false && this.lastActive !== false){
7567                     var last = this.last;
7568                     this.selectRange(this.last,  this.lastActive-1);
7569                     view.focusRow(this.lastActive);
7570                     if(last !== false){
7571                         this.last = last;
7572                     }
7573                 }else{
7574                     this.selectFirstRow();
7575                 }
7576                 this.fireEvent("afterselectionchange", this);
7577             },
7578             "down" : function(e){
7579                 if(!e.shiftKey){
7580                     this.selectNext(e.shiftKey);
7581                 }else if(this.last !== false && this.lastActive !== false){
7582                     var last = this.last;
7583                     this.selectRange(this.last,  this.lastActive+1);
7584                     view.focusRow(this.lastActive);
7585                     if(last !== false){
7586                         this.last = last;
7587                     }
7588                 }else{
7589                     this.selectFirstRow();
7590                 }
7591                 this.fireEvent("afterselectionchange", this);
7592             },
7593             scope: this
7594         });
7595
7596          
7597         view.on("refresh", this.onRefresh, this);
7598         view.on("rowupdated", this.onRowUpdated, this);
7599         view.on("rowremoved", this.onRemove, this);
7600     },
7601
7602     // private
7603     onRefresh : function(){
7604         var ds = this.grid.ds, i, v = this.grid.view;
7605         var s = this.selections;
7606         s.each(function(r){
7607             if((i = ds.indexOfId(r.id)) != -1){
7608                 v.onRowSelect(i);
7609                 s.add(ds.getAt(i)); // updating the selection relate data
7610             }else{
7611                 s.remove(r);
7612             }
7613         });
7614     },
7615
7616     // private
7617     onRemove : function(v, index, r){
7618         this.selections.remove(r);
7619     },
7620
7621     // private
7622     onRowUpdated : function(v, index, r){
7623         if(this.isSelected(r)){
7624             v.onRowSelect(index);
7625         }
7626     },
7627
7628     /**
7629      * Select records.
7630      * @param {Array} records The records to select
7631      * @param {Boolean} keepExisting (optional) True to keep existing selections
7632      */
7633     selectRecords : function(records, keepExisting){
7634         if(!keepExisting){
7635             this.clearSelections();
7636         }
7637         var ds = this.grid.ds;
7638         for(var i = 0, len = records.length; i < len; i++){
7639             this.selectRow(ds.indexOf(records[i]), true);
7640         }
7641     },
7642
7643     /**
7644      * Gets the number of selected rows.
7645      * @return {Number}
7646      */
7647     getCount : function(){
7648         return this.selections.length;
7649     },
7650
7651     /**
7652      * Selects the first row in the grid.
7653      */
7654     selectFirstRow : function(){
7655         this.selectRow(0);
7656     },
7657
7658     /**
7659      * Select the last row.
7660      * @param {Boolean} keepExisting (optional) True to keep existing selections
7661      */
7662     selectLastRow : function(keepExisting){
7663         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7664     },
7665
7666     /**
7667      * Selects the row immediately following the last selected row.
7668      * @param {Boolean} keepExisting (optional) True to keep existing selections
7669      */
7670     selectNext : function(keepExisting){
7671         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7672             this.selectRow(this.last+1, keepExisting);
7673             var view = this.grid.view ? this.grid.view : this.grid;
7674             view.focusRow(this.last);
7675         }
7676     },
7677
7678     /**
7679      * Selects the row that precedes the last selected row.
7680      * @param {Boolean} keepExisting (optional) True to keep existing selections
7681      */
7682     selectPrevious : function(keepExisting){
7683         if(this.last){
7684             this.selectRow(this.last-1, keepExisting);
7685             var view = this.grid.view ? this.grid.view : this.grid;
7686             view.focusRow(this.last);
7687         }
7688     },
7689
7690     /**
7691      * Returns the selected records
7692      * @return {Array} Array of selected records
7693      */
7694     getSelections : function(){
7695         return [].concat(this.selections.items);
7696     },
7697
7698     /**
7699      * Returns the first selected record.
7700      * @return {Record}
7701      */
7702     getSelected : function(){
7703         return this.selections.itemAt(0);
7704     },
7705
7706
7707     /**
7708      * Clears all selections.
7709      */
7710     clearSelections : function(fast){
7711         if(this.locked) {
7712             return;
7713         }
7714         if(fast !== true){
7715             var ds = this.grid.ds;
7716             var s = this.selections;
7717             s.each(function(r){
7718                 this.deselectRow(ds.indexOfId(r.id));
7719             }, this);
7720             s.clear();
7721         }else{
7722             this.selections.clear();
7723         }
7724         this.last = false;
7725     },
7726
7727
7728     /**
7729      * Selects all rows.
7730      */
7731     selectAll : function(){
7732         if(this.locked) {
7733             return;
7734         }
7735         this.selections.clear();
7736         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7737             this.selectRow(i, true);
7738         }
7739     },
7740
7741     /**
7742      * Returns True if there is a selection.
7743      * @return {Boolean}
7744      */
7745     hasSelection : function(){
7746         return this.selections.length > 0;
7747     },
7748
7749     /**
7750      * Returns True if the specified row is selected.
7751      * @param {Number/Record} record The record or index of the record to check
7752      * @return {Boolean}
7753      */
7754     isSelected : function(index){
7755         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7756         return (r && this.selections.key(r.id) ? true : false);
7757     },
7758
7759     /**
7760      * Returns True if the specified record id is selected.
7761      * @param {String} id The id of record to check
7762      * @return {Boolean}
7763      */
7764     isIdSelected : function(id){
7765         return (this.selections.key(id) ? true : false);
7766     },
7767
7768     // private
7769     handleMouseDown : function(e, t)
7770     {
7771         var view = this.grid.view ? this.grid.view : this.grid;
7772         var rowIndex;
7773         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7774             return;
7775         };
7776         if(e.shiftKey && this.last !== false){
7777             var last = this.last;
7778             this.selectRange(last, rowIndex, e.ctrlKey);
7779             this.last = last; // reset the last
7780             view.focusRow(rowIndex);
7781         }else{
7782             var isSelected = this.isSelected(rowIndex);
7783             if(e.button !== 0 && isSelected){
7784                 view.focusRow(rowIndex);
7785             }else if(e.ctrlKey && isSelected){
7786                 this.deselectRow(rowIndex);
7787             }else if(!isSelected){
7788                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7789                 view.focusRow(rowIndex);
7790             }
7791         }
7792         this.fireEvent("afterselectionchange", this);
7793     },
7794     // private
7795     handleDragableRowClick :  function(grid, rowIndex, e) 
7796     {
7797         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7798             this.selectRow(rowIndex, false);
7799             var view = this.grid.view ? this.grid.view : this.grid;
7800             view.focusRow(rowIndex);
7801              this.fireEvent("afterselectionchange", this);
7802         }
7803     },
7804     
7805     /**
7806      * Selects multiple rows.
7807      * @param {Array} rows Array of the indexes of the row to select
7808      * @param {Boolean} keepExisting (optional) True to keep existing selections
7809      */
7810     selectRows : function(rows, keepExisting){
7811         if(!keepExisting){
7812             this.clearSelections();
7813         }
7814         for(var i = 0, len = rows.length; i < len; i++){
7815             this.selectRow(rows[i], true);
7816         }
7817     },
7818
7819     /**
7820      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7821      * @param {Number} startRow The index of the first row in the range
7822      * @param {Number} endRow The index of the last row in the range
7823      * @param {Boolean} keepExisting (optional) True to retain existing selections
7824      */
7825     selectRange : function(startRow, endRow, keepExisting){
7826         if(this.locked) {
7827             return;
7828         }
7829         if(!keepExisting){
7830             this.clearSelections();
7831         }
7832         if(startRow <= endRow){
7833             for(var i = startRow; i <= endRow; i++){
7834                 this.selectRow(i, true);
7835             }
7836         }else{
7837             for(var i = startRow; i >= endRow; i--){
7838                 this.selectRow(i, true);
7839             }
7840         }
7841     },
7842
7843     /**
7844      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7845      * @param {Number} startRow The index of the first row in the range
7846      * @param {Number} endRow The index of the last row in the range
7847      */
7848     deselectRange : function(startRow, endRow, preventViewNotify){
7849         if(this.locked) {
7850             return;
7851         }
7852         for(var i = startRow; i <= endRow; i++){
7853             this.deselectRow(i, preventViewNotify);
7854         }
7855     },
7856
7857     /**
7858      * Selects a row.
7859      * @param {Number} row The index of the row to select
7860      * @param {Boolean} keepExisting (optional) True to keep existing selections
7861      */
7862     selectRow : function(index, keepExisting, preventViewNotify){
7863         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7864             return;
7865         }
7866         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7867             if(!keepExisting || this.singleSelect){
7868                 this.clearSelections();
7869             }
7870             var r = this.grid.ds.getAt(index);
7871             this.selections.add(r);
7872             this.last = this.lastActive = index;
7873             if(!preventViewNotify){
7874                 var view = this.grid.view ? this.grid.view : this.grid;
7875                 view.onRowSelect(index);
7876             }
7877             this.fireEvent("rowselect", this, index, r);
7878             this.fireEvent("selectionchange", this);
7879         }
7880     },
7881
7882     /**
7883      * Deselects a row.
7884      * @param {Number} row The index of the row to deselect
7885      */
7886     deselectRow : function(index, preventViewNotify){
7887         if(this.locked) {
7888             return;
7889         }
7890         if(this.last == index){
7891             this.last = false;
7892         }
7893         if(this.lastActive == index){
7894             this.lastActive = false;
7895         }
7896         var r = this.grid.ds.getAt(index);
7897         this.selections.remove(r);
7898         if(!preventViewNotify){
7899             var view = this.grid.view ? this.grid.view : this.grid;
7900             view.onRowDeselect(index);
7901         }
7902         this.fireEvent("rowdeselect", this, index);
7903         this.fireEvent("selectionchange", this);
7904     },
7905
7906     // private
7907     restoreLast : function(){
7908         if(this._last){
7909             this.last = this._last;
7910         }
7911     },
7912
7913     // private
7914     acceptsNav : function(row, col, cm){
7915         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7916     },
7917
7918     // private
7919     onEditorKey : function(field, e){
7920         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7921         if(k == e.TAB){
7922             e.stopEvent();
7923             ed.completeEdit();
7924             if(e.shiftKey){
7925                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7926             }else{
7927                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7928             }
7929         }else if(k == e.ENTER && !e.ctrlKey){
7930             e.stopEvent();
7931             ed.completeEdit();
7932             if(e.shiftKey){
7933                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7934             }else{
7935                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7936             }
7937         }else if(k == e.ESC){
7938             ed.cancelEdit();
7939         }
7940         if(newCell){
7941             g.startEditing(newCell[0], newCell[1]);
7942         }
7943     }
7944 });/*
7945  * Based on:
7946  * Ext JS Library 1.1.1
7947  * Copyright(c) 2006-2007, Ext JS, LLC.
7948  *
7949  * Originally Released Under LGPL - original licence link has changed is not relivant.
7950  *
7951  * Fork - LGPL
7952  * <script type="text/javascript">
7953  */
7954  
7955
7956 /**
7957  * @class Roo.grid.ColumnModel
7958  * @extends Roo.util.Observable
7959  * This is the default implementation of a ColumnModel used by the Grid. It defines
7960  * the columns in the grid.
7961  * <br>Usage:<br>
7962  <pre><code>
7963  var colModel = new Roo.grid.ColumnModel([
7964         {header: "Ticker", width: 60, sortable: true, locked: true},
7965         {header: "Company Name", width: 150, sortable: true},
7966         {header: "Market Cap.", width: 100, sortable: true},
7967         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7968         {header: "Employees", width: 100, sortable: true, resizable: false}
7969  ]);
7970  </code></pre>
7971  * <p>
7972  
7973  * The config options listed for this class are options which may appear in each
7974  * individual column definition.
7975  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7976  * @constructor
7977  * @param {Object} config An Array of column config objects. See this class's
7978  * config objects for details.
7979 */
7980 Roo.grid.ColumnModel = function(config){
7981         /**
7982      * The config passed into the constructor
7983      */
7984     this.config = []; //config;
7985     this.lookup = {};
7986
7987     // if no id, create one
7988     // if the column does not have a dataIndex mapping,
7989     // map it to the order it is in the config
7990     for(var i = 0, len = config.length; i < len; i++){
7991         this.addColumn(config[i]);
7992         
7993     }
7994
7995     /**
7996      * The width of columns which have no width specified (defaults to 100)
7997      * @type Number
7998      */
7999     this.defaultWidth = 100;
8000
8001     /**
8002      * Default sortable of columns which have no sortable specified (defaults to false)
8003      * @type Boolean
8004      */
8005     this.defaultSortable = false;
8006
8007     this.addEvents({
8008         /**
8009              * @event widthchange
8010              * Fires when the width of a column changes.
8011              * @param {ColumnModel} this
8012              * @param {Number} columnIndex The column index
8013              * @param {Number} newWidth The new width
8014              */
8015             "widthchange": true,
8016         /**
8017              * @event headerchange
8018              * Fires when the text of a header changes.
8019              * @param {ColumnModel} this
8020              * @param {Number} columnIndex The column index
8021              * @param {Number} newText The new header text
8022              */
8023             "headerchange": true,
8024         /**
8025              * @event hiddenchange
8026              * Fires when a column is hidden or "unhidden".
8027              * @param {ColumnModel} this
8028              * @param {Number} columnIndex The column index
8029              * @param {Boolean} hidden true if hidden, false otherwise
8030              */
8031             "hiddenchange": true,
8032             /**
8033          * @event columnmoved
8034          * Fires when a column is moved.
8035          * @param {ColumnModel} this
8036          * @param {Number} oldIndex
8037          * @param {Number} newIndex
8038          */
8039         "columnmoved" : true,
8040         /**
8041          * @event columlockchange
8042          * Fires when a column's locked state is changed
8043          * @param {ColumnModel} this
8044          * @param {Number} colIndex
8045          * @param {Boolean} locked true if locked
8046          */
8047         "columnlockchange" : true
8048     });
8049     Roo.grid.ColumnModel.superclass.constructor.call(this);
8050 };
8051 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8052     /**
8053      * @cfg {String} header The header text to display in the Grid view.
8054      */
8055         /**
8056      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8057      */
8058         /**
8059      * @cfg {String} smHeader Header at Bootsrap Small width
8060      */
8061         /**
8062      * @cfg {String} mdHeader Header at Bootsrap Medium width
8063      */
8064         /**
8065      * @cfg {String} lgHeader Header at Bootsrap Large width
8066      */
8067         /**
8068      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8069      */
8070     /**
8071      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8072      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8073      * specified, the column's index is used as an index into the Record's data Array.
8074      */
8075     /**
8076      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8077      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8078      */
8079     /**
8080      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8081      * Defaults to the value of the {@link #defaultSortable} property.
8082      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8083      */
8084     /**
8085      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8086      */
8087     /**
8088      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8089      */
8090     /**
8091      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8092      */
8093     /**
8094      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8095      */
8096     /**
8097      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8098      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8099      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8100      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8101      */
8102        /**
8103      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8104      */
8105     /**
8106      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8107      */
8108     /**
8109      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8110      */
8111     /**
8112      * @cfg {String} cursor (Optional)
8113      */
8114     /**
8115      * @cfg {String} tooltip (Optional)
8116      */
8117     /**
8118      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8119      */
8120     /**
8121      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8122      */
8123     /**
8124      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8125      */
8126     /**
8127      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8128      */
8129         /**
8130      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8131      */
8132     /**
8133      * Returns the id of the column at the specified index.
8134      * @param {Number} index The column index
8135      * @return {String} the id
8136      */
8137     getColumnId : function(index){
8138         return this.config[index].id;
8139     },
8140
8141     /**
8142      * Returns the column for a specified id.
8143      * @param {String} id The column id
8144      * @return {Object} the column
8145      */
8146     getColumnById : function(id){
8147         return this.lookup[id];
8148     },
8149
8150     
8151     /**
8152      * Returns the column Object for a specified dataIndex.
8153      * @param {String} dataIndex The column dataIndex
8154      * @return {Object|Boolean} the column or false if not found
8155      */
8156     getColumnByDataIndex: function(dataIndex){
8157         var index = this.findColumnIndex(dataIndex);
8158         return index > -1 ? this.config[index] : false;
8159     },
8160     
8161     /**
8162      * Returns the index for a specified column id.
8163      * @param {String} id The column id
8164      * @return {Number} the index, or -1 if not found
8165      */
8166     getIndexById : function(id){
8167         for(var i = 0, len = this.config.length; i < len; i++){
8168             if(this.config[i].id == id){
8169                 return i;
8170             }
8171         }
8172         return -1;
8173     },
8174     
8175     /**
8176      * Returns the index for a specified column dataIndex.
8177      * @param {String} dataIndex The column dataIndex
8178      * @return {Number} the index, or -1 if not found
8179      */
8180     
8181     findColumnIndex : function(dataIndex){
8182         for(var i = 0, len = this.config.length; i < len; i++){
8183             if(this.config[i].dataIndex == dataIndex){
8184                 return i;
8185             }
8186         }
8187         return -1;
8188     },
8189     
8190     
8191     moveColumn : function(oldIndex, newIndex){
8192         var c = this.config[oldIndex];
8193         this.config.splice(oldIndex, 1);
8194         this.config.splice(newIndex, 0, c);
8195         this.dataMap = null;
8196         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8197     },
8198
8199     isLocked : function(colIndex){
8200         return this.config[colIndex].locked === true;
8201     },
8202
8203     setLocked : function(colIndex, value, suppressEvent){
8204         if(this.isLocked(colIndex) == value){
8205             return;
8206         }
8207         this.config[colIndex].locked = value;
8208         if(!suppressEvent){
8209             this.fireEvent("columnlockchange", this, colIndex, value);
8210         }
8211     },
8212
8213     getTotalLockedWidth : function(){
8214         var totalWidth = 0;
8215         for(var i = 0; i < this.config.length; i++){
8216             if(this.isLocked(i) && !this.isHidden(i)){
8217                 this.totalWidth += this.getColumnWidth(i);
8218             }
8219         }
8220         return totalWidth;
8221     },
8222
8223     getLockedCount : function(){
8224         for(var i = 0, len = this.config.length; i < len; i++){
8225             if(!this.isLocked(i)){
8226                 return i;
8227             }
8228         }
8229         
8230         return this.config.length;
8231     },
8232
8233     /**
8234      * Returns the number of columns.
8235      * @return {Number}
8236      */
8237     getColumnCount : function(visibleOnly){
8238         if(visibleOnly === true){
8239             var c = 0;
8240             for(var i = 0, len = this.config.length; i < len; i++){
8241                 if(!this.isHidden(i)){
8242                     c++;
8243                 }
8244             }
8245             return c;
8246         }
8247         return this.config.length;
8248     },
8249
8250     /**
8251      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8252      * @param {Function} fn
8253      * @param {Object} scope (optional)
8254      * @return {Array} result
8255      */
8256     getColumnsBy : function(fn, scope){
8257         var r = [];
8258         for(var i = 0, len = this.config.length; i < len; i++){
8259             var c = this.config[i];
8260             if(fn.call(scope||this, c, i) === true){
8261                 r[r.length] = c;
8262             }
8263         }
8264         return r;
8265     },
8266
8267     /**
8268      * Returns true if the specified column is sortable.
8269      * @param {Number} col The column index
8270      * @return {Boolean}
8271      */
8272     isSortable : function(col){
8273         if(typeof this.config[col].sortable == "undefined"){
8274             return this.defaultSortable;
8275         }
8276         return this.config[col].sortable;
8277     },
8278
8279     /**
8280      * Returns the rendering (formatting) function defined for the column.
8281      * @param {Number} col The column index.
8282      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8283      */
8284     getRenderer : function(col){
8285         if(!this.config[col].renderer){
8286             return Roo.grid.ColumnModel.defaultRenderer;
8287         }
8288         return this.config[col].renderer;
8289     },
8290
8291     /**
8292      * Sets the rendering (formatting) function for a column.
8293      * @param {Number} col The column index
8294      * @param {Function} fn The function to use to process the cell's raw data
8295      * to return HTML markup for the grid view. The render function is called with
8296      * the following parameters:<ul>
8297      * <li>Data value.</li>
8298      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8299      * <li>css A CSS style string to apply to the table cell.</li>
8300      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8301      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8302      * <li>Row index</li>
8303      * <li>Column index</li>
8304      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8305      */
8306     setRenderer : function(col, fn){
8307         this.config[col].renderer = fn;
8308     },
8309
8310     /**
8311      * Returns the width for the specified column.
8312      * @param {Number} col The column index
8313      * @param (optional) {String} gridSize bootstrap width size.
8314      * @return {Number}
8315      */
8316     getColumnWidth : function(col, gridSize)
8317         {
8318                 var cfg = this.config[col];
8319                 
8320                 if (typeof(gridSize) == 'undefined') {
8321                         return cfg.width * 1 || this.defaultWidth;
8322                 }
8323                 if (gridSize === false) { // if we set it..
8324                         return cfg.width || false;
8325                 }
8326                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8327                 
8328                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8329                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8330                                 continue;
8331                         }
8332                         return cfg[ sizes[i] ];
8333                 }
8334                 return 1;
8335                 
8336     },
8337
8338     /**
8339      * Sets the width for a column.
8340      * @param {Number} col The column index
8341      * @param {Number} width The new width
8342      */
8343     setColumnWidth : function(col, width, suppressEvent){
8344         this.config[col].width = width;
8345         this.totalWidth = null;
8346         if(!suppressEvent){
8347              this.fireEvent("widthchange", this, col, width);
8348         }
8349     },
8350
8351     /**
8352      * Returns the total width of all columns.
8353      * @param {Boolean} includeHidden True to include hidden column widths
8354      * @return {Number}
8355      */
8356     getTotalWidth : function(includeHidden){
8357         if(!this.totalWidth){
8358             this.totalWidth = 0;
8359             for(var i = 0, len = this.config.length; i < len; i++){
8360                 if(includeHidden || !this.isHidden(i)){
8361                     this.totalWidth += this.getColumnWidth(i);
8362                 }
8363             }
8364         }
8365         return this.totalWidth;
8366     },
8367
8368     /**
8369      * Returns the header for the specified column.
8370      * @param {Number} col The column index
8371      * @return {String}
8372      */
8373     getColumnHeader : function(col){
8374         return this.config[col].header;
8375     },
8376
8377     /**
8378      * Sets the header for a column.
8379      * @param {Number} col The column index
8380      * @param {String} header The new header
8381      */
8382     setColumnHeader : function(col, header){
8383         this.config[col].header = header;
8384         this.fireEvent("headerchange", this, col, header);
8385     },
8386
8387     /**
8388      * Returns the tooltip for the specified column.
8389      * @param {Number} col The column index
8390      * @return {String}
8391      */
8392     getColumnTooltip : function(col){
8393             return this.config[col].tooltip;
8394     },
8395     /**
8396      * Sets the tooltip for a column.
8397      * @param {Number} col The column index
8398      * @param {String} tooltip The new tooltip
8399      */
8400     setColumnTooltip : function(col, tooltip){
8401             this.config[col].tooltip = tooltip;
8402     },
8403
8404     /**
8405      * Returns the dataIndex for the specified column.
8406      * @param {Number} col The column index
8407      * @return {Number}
8408      */
8409     getDataIndex : function(col){
8410         return this.config[col].dataIndex;
8411     },
8412
8413     /**
8414      * Sets the dataIndex for a column.
8415      * @param {Number} col The column index
8416      * @param {Number} dataIndex The new dataIndex
8417      */
8418     setDataIndex : function(col, dataIndex){
8419         this.config[col].dataIndex = dataIndex;
8420     },
8421
8422     
8423     
8424     /**
8425      * Returns true if the cell is editable.
8426      * @param {Number} colIndex The column index
8427      * @param {Number} rowIndex The row index - this is nto actually used..?
8428      * @return {Boolean}
8429      */
8430     isCellEditable : function(colIndex, rowIndex){
8431         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8432     },
8433
8434     /**
8435      * Returns the editor defined for the cell/column.
8436      * return false or null to disable editing.
8437      * @param {Number} colIndex The column index
8438      * @param {Number} rowIndex The row index
8439      * @return {Object}
8440      */
8441     getCellEditor : function(colIndex, rowIndex){
8442         return this.config[colIndex].editor;
8443     },
8444
8445     /**
8446      * Sets if a column is editable.
8447      * @param {Number} col The column index
8448      * @param {Boolean} editable True if the column is editable
8449      */
8450     setEditable : function(col, editable){
8451         this.config[col].editable = editable;
8452     },
8453
8454
8455     /**
8456      * Returns true if the column is hidden.
8457      * @param {Number} colIndex The column index
8458      * @return {Boolean}
8459      */
8460     isHidden : function(colIndex){
8461         return this.config[colIndex].hidden;
8462     },
8463
8464
8465     /**
8466      * Returns true if the column width cannot be changed
8467      */
8468     isFixed : function(colIndex){
8469         return this.config[colIndex].fixed;
8470     },
8471
8472     /**
8473      * Returns true if the column can be resized
8474      * @return {Boolean}
8475      */
8476     isResizable : function(colIndex){
8477         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8478     },
8479     /**
8480      * Sets if a column is hidden.
8481      * @param {Number} colIndex The column index
8482      * @param {Boolean} hidden True if the column is hidden
8483      */
8484     setHidden : function(colIndex, hidden){
8485         this.config[colIndex].hidden = hidden;
8486         this.totalWidth = null;
8487         this.fireEvent("hiddenchange", this, colIndex, hidden);
8488     },
8489
8490     /**
8491      * Sets the editor for a column.
8492      * @param {Number} col The column index
8493      * @param {Object} editor The editor object
8494      */
8495     setEditor : function(col, editor){
8496         this.config[col].editor = editor;
8497     },
8498     /**
8499      * Add a column (experimental...) - defaults to adding to the end..
8500      * @param {Object} config 
8501     */
8502     addColumn : function(c)
8503     {
8504     
8505         var i = this.config.length;
8506         this.config[i] = c;
8507         
8508         if(typeof c.dataIndex == "undefined"){
8509             c.dataIndex = i;
8510         }
8511         if(typeof c.renderer == "string"){
8512             c.renderer = Roo.util.Format[c.renderer];
8513         }
8514         if(typeof c.id == "undefined"){
8515             c.id = Roo.id();
8516         }
8517         if(c.editor && c.editor.xtype){
8518             c.editor  = Roo.factory(c.editor, Roo.grid);
8519         }
8520         if(c.editor && c.editor.isFormField){
8521             c.editor = new Roo.grid.GridEditor(c.editor);
8522         }
8523         this.lookup[c.id] = c;
8524     }
8525     
8526 });
8527
8528 Roo.grid.ColumnModel.defaultRenderer = function(value)
8529 {
8530     if(typeof value == "object") {
8531         return value;
8532     }
8533         if(typeof value == "string" && value.length < 1){
8534             return "&#160;";
8535         }
8536     
8537         return String.format("{0}", value);
8538 };
8539
8540 // Alias for backwards compatibility
8541 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8542 /*
8543  * Based on:
8544  * Ext JS Library 1.1.1
8545  * Copyright(c) 2006-2007, Ext JS, LLC.
8546  *
8547  * Originally Released Under LGPL - original licence link has changed is not relivant.
8548  *
8549  * Fork - LGPL
8550  * <script type="text/javascript">
8551  */
8552  
8553 /**
8554  * @class Roo.LoadMask
8555  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8556  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8557  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8558  * element's UpdateManager load indicator and will be destroyed after the initial load.
8559  * @constructor
8560  * Create a new LoadMask
8561  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8562  * @param {Object} config The config object
8563  */
8564 Roo.LoadMask = function(el, config){
8565     this.el = Roo.get(el);
8566     Roo.apply(this, config);
8567     if(this.store){
8568         this.store.on('beforeload', this.onBeforeLoad, this);
8569         this.store.on('load', this.onLoad, this);
8570         this.store.on('loadexception', this.onLoadException, this);
8571         this.removeMask = false;
8572     }else{
8573         var um = this.el.getUpdateManager();
8574         um.showLoadIndicator = false; // disable the default indicator
8575         um.on('beforeupdate', this.onBeforeLoad, this);
8576         um.on('update', this.onLoad, this);
8577         um.on('failure', this.onLoad, this);
8578         this.removeMask = true;
8579     }
8580 };
8581
8582 Roo.LoadMask.prototype = {
8583     /**
8584      * @cfg {Boolean} removeMask
8585      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8586      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8587      */
8588     removeMask : false,
8589     /**
8590      * @cfg {String} msg
8591      * The text to display in a centered loading message box (defaults to 'Loading...')
8592      */
8593     msg : 'Loading...',
8594     /**
8595      * @cfg {String} msgCls
8596      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8597      */
8598     msgCls : 'x-mask-loading',
8599
8600     /**
8601      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8602      * @type Boolean
8603      */
8604     disabled: false,
8605
8606     /**
8607      * Disables the mask to prevent it from being displayed
8608      */
8609     disable : function(){
8610        this.disabled = true;
8611     },
8612
8613     /**
8614      * Enables the mask so that it can be displayed
8615      */
8616     enable : function(){
8617         this.disabled = false;
8618     },
8619     
8620     onLoadException : function()
8621     {
8622         Roo.log(arguments);
8623         
8624         if (typeof(arguments[3]) != 'undefined') {
8625             Roo.MessageBox.alert("Error loading",arguments[3]);
8626         } 
8627         /*
8628         try {
8629             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8630                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8631             }   
8632         } catch(e) {
8633             
8634         }
8635         */
8636     
8637         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8638     },
8639     // private
8640     onLoad : function()
8641     {
8642         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8643     },
8644
8645     // private
8646     onBeforeLoad : function(){
8647         if(!this.disabled){
8648             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8649         }
8650     },
8651
8652     // private
8653     destroy : function(){
8654         if(this.store){
8655             this.store.un('beforeload', this.onBeforeLoad, this);
8656             this.store.un('load', this.onLoad, this);
8657             this.store.un('loadexception', this.onLoadException, this);
8658         }else{
8659             var um = this.el.getUpdateManager();
8660             um.un('beforeupdate', this.onBeforeLoad, this);
8661             um.un('update', this.onLoad, this);
8662             um.un('failure', this.onLoad, this);
8663         }
8664     }
8665 };/**
8666  * @class Roo.bootstrap.Table
8667  * @licence LGBL
8668  * @extends Roo.bootstrap.Component
8669  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8670  * Similar to Roo.grid.Grid
8671  * <pre><code>
8672  var table = Roo.factory({
8673     xtype : 'Table',
8674     xns : Roo.bootstrap,
8675     autoSizeColumns: true,
8676     
8677     
8678     store : {
8679         xtype : 'Store',
8680         xns : Roo.data,
8681         remoteSort : true,
8682         sortInfo : { direction : 'ASC', field: 'name' },
8683         proxy : {
8684            xtype : 'HttpProxy',
8685            xns : Roo.data,
8686            method : 'GET',
8687            url : 'https://example.com/some.data.url.json'
8688         },
8689         reader : {
8690            xtype : 'JsonReader',
8691            xns : Roo.data,
8692            fields : [ 'id', 'name', whatever' ],
8693            id : 'id',
8694            root : 'data'
8695         }
8696     },
8697     cm : [
8698         {
8699             xtype : 'ColumnModel',
8700             xns : Roo.grid,
8701             align : 'center',
8702             cursor : 'pointer',
8703             dataIndex : 'is_in_group',
8704             header : "Name",
8705             sortable : true,
8706             renderer : function(v, x , r) {  
8707             
8708                 return String.format("{0}", v)
8709             }
8710             width : 3
8711         } // more columns..
8712     ],
8713     selModel : {
8714         xtype : 'RowSelectionModel',
8715         xns : Roo.bootstrap.Table
8716         // you can add listeners to catch selection change here....
8717     }
8718      
8719
8720  });
8721  // set any options
8722  grid.render(Roo.get("some-div"));
8723 </code></pre>
8724
8725 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8726
8727
8728
8729  *
8730  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8731  * @cfg {Roo.data.Store} store The data store to use
8732  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8733  * 
8734  * @cfg {String} cls table class
8735  *
8736  * 
8737  * @cfg {boolean} striped Should the rows be alternative striped
8738  * @cfg {boolean} bordered Add borders to the table
8739  * @cfg {boolean} hover Add hover highlighting
8740  * @cfg {boolean} condensed Format condensed
8741  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8742  *                also adds table-responsive (see bootstrap docs for details)
8743  * @cfg {Boolean} loadMask (true|false) default false
8744  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8745  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8746  * @cfg {Boolean} rowSelection (true|false) default false
8747  * @cfg {Boolean} cellSelection (true|false) default false
8748  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8749  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8750  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8751  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8752  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8753  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8754  * 
8755  * @constructor
8756  * Create a new Table
8757  * @param {Object} config The config object
8758  */
8759
8760 Roo.bootstrap.Table = function(config)
8761 {
8762     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8763      
8764     // BC...
8765     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8766     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8767     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8768     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8769     
8770     this.view = this; // compat with grid.
8771     
8772     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8773     if (this.sm) {
8774         this.sm.grid = this;
8775         this.selModel = Roo.factory(this.sm, Roo.grid);
8776         this.sm = this.selModel;
8777         this.sm.xmodule = this.xmodule || false;
8778     }
8779     
8780     if (this.cm && typeof(this.cm.config) == 'undefined') {
8781         this.colModel = new Roo.grid.ColumnModel(this.cm);
8782         this.cm = this.colModel;
8783         this.cm.xmodule = this.xmodule || false;
8784     }
8785     if (this.store) {
8786         this.store= Roo.factory(this.store, Roo.data);
8787         this.ds = this.store;
8788         this.ds.xmodule = this.xmodule || false;
8789          
8790     }
8791     if (this.footer && this.store) {
8792         this.footer.dataSource = this.ds;
8793         this.footer = Roo.factory(this.footer);
8794     }
8795     
8796     /** @private */
8797     this.addEvents({
8798         /**
8799          * @event cellclick
8800          * Fires when a cell is clicked
8801          * @param {Roo.bootstrap.Table} this
8802          * @param {Roo.Element} el
8803          * @param {Number} rowIndex
8804          * @param {Number} columnIndex
8805          * @param {Roo.EventObject} e
8806          */
8807         "cellclick" : true,
8808         /**
8809          * @event celldblclick
8810          * Fires when a cell is double clicked
8811          * @param {Roo.bootstrap.Table} this
8812          * @param {Roo.Element} el
8813          * @param {Number} rowIndex
8814          * @param {Number} columnIndex
8815          * @param {Roo.EventObject} e
8816          */
8817         "celldblclick" : true,
8818         /**
8819          * @event rowclick
8820          * Fires when a row is clicked
8821          * @param {Roo.bootstrap.Table} this
8822          * @param {Roo.Element} el
8823          * @param {Number} rowIndex
8824          * @param {Roo.EventObject} e
8825          */
8826         "rowclick" : true,
8827         /**
8828          * @event rowdblclick
8829          * Fires when a row is double clicked
8830          * @param {Roo.bootstrap.Table} this
8831          * @param {Roo.Element} el
8832          * @param {Number} rowIndex
8833          * @param {Roo.EventObject} e
8834          */
8835         "rowdblclick" : true,
8836         /**
8837          * @event mouseover
8838          * Fires when a mouseover occur
8839          * @param {Roo.bootstrap.Table} this
8840          * @param {Roo.Element} el
8841          * @param {Number} rowIndex
8842          * @param {Number} columnIndex
8843          * @param {Roo.EventObject} e
8844          */
8845         "mouseover" : true,
8846         /**
8847          * @event mouseout
8848          * Fires when a mouseout occur
8849          * @param {Roo.bootstrap.Table} this
8850          * @param {Roo.Element} el
8851          * @param {Number} rowIndex
8852          * @param {Number} columnIndex
8853          * @param {Roo.EventObject} e
8854          */
8855         "mouseout" : true,
8856         /**
8857          * @event rowclass
8858          * Fires when a row is rendered, so you can change add a style to it.
8859          * @param {Roo.bootstrap.Table} this
8860          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8861          */
8862         'rowclass' : true,
8863           /**
8864          * @event rowsrendered
8865          * Fires when all the  rows have been rendered
8866          * @param {Roo.bootstrap.Table} this
8867          */
8868         'rowsrendered' : true,
8869         /**
8870          * @event contextmenu
8871          * The raw contextmenu event for the entire grid.
8872          * @param {Roo.EventObject} e
8873          */
8874         "contextmenu" : true,
8875         /**
8876          * @event rowcontextmenu
8877          * Fires when a row is right clicked
8878          * @param {Roo.bootstrap.Table} this
8879          * @param {Number} rowIndex
8880          * @param {Roo.EventObject} e
8881          */
8882         "rowcontextmenu" : true,
8883         /**
8884          * @event cellcontextmenu
8885          * Fires when a cell is right clicked
8886          * @param {Roo.bootstrap.Table} this
8887          * @param {Number} rowIndex
8888          * @param {Number} cellIndex
8889          * @param {Roo.EventObject} e
8890          */
8891          "cellcontextmenu" : true,
8892          /**
8893          * @event headercontextmenu
8894          * Fires when a header is right clicked
8895          * @param {Roo.bootstrap.Table} this
8896          * @param {Number} columnIndex
8897          * @param {Roo.EventObject} e
8898          */
8899         "headercontextmenu" : true,
8900         /**
8901          * @event mousedown
8902          * The raw mousedown event for the entire grid.
8903          * @param {Roo.EventObject} e
8904          */
8905         "mousedown" : true
8906         
8907     });
8908 };
8909
8910 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8911     
8912     cls: false,
8913     
8914     striped : false,
8915     scrollBody : false,
8916     bordered: false,
8917     hover:  false,
8918     condensed : false,
8919     responsive : false,
8920     sm : false,
8921     cm : false,
8922     store : false,
8923     loadMask : false,
8924     footerShow : true,
8925     headerShow : true,
8926     enableColumnResize: true,
8927   
8928     rowSelection : false,
8929     cellSelection : false,
8930     layout : false,
8931
8932     minColumnWidth : 50,
8933     
8934     // Roo.Element - the tbody
8935     bodyEl: false,  // <tbody> Roo.Element - thead element    
8936     headEl: false,  // <thead> Roo.Element - thead element
8937     resizeProxy : false, // proxy element for dragging?
8938
8939
8940     
8941     container: false, // used by gridpanel...
8942     
8943     lazyLoad : false,
8944     
8945     CSS : Roo.util.CSS,
8946     
8947     auto_hide_footer : false,
8948     
8949     view: false, // actually points to this..
8950     
8951     getAutoCreate : function()
8952     {
8953         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8954         
8955         cfg = {
8956             tag: 'table',
8957             cls : 'table', 
8958             cn : []
8959         };
8960         // this get's auto added by panel.Grid
8961         if (this.scrollBody) {
8962             cfg.cls += ' table-body-fixed';
8963         }    
8964         if (this.striped) {
8965             cfg.cls += ' table-striped';
8966         }
8967         
8968         if (this.hover) {
8969             cfg.cls += ' table-hover';
8970         }
8971         if (this.bordered) {
8972             cfg.cls += ' table-bordered';
8973         }
8974         if (this.condensed) {
8975             cfg.cls += ' table-condensed';
8976         }
8977         
8978         if (this.responsive) {
8979             cfg.cls += ' table-responsive';
8980         }
8981         
8982         if (this.cls) {
8983             cfg.cls+=  ' ' +this.cls;
8984         }
8985         
8986         
8987         
8988         if (this.layout) {
8989             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8990         }
8991         
8992         if(this.store || this.cm){
8993             if(this.headerShow){
8994                 cfg.cn.push(this.renderHeader());
8995             }
8996             
8997             cfg.cn.push(this.renderBody());
8998             
8999             if(this.footerShow){
9000                 cfg.cn.push(this.renderFooter());
9001             }
9002             // where does this come from?
9003             //cfg.cls+=  ' TableGrid';
9004         }
9005         
9006         return { cn : [ cfg ] };
9007     },
9008     
9009     initEvents : function()
9010     {   
9011         if(!this.store || !this.cm){
9012             return;
9013         }
9014         if (this.selModel) {
9015             this.selModel.initEvents();
9016         }
9017         
9018         
9019         //Roo.log('initEvents with ds!!!!');
9020         
9021         this.bodyEl = this.el.select('tbody', true).first();
9022         this.headEl = this.el.select('thead', true).first();
9023         this.mainFoot = this.el.select('tfoot', true).first();
9024         
9025         
9026         
9027         
9028         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9029             e.on('click', this.sort, this);
9030         }, this);
9031         
9032         
9033         // why is this done????? = it breaks dialogs??
9034         //this.parent().el.setStyle('position', 'relative');
9035         
9036         
9037         if (this.footer) {
9038             this.footer.parentId = this.id;
9039             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9040             
9041             if(this.lazyLoad){
9042                 this.el.select('tfoot tr td').first().addClass('hide');
9043             }
9044         } 
9045         
9046         if(this.loadMask) {
9047             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9048         }
9049         
9050         this.store.on('load', this.onLoad, this);
9051         this.store.on('beforeload', this.onBeforeLoad, this);
9052         this.store.on('update', this.onUpdate, this);
9053         this.store.on('add', this.onAdd, this);
9054         this.store.on("clear", this.clear, this);
9055         
9056         this.el.on("contextmenu", this.onContextMenu, this);
9057         
9058         
9059         this.cm.on("headerchange", this.onHeaderChange, this);
9060         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9061
9062  //?? does bodyEl get replaced on render?
9063         this.bodyEl.on("click", this.onClick, this);
9064         this.bodyEl.on("dblclick", this.onDblClick, this);        
9065         this.bodyEl.on('scroll', this.onBodyScroll, this);
9066
9067         // guessing mainbody will work - this relays usually caught by selmodel at present.
9068         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9069   
9070   
9071         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9072         
9073   
9074         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9075             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9076         }
9077         
9078         this.initCSS();
9079     },
9080     // Compatibility with grid - we implement all the view features at present.
9081     getView : function()
9082     {
9083         return this;
9084     },
9085     
9086     initCSS : function()
9087     {
9088         
9089         
9090         var cm = this.cm, styles = [];
9091         this.CSS.removeStyleSheet(this.id + '-cssrules');
9092         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9093         // we can honour xs/sm/md/xl  as widths...
9094         // we first have to decide what widht we are currently at...
9095         var sz = Roo.getGridSize();
9096         
9097         var total = 0;
9098         var last = -1;
9099         var cols = []; // visable cols.
9100         var total_abs = 0;
9101         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9102             var w = cm.getColumnWidth(i, false);
9103             if(cm.isHidden(i)){
9104                 cols.push( { rel : false, abs : 0 });
9105                 continue;
9106             }
9107             if (w !== false) {
9108                 cols.push( { rel : false, abs : w });
9109                 total_abs += w;
9110                 last = i; // not really..
9111                 continue;
9112             }
9113             var w = cm.getColumnWidth(i, sz);
9114             if (w > 0) {
9115                 last = i
9116             }
9117             total += w;
9118             cols.push( { rel : w, abs : false });
9119         }
9120         
9121         var avail = this.bodyEl.dom.clientWidth - total_abs;
9122         
9123         var unitWidth = Math.floor(avail / total);
9124         var rem = avail - (unitWidth * total);
9125         
9126         var hidden, width, pos = 0 , splithide , left;
9127         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9128             
9129             hidden = 'display:none;';
9130             left = '';
9131             width  = 'width:0px;';
9132             splithide = '';
9133             if(!cm.isHidden(i)){
9134                 hidden = '';
9135                 
9136                 
9137                 // we can honour xs/sm/md/xl ?
9138                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9139                 if (w===0) {
9140                     hidden = 'display:none;';
9141                 }
9142                 // width should return a small number...
9143                 if (i == last) {
9144                     w+=rem; // add the remaining with..
9145                 }
9146                 pos += w;
9147                 left = "left:" + (pos -4) + "px;";
9148                 width = "width:" + w+ "px;";
9149                 
9150             }
9151             if (this.responsive) {
9152                 width = '';
9153                 left = '';
9154                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9155                 splithide = 'display: none;';
9156             }
9157             
9158             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9159             if (this.headEl) {
9160                 if (i == last) {
9161                     splithide = 'display:none;';
9162                 }
9163                 
9164                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9165                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9166                 );
9167             }
9168             
9169         }
9170         //Roo.log(styles.join(''));
9171         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9172         
9173     },
9174     
9175     
9176     
9177     onContextMenu : function(e, t)
9178     {
9179         this.processEvent("contextmenu", e);
9180     },
9181     
9182     processEvent : function(name, e)
9183     {
9184         if (name != 'touchstart' ) {
9185             this.fireEvent(name, e);    
9186         }
9187         
9188         var t = e.getTarget();
9189         
9190         var cell = Roo.get(t);
9191         
9192         if(!cell){
9193             return;
9194         }
9195         
9196         if(cell.findParent('tfoot', false, true)){
9197             return;
9198         }
9199         
9200         if(cell.findParent('thead', false, true)){
9201             
9202             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9203                 cell = Roo.get(t).findParent('th', false, true);
9204                 if (!cell) {
9205                     Roo.log("failed to find th in thead?");
9206                     Roo.log(e.getTarget());
9207                     return;
9208                 }
9209             }
9210             
9211             var cellIndex = cell.dom.cellIndex;
9212             
9213             var ename = name == 'touchstart' ? 'click' : name;
9214             this.fireEvent("header" + ename, this, cellIndex, e);
9215             
9216             return;
9217         }
9218         
9219         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9220             cell = Roo.get(t).findParent('td', false, true);
9221             if (!cell) {
9222                 Roo.log("failed to find th in tbody?");
9223                 Roo.log(e.getTarget());
9224                 return;
9225             }
9226         }
9227         
9228         var row = cell.findParent('tr', false, true);
9229         var cellIndex = cell.dom.cellIndex;
9230         var rowIndex = row.dom.rowIndex - 1;
9231         
9232         if(row !== false){
9233             
9234             this.fireEvent("row" + name, this, rowIndex, e);
9235             
9236             if(cell !== false){
9237             
9238                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9239             }
9240         }
9241         
9242     },
9243     
9244     onMouseover : function(e, el)
9245     {
9246         var cell = Roo.get(el);
9247         
9248         if(!cell){
9249             return;
9250         }
9251         
9252         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9253             cell = cell.findParent('td', false, true);
9254         }
9255         
9256         var row = cell.findParent('tr', false, true);
9257         var cellIndex = cell.dom.cellIndex;
9258         var rowIndex = row.dom.rowIndex - 1; // start from 0
9259         
9260         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9261         
9262     },
9263     
9264     onMouseout : function(e, el)
9265     {
9266         var cell = Roo.get(el);
9267         
9268         if(!cell){
9269             return;
9270         }
9271         
9272         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9273             cell = cell.findParent('td', false, true);
9274         }
9275         
9276         var row = cell.findParent('tr', false, true);
9277         var cellIndex = cell.dom.cellIndex;
9278         var rowIndex = row.dom.rowIndex - 1; // start from 0
9279         
9280         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9281         
9282     },
9283     
9284     onClick : function(e, el)
9285     {
9286         var cell = Roo.get(el);
9287         
9288         if(!cell || (!this.cellSelection && !this.rowSelection)){
9289             return;
9290         }
9291         
9292         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9293             cell = cell.findParent('td', false, true);
9294         }
9295         
9296         if(!cell || typeof(cell) == 'undefined'){
9297             return;
9298         }
9299         
9300         var row = cell.findParent('tr', false, true);
9301         
9302         if(!row || typeof(row) == 'undefined'){
9303             return;
9304         }
9305         
9306         var cellIndex = cell.dom.cellIndex;
9307         var rowIndex = this.getRowIndex(row);
9308         
9309         // why??? - should these not be based on SelectionModel?
9310         //if(this.cellSelection){
9311             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9312         //}
9313         
9314         //if(this.rowSelection){
9315             this.fireEvent('rowclick', this, row, rowIndex, e);
9316         //}
9317          
9318     },
9319         
9320     onDblClick : function(e,el)
9321     {
9322         var cell = Roo.get(el);
9323         
9324         if(!cell || (!this.cellSelection && !this.rowSelection)){
9325             return;
9326         }
9327         
9328         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9329             cell = cell.findParent('td', false, true);
9330         }
9331         
9332         if(!cell || typeof(cell) == 'undefined'){
9333             return;
9334         }
9335         
9336         var row = cell.findParent('tr', false, true);
9337         
9338         if(!row || typeof(row) == 'undefined'){
9339             return;
9340         }
9341         
9342         var cellIndex = cell.dom.cellIndex;
9343         var rowIndex = this.getRowIndex(row);
9344         
9345         if(this.cellSelection){
9346             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9347         }
9348         
9349         if(this.rowSelection){
9350             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9351         }
9352     },
9353     findRowIndex : function(el)
9354     {
9355         var cell = Roo.get(el);
9356         if(!cell) {
9357             return false;
9358         }
9359         var row = cell.findParent('tr', false, true);
9360         
9361         if(!row || typeof(row) == 'undefined'){
9362             return false;
9363         }
9364         return this.getRowIndex(row);
9365     },
9366     sort : function(e,el)
9367     {
9368         var col = Roo.get(el);
9369         
9370         if(!col.hasClass('sortable')){
9371             return;
9372         }
9373         
9374         var sort = col.attr('sort');
9375         var dir = 'ASC';
9376         
9377         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9378             dir = 'DESC';
9379         }
9380         
9381         this.store.sortInfo = {field : sort, direction : dir};
9382         
9383         if (this.footer) {
9384             Roo.log("calling footer first");
9385             this.footer.onClick('first');
9386         } else {
9387         
9388             this.store.load({ params : { start : 0 } });
9389         }
9390     },
9391     
9392     renderHeader : function()
9393     {
9394         var header = {
9395             tag: 'thead',
9396             cn : []
9397         };
9398         
9399         var cm = this.cm;
9400         this.totalWidth = 0;
9401         
9402         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9403             
9404             var config = cm.config[i];
9405             
9406             var c = {
9407                 tag: 'th',
9408                 cls : 'x-hcol-' + i,
9409                 style : '',
9410                 
9411                 html: cm.getColumnHeader(i)
9412             };
9413             
9414             var tooltip = cm.getColumnTooltip(i);
9415             if (tooltip) {
9416                 c.tooltip = tooltip;
9417             }
9418             
9419             
9420             var hh = '';
9421             
9422             if(typeof(config.sortable) != 'undefined' && config.sortable){
9423                 c.cls += ' sortable';
9424                 c.html = '<i class="fa"></i>' + c.html;
9425             }
9426             
9427             // could use BS4 hidden-..-down 
9428             
9429             if(typeof(config.lgHeader) != 'undefined'){
9430                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9431             }
9432             
9433             if(typeof(config.mdHeader) != 'undefined'){
9434                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9435             }
9436             
9437             if(typeof(config.smHeader) != 'undefined'){
9438                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9439             }
9440             
9441             if(typeof(config.xsHeader) != 'undefined'){
9442                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9443             }
9444             
9445             if(hh.length){
9446                 c.html = hh;
9447             }
9448             
9449             if(typeof(config.tooltip) != 'undefined'){
9450                 c.tooltip = config.tooltip;
9451             }
9452             
9453             if(typeof(config.colspan) != 'undefined'){
9454                 c.colspan = config.colspan;
9455             }
9456             
9457             // hidden is handled by CSS now
9458             
9459             if(typeof(config.dataIndex) != 'undefined'){
9460                 c.sort = config.dataIndex;
9461             }
9462             
9463            
9464             
9465             if(typeof(config.align) != 'undefined' && config.align.length){
9466                 c.style += ' text-align:' + config.align + ';';
9467             }
9468             
9469             /* width is done in CSS
9470              *if(typeof(config.width) != 'undefined'){
9471                 c.style += ' width:' + config.width + 'px;';
9472                 this.totalWidth += config.width;
9473             } else {
9474                 this.totalWidth += 100; // assume minimum of 100 per column?
9475             }
9476             */
9477             
9478             if(typeof(config.cls) != 'undefined'){
9479                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9480             }
9481             // this is the bit that doesnt reall work at all...
9482             
9483             if (this.responsive) {
9484                  
9485             
9486                 ['xs','sm','md','lg'].map(function(size){
9487                     
9488                     if(typeof(config[size]) == 'undefined'){
9489                         return;
9490                     }
9491                      
9492                     if (!config[size]) { // 0 = hidden
9493                         // BS 4 '0' is treated as hide that column and below.
9494                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9495                         return;
9496                     }
9497                     
9498                     c.cls += ' col-' + size + '-' + config[size] + (
9499                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9500                     );
9501                     
9502                     
9503                 });
9504             }
9505             // at the end?
9506             
9507             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9508             
9509             
9510             
9511             
9512             header.cn.push(c)
9513         }
9514         
9515         return header;
9516     },
9517     
9518     renderBody : function()
9519     {
9520         var body = {
9521             tag: 'tbody',
9522             cn : [
9523                 {
9524                     tag: 'tr',
9525                     cn : [
9526                         {
9527                             tag : 'td',
9528                             colspan :  this.cm.getColumnCount()
9529                         }
9530                     ]
9531                 }
9532             ]
9533         };
9534         
9535         return body;
9536     },
9537     
9538     renderFooter : function()
9539     {
9540         var footer = {
9541             tag: 'tfoot',
9542             cn : [
9543                 {
9544                     tag: 'tr',
9545                     cn : [
9546                         {
9547                             tag : 'td',
9548                             colspan :  this.cm.getColumnCount()
9549                         }
9550                     ]
9551                 }
9552             ]
9553         };
9554         
9555         return footer;
9556     },
9557     
9558     
9559     
9560     onLoad : function()
9561     {
9562 //        Roo.log('ds onload');
9563         this.clear();
9564         
9565         var _this = this;
9566         var cm = this.cm;
9567         var ds = this.store;
9568         
9569         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9570             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9571             if (_this.store.sortInfo) {
9572                     
9573                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9574                     e.select('i', true).addClass(['fa-arrow-up']);
9575                 }
9576                 
9577                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9578                     e.select('i', true).addClass(['fa-arrow-down']);
9579                 }
9580             }
9581         });
9582         
9583         var tbody =  this.bodyEl;
9584               
9585         if(ds.getCount() > 0){
9586             ds.data.each(function(d,rowIndex){
9587                 var row =  this.renderRow(cm, ds, rowIndex);
9588                 
9589                 tbody.createChild(row);
9590                 
9591                 var _this = this;
9592                 
9593                 if(row.cellObjects.length){
9594                     Roo.each(row.cellObjects, function(r){
9595                         _this.renderCellObject(r);
9596                     })
9597                 }
9598                 
9599             }, this);
9600         }
9601         
9602         var tfoot = this.el.select('tfoot', true).first();
9603         
9604         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9605             
9606             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9607             
9608             var total = this.ds.getTotalCount();
9609             
9610             if(this.footer.pageSize < total){
9611                 this.mainFoot.show();
9612             }
9613         }
9614         
9615         Roo.each(this.el.select('tbody td', true).elements, function(e){
9616             e.on('mouseover', _this.onMouseover, _this);
9617         });
9618         
9619         Roo.each(this.el.select('tbody td', true).elements, function(e){
9620             e.on('mouseout', _this.onMouseout, _this);
9621         });
9622         this.fireEvent('rowsrendered', this);
9623         
9624         this.autoSize();
9625         
9626         this.initCSS(); /// resize cols
9627
9628         
9629     },
9630     
9631     
9632     onUpdate : function(ds,record)
9633     {
9634         this.refreshRow(record);
9635         this.autoSize();
9636     },
9637     
9638     onRemove : function(ds, record, index, isUpdate){
9639         if(isUpdate !== true){
9640             this.fireEvent("beforerowremoved", this, index, record);
9641         }
9642         var bt = this.bodyEl.dom;
9643         
9644         var rows = this.el.select('tbody > tr', true).elements;
9645         
9646         if(typeof(rows[index]) != 'undefined'){
9647             bt.removeChild(rows[index].dom);
9648         }
9649         
9650 //        if(bt.rows[index]){
9651 //            bt.removeChild(bt.rows[index]);
9652 //        }
9653         
9654         if(isUpdate !== true){
9655             //this.stripeRows(index);
9656             //this.syncRowHeights(index, index);
9657             //this.layout();
9658             this.fireEvent("rowremoved", this, index, record);
9659         }
9660     },
9661     
9662     onAdd : function(ds, records, rowIndex)
9663     {
9664         //Roo.log('on Add called');
9665         // - note this does not handle multiple adding very well..
9666         var bt = this.bodyEl.dom;
9667         for (var i =0 ; i < records.length;i++) {
9668             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9669             //Roo.log(records[i]);
9670             //Roo.log(this.store.getAt(rowIndex+i));
9671             this.insertRow(this.store, rowIndex + i, false);
9672             return;
9673         }
9674         
9675     },
9676     
9677     
9678     refreshRow : function(record){
9679         var ds = this.store, index;
9680         if(typeof record == 'number'){
9681             index = record;
9682             record = ds.getAt(index);
9683         }else{
9684             index = ds.indexOf(record);
9685             if (index < 0) {
9686                 return; // should not happen - but seems to 
9687             }
9688         }
9689         this.insertRow(ds, index, true);
9690         this.autoSize();
9691         this.onRemove(ds, record, index+1, true);
9692         this.autoSize();
9693         //this.syncRowHeights(index, index);
9694         //this.layout();
9695         this.fireEvent("rowupdated", this, index, record);
9696     },
9697     // private - called by RowSelection
9698     onRowSelect : function(rowIndex){
9699         var row = this.getRowDom(rowIndex);
9700         row.addClass(['bg-info','info']);
9701     },
9702     // private - called by RowSelection
9703     onRowDeselect : function(rowIndex)
9704     {
9705         if (rowIndex < 0) {
9706             return;
9707         }
9708         var row = this.getRowDom(rowIndex);
9709         row.removeClass(['bg-info','info']);
9710     },
9711       /**
9712      * Focuses the specified row.
9713      * @param {Number} row The row index
9714      */
9715     focusRow : function(row)
9716     {
9717         //Roo.log('GridView.focusRow');
9718         var x = this.bodyEl.dom.scrollLeft;
9719         this.focusCell(row, 0, false);
9720         this.bodyEl.dom.scrollLeft = x;
9721
9722     },
9723      /**
9724      * Focuses the specified cell.
9725      * @param {Number} row The row index
9726      * @param {Number} col The column index
9727      * @param {Boolean} hscroll false to disable horizontal scrolling
9728      */
9729     focusCell : function(row, col, hscroll)
9730     {
9731         //Roo.log('GridView.focusCell');
9732         var el = this.ensureVisible(row, col, hscroll);
9733         // not sure what focusEL achives = it's a <a> pos relative 
9734         //this.focusEl.alignTo(el, "tl-tl");
9735         //if(Roo.isGecko){
9736         //    this.focusEl.focus();
9737         //}else{
9738         //    this.focusEl.focus.defer(1, this.focusEl);
9739         //}
9740     },
9741     
9742      /**
9743      * Scrolls the specified cell into view
9744      * @param {Number} row The row index
9745      * @param {Number} col The column index
9746      * @param {Boolean} hscroll false to disable horizontal scrolling
9747      */
9748     ensureVisible : function(row, col, hscroll)
9749     {
9750         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9751         //return null; //disable for testing.
9752         if(typeof row != "number"){
9753             row = row.rowIndex;
9754         }
9755         if(row < 0 && row >= this.ds.getCount()){
9756             return  null;
9757         }
9758         col = (col !== undefined ? col : 0);
9759         var cm = this.cm;
9760         while(cm.isHidden(col)){
9761             col++;
9762         }
9763
9764         var el = this.getCellDom(row, col);
9765         if(!el){
9766             return null;
9767         }
9768         var c = this.bodyEl.dom;
9769
9770         var ctop = parseInt(el.offsetTop, 10);
9771         var cleft = parseInt(el.offsetLeft, 10);
9772         var cbot = ctop + el.offsetHeight;
9773         var cright = cleft + el.offsetWidth;
9774
9775         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9776         var ch = 0; //?? header is not withing the area?
9777         var stop = parseInt(c.scrollTop, 10);
9778         var sleft = parseInt(c.scrollLeft, 10);
9779         var sbot = stop + ch;
9780         var sright = sleft + c.clientWidth;
9781         /*
9782         Roo.log('GridView.ensureVisible:' +
9783                 ' ctop:' + ctop +
9784                 ' c.clientHeight:' + c.clientHeight +
9785                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9786                 ' stop:' + stop +
9787                 ' cbot:' + cbot +
9788                 ' sbot:' + sbot +
9789                 ' ch:' + ch  
9790                 );
9791         */
9792         if(ctop < stop){
9793             c.scrollTop = ctop;
9794             //Roo.log("set scrolltop to ctop DISABLE?");
9795         }else if(cbot > sbot){
9796             //Roo.log("set scrolltop to cbot-ch");
9797             c.scrollTop = cbot-ch;
9798         }
9799
9800         if(hscroll !== false){
9801             if(cleft < sleft){
9802                 c.scrollLeft = cleft;
9803             }else if(cright > sright){
9804                 c.scrollLeft = cright-c.clientWidth;
9805             }
9806         }
9807
9808         return el;
9809     },
9810     
9811     
9812     insertRow : function(dm, rowIndex, isUpdate){
9813         
9814         if(!isUpdate){
9815             this.fireEvent("beforerowsinserted", this, rowIndex);
9816         }
9817             //var s = this.getScrollState();
9818         var row = this.renderRow(this.cm, this.store, rowIndex);
9819         // insert before rowIndex..
9820         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9821         
9822         var _this = this;
9823                 
9824         if(row.cellObjects.length){
9825             Roo.each(row.cellObjects, function(r){
9826                 _this.renderCellObject(r);
9827             })
9828         }
9829             
9830         if(!isUpdate){
9831             this.fireEvent("rowsinserted", this, rowIndex);
9832             //this.syncRowHeights(firstRow, lastRow);
9833             //this.stripeRows(firstRow);
9834             //this.layout();
9835         }
9836         
9837     },
9838     
9839     
9840     getRowDom : function(rowIndex)
9841     {
9842         var rows = this.el.select('tbody > tr', true).elements;
9843         
9844         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9845         
9846     },
9847     getCellDom : function(rowIndex, colIndex)
9848     {
9849         var row = this.getRowDom(rowIndex);
9850         if (row === false) {
9851             return false;
9852         }
9853         var cols = row.select('td', true).elements;
9854         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9855         
9856     },
9857     
9858     // returns the object tree for a tr..
9859   
9860     
9861     renderRow : function(cm, ds, rowIndex) 
9862     {
9863         var d = ds.getAt(rowIndex);
9864         
9865         var row = {
9866             tag : 'tr',
9867             cls : 'x-row-' + rowIndex,
9868             cn : []
9869         };
9870             
9871         var cellObjects = [];
9872         
9873         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9874             var config = cm.config[i];
9875             
9876             var renderer = cm.getRenderer(i);
9877             var value = '';
9878             var id = false;
9879             
9880             if(typeof(renderer) !== 'undefined'){
9881                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9882             }
9883             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9884             // and are rendered into the cells after the row is rendered - using the id for the element.
9885             
9886             if(typeof(value) === 'object'){
9887                 id = Roo.id();
9888                 cellObjects.push({
9889                     container : id,
9890                     cfg : value 
9891                 })
9892             }
9893             
9894             var rowcfg = {
9895                 record: d,
9896                 rowIndex : rowIndex,
9897                 colIndex : i,
9898                 rowClass : ''
9899             };
9900
9901             this.fireEvent('rowclass', this, rowcfg);
9902             
9903             var td = {
9904                 tag: 'td',
9905                 // this might end up displaying HTML?
9906                 // this is too messy... - better to only do it on columsn you know are going to be too long
9907                 //tooltip : (typeof(value) === 'object') ? '' : value,
9908                 cls : rowcfg.rowClass + ' x-col-' + i,
9909                 style: '',
9910                 html: (typeof(value) === 'object') ? '' : value
9911             };
9912             
9913             if (id) {
9914                 td.id = id;
9915             }
9916             
9917             if(typeof(config.colspan) != 'undefined'){
9918                 td.colspan = config.colspan;
9919             }
9920             
9921             
9922             
9923             if(typeof(config.align) != 'undefined' && config.align.length){
9924                 td.style += ' text-align:' + config.align + ';';
9925             }
9926             if(typeof(config.valign) != 'undefined' && config.valign.length){
9927                 td.style += ' vertical-align:' + config.valign + ';';
9928             }
9929             /*
9930             if(typeof(config.width) != 'undefined'){
9931                 td.style += ' width:' +  config.width + 'px;';
9932             }
9933             */
9934             
9935             if(typeof(config.cursor) != 'undefined'){
9936                 td.style += ' cursor:' +  config.cursor + ';';
9937             }
9938             
9939             if(typeof(config.cls) != 'undefined'){
9940                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9941             }
9942             if (this.responsive) {
9943                 ['xs','sm','md','lg'].map(function(size){
9944                     
9945                     if(typeof(config[size]) == 'undefined'){
9946                         return;
9947                     }
9948                     
9949                     
9950                       
9951                     if (!config[size]) { // 0 = hidden
9952                         // BS 4 '0' is treated as hide that column and below.
9953                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9954                         return;
9955                     }
9956                     
9957                     td.cls += ' col-' + size + '-' + config[size] + (
9958                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9959                     );
9960                      
9961     
9962                 });
9963             }
9964             row.cn.push(td);
9965            
9966         }
9967         
9968         row.cellObjects = cellObjects;
9969         
9970         return row;
9971           
9972     },
9973     
9974     
9975     
9976     onBeforeLoad : function()
9977     {
9978         
9979     },
9980      /**
9981      * Remove all rows
9982      */
9983     clear : function()
9984     {
9985         this.el.select('tbody', true).first().dom.innerHTML = '';
9986     },
9987     /**
9988      * Show or hide a row.
9989      * @param {Number} rowIndex to show or hide
9990      * @param {Boolean} state hide
9991      */
9992     setRowVisibility : function(rowIndex, state)
9993     {
9994         var bt = this.bodyEl.dom;
9995         
9996         var rows = this.el.select('tbody > tr', true).elements;
9997         
9998         if(typeof(rows[rowIndex]) == 'undefined'){
9999             return;
10000         }
10001         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10002         
10003     },
10004     
10005     
10006     getSelectionModel : function(){
10007         if(!this.selModel){
10008             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10009         }
10010         return this.selModel;
10011     },
10012     /*
10013      * Render the Roo.bootstrap object from renderder
10014      */
10015     renderCellObject : function(r)
10016     {
10017         var _this = this;
10018         
10019         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10020         
10021         var t = r.cfg.render(r.container);
10022         
10023         if(r.cfg.cn){
10024             Roo.each(r.cfg.cn, function(c){
10025                 var child = {
10026                     container: t.getChildContainer(),
10027                     cfg: c
10028                 };
10029                 _this.renderCellObject(child);
10030             })
10031         }
10032     },
10033     /**
10034      * get the Row Index from a dom element.
10035      * @param {Roo.Element} row The row to look for
10036      * @returns {Number} the row
10037      */
10038     getRowIndex : function(row)
10039     {
10040         var rowIndex = -1;
10041         
10042         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10043             if(el != row){
10044                 return;
10045             }
10046             
10047             rowIndex = index;
10048         });
10049         
10050         return rowIndex;
10051     },
10052     /**
10053      * get the header TH element for columnIndex
10054      * @param {Number} columnIndex
10055      * @returns {Roo.Element}
10056      */
10057     getHeaderIndex: function(colIndex)
10058     {
10059         var cols = this.headEl.select('th', true).elements;
10060         return cols[colIndex]; 
10061     },
10062     /**
10063      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10064      * @param {domElement} cell to look for
10065      * @returns {Number} the column
10066      */
10067     getCellIndex : function(cell)
10068     {
10069         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10070         if(id){
10071             return parseInt(id[1], 10);
10072         }
10073         return 0;
10074     },
10075      /**
10076      * Returns the grid's underlying element = used by panel.Grid
10077      * @return {Element} The element
10078      */
10079     getGridEl : function(){
10080         return this.el;
10081     },
10082      /**
10083      * Forces a resize - used by panel.Grid
10084      * @return {Element} The element
10085      */
10086     autoSize : function()
10087     {
10088         //var ctr = Roo.get(this.container.dom.parentElement);
10089         var ctr = Roo.get(this.el.dom);
10090         
10091         var thd = this.getGridEl().select('thead',true).first();
10092         var tbd = this.getGridEl().select('tbody', true).first();
10093         var tfd = this.getGridEl().select('tfoot', true).first();
10094         
10095         var cw = ctr.getWidth();
10096         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10097         
10098         if (tbd) {
10099             
10100             tbd.setWidth(ctr.getWidth());
10101             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10102             // this needs fixing for various usage - currently only hydra job advers I think..
10103             //tdb.setHeight(
10104             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10105             //); 
10106             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10107             cw -= barsize;
10108         }
10109         cw = Math.max(cw, this.totalWidth);
10110         this.getGridEl().select('tbody tr',true).setWidth(cw);
10111         this.initCSS();
10112         
10113         // resize 'expandable coloumn?
10114         
10115         return; // we doe not have a view in this design..
10116         
10117     },
10118     onBodyScroll: function()
10119     {
10120         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10121         if(this.headEl){
10122             this.headEl.setStyle({
10123                 'position' : 'relative',
10124                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10125             });
10126         }
10127         
10128         if(this.lazyLoad){
10129             
10130             var scrollHeight = this.bodyEl.dom.scrollHeight;
10131             
10132             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10133             
10134             var height = this.bodyEl.getHeight();
10135             
10136             if(scrollHeight - height == scrollTop) {
10137                 
10138                 var total = this.ds.getTotalCount();
10139                 
10140                 if(this.footer.cursor + this.footer.pageSize < total){
10141                     
10142                     this.footer.ds.load({
10143                         params : {
10144                             start : this.footer.cursor + this.footer.pageSize,
10145                             limit : this.footer.pageSize
10146                         },
10147                         add : true
10148                     });
10149                 }
10150             }
10151             
10152         }
10153     },
10154     onColumnSplitterMoved : function(i, diff)
10155     {
10156         this.userResized = true;
10157         
10158         var cm = this.colModel;
10159         
10160         var w = this.getHeaderIndex(i).getWidth() + diff;
10161         
10162         
10163         cm.setColumnWidth(i, w, true);
10164         this.initCSS();
10165         //var cid = cm.getColumnId(i); << not used in this version?
10166        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10167         
10168         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10169         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10170         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10171 */
10172         //this.updateSplitters();
10173         //this.layout(); << ??
10174         this.fireEvent("columnresize", i, w);
10175     },
10176     onHeaderChange : function()
10177     {
10178         var header = this.renderHeader();
10179         var table = this.el.select('table', true).first();
10180         
10181         this.headEl.remove();
10182         this.headEl = table.createChild(header, this.bodyEl, false);
10183         
10184         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10185             e.on('click', this.sort, this);
10186         }, this);
10187         
10188         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10189             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10190         }
10191         
10192     },
10193     
10194     onHiddenChange : function(colModel, colIndex, hidden)
10195     {
10196         /*
10197         this.cm.setHidden()
10198         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10199         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10200         
10201         this.CSS.updateRule(thSelector, "display", "");
10202         this.CSS.updateRule(tdSelector, "display", "");
10203         
10204         if(hidden){
10205             this.CSS.updateRule(thSelector, "display", "none");
10206             this.CSS.updateRule(tdSelector, "display", "none");
10207         }
10208         */
10209         // onload calls initCSS()
10210         this.onHeaderChange();
10211         this.onLoad();
10212     },
10213     
10214     setColumnWidth: function(col_index, width)
10215     {
10216         // width = "md-2 xs-2..."
10217         if(!this.colModel.config[col_index]) {
10218             return;
10219         }
10220         
10221         var w = width.split(" ");
10222         
10223         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10224         
10225         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10226         
10227         
10228         for(var j = 0; j < w.length; j++) {
10229             
10230             if(!w[j]) {
10231                 continue;
10232             }
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(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10245                 continue;
10246             }
10247             
10248             h_row[0].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             for(var i = 0; i < rows.length; i++) {
10254                 
10255                 var size_cls = w[j].split("-");
10256                 
10257                 if(!Number.isInteger(size_cls[1] * 1)) {
10258                     continue;
10259                 }
10260                 
10261                 if(!this.colModel.config[col_index][size_cls[0]]) {
10262                     continue;
10263                 }
10264                 
10265                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10266                     continue;
10267                 }
10268                 
10269                 rows[i].classList.replace(
10270                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10271                     "col-"+size_cls[0]+"-"+size_cls[1]
10272                 );
10273             }
10274             
10275             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10276         }
10277     }
10278 });
10279
10280 // currently only used to find the split on drag.. 
10281 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10282
10283 /**
10284  * @depricated
10285 */
10286 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10287 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10288 /*
10289  * - LGPL
10290  *
10291  * table cell
10292  * 
10293  */
10294
10295 /**
10296  * @class Roo.bootstrap.TableCell
10297  * @extends Roo.bootstrap.Component
10298  * Bootstrap TableCell class
10299  * @cfg {String} html cell contain text
10300  * @cfg {String} cls cell class
10301  * @cfg {String} tag cell tag (td|th) default td
10302  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10303  * @cfg {String} align Aligns the content in a cell
10304  * @cfg {String} axis Categorizes cells
10305  * @cfg {String} bgcolor Specifies the background color of a cell
10306  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10307  * @cfg {Number} colspan Specifies the number of columns a cell should span
10308  * @cfg {String} headers Specifies one or more header cells a cell is related to
10309  * @cfg {Number} height Sets the height of a cell
10310  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10311  * @cfg {Number} rowspan Sets the number of rows a cell should span
10312  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10313  * @cfg {String} valign Vertical aligns the content in a cell
10314  * @cfg {Number} width Specifies the width of a cell
10315  * 
10316  * @constructor
10317  * Create a new TableCell
10318  * @param {Object} config The config object
10319  */
10320
10321 Roo.bootstrap.TableCell = function(config){
10322     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10323 };
10324
10325 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10326     
10327     html: false,
10328     cls: false,
10329     tag: false,
10330     abbr: false,
10331     align: false,
10332     axis: false,
10333     bgcolor: false,
10334     charoff: false,
10335     colspan: false,
10336     headers: false,
10337     height: false,
10338     nowrap: false,
10339     rowspan: false,
10340     scope: false,
10341     valign: false,
10342     width: false,
10343     
10344     
10345     getAutoCreate : function(){
10346         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10347         
10348         cfg = {
10349             tag: 'td'
10350         };
10351         
10352         if(this.tag){
10353             cfg.tag = this.tag;
10354         }
10355         
10356         if (this.html) {
10357             cfg.html=this.html
10358         }
10359         if (this.cls) {
10360             cfg.cls=this.cls
10361         }
10362         if (this.abbr) {
10363             cfg.abbr=this.abbr
10364         }
10365         if (this.align) {
10366             cfg.align=this.align
10367         }
10368         if (this.axis) {
10369             cfg.axis=this.axis
10370         }
10371         if (this.bgcolor) {
10372             cfg.bgcolor=this.bgcolor
10373         }
10374         if (this.charoff) {
10375             cfg.charoff=this.charoff
10376         }
10377         if (this.colspan) {
10378             cfg.colspan=this.colspan
10379         }
10380         if (this.headers) {
10381             cfg.headers=this.headers
10382         }
10383         if (this.height) {
10384             cfg.height=this.height
10385         }
10386         if (this.nowrap) {
10387             cfg.nowrap=this.nowrap
10388         }
10389         if (this.rowspan) {
10390             cfg.rowspan=this.rowspan
10391         }
10392         if (this.scope) {
10393             cfg.scope=this.scope
10394         }
10395         if (this.valign) {
10396             cfg.valign=this.valign
10397         }
10398         if (this.width) {
10399             cfg.width=this.width
10400         }
10401         
10402         
10403         return cfg;
10404     }
10405    
10406 });
10407
10408  
10409
10410  /*
10411  * - LGPL
10412  *
10413  * table row
10414  * 
10415  */
10416
10417 /**
10418  * @class Roo.bootstrap.TableRow
10419  * @extends Roo.bootstrap.Component
10420  * Bootstrap TableRow class
10421  * @cfg {String} cls row class
10422  * @cfg {String} align Aligns the content in a table row
10423  * @cfg {String} bgcolor Specifies a background color for a table row
10424  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10425  * @cfg {String} valign Vertical aligns the content in a table row
10426  * 
10427  * @constructor
10428  * Create a new TableRow
10429  * @param {Object} config The config object
10430  */
10431
10432 Roo.bootstrap.TableRow = function(config){
10433     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10434 };
10435
10436 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10437     
10438     cls: false,
10439     align: false,
10440     bgcolor: false,
10441     charoff: false,
10442     valign: false,
10443     
10444     getAutoCreate : function(){
10445         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10446         
10447         cfg = {
10448             tag: 'tr'
10449         };
10450             
10451         if(this.cls){
10452             cfg.cls = this.cls;
10453         }
10454         if(this.align){
10455             cfg.align = this.align;
10456         }
10457         if(this.bgcolor){
10458             cfg.bgcolor = this.bgcolor;
10459         }
10460         if(this.charoff){
10461             cfg.charoff = this.charoff;
10462         }
10463         if(this.valign){
10464             cfg.valign = this.valign;
10465         }
10466         
10467         return cfg;
10468     }
10469    
10470 });
10471
10472  
10473
10474  /*
10475  * - LGPL
10476  *
10477  * table body
10478  * 
10479  */
10480
10481 /**
10482  * @class Roo.bootstrap.TableBody
10483  * @extends Roo.bootstrap.Component
10484  * Bootstrap TableBody class
10485  * @cfg {String} cls element class
10486  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10487  * @cfg {String} align Aligns the content inside the element
10488  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10489  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10490  * 
10491  * @constructor
10492  * Create a new TableBody
10493  * @param {Object} config The config object
10494  */
10495
10496 Roo.bootstrap.TableBody = function(config){
10497     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10498 };
10499
10500 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10501     
10502     cls: false,
10503     tag: false,
10504     align: false,
10505     charoff: false,
10506     valign: false,
10507     
10508     getAutoCreate : function(){
10509         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10510         
10511         cfg = {
10512             tag: 'tbody'
10513         };
10514             
10515         if (this.cls) {
10516             cfg.cls=this.cls
10517         }
10518         if(this.tag){
10519             cfg.tag = this.tag;
10520         }
10521         
10522         if(this.align){
10523             cfg.align = this.align;
10524         }
10525         if(this.charoff){
10526             cfg.charoff = this.charoff;
10527         }
10528         if(this.valign){
10529             cfg.valign = this.valign;
10530         }
10531         
10532         return cfg;
10533     }
10534     
10535     
10536 //    initEvents : function()
10537 //    {
10538 //        
10539 //        if(!this.store){
10540 //            return;
10541 //        }
10542 //        
10543 //        this.store = Roo.factory(this.store, Roo.data);
10544 //        this.store.on('load', this.onLoad, this);
10545 //        
10546 //        this.store.load();
10547 //        
10548 //    },
10549 //    
10550 //    onLoad: function () 
10551 //    {   
10552 //        this.fireEvent('load', this);
10553 //    }
10554 //    
10555 //   
10556 });
10557
10558  
10559
10560  /*
10561  * Based on:
10562  * Ext JS Library 1.1.1
10563  * Copyright(c) 2006-2007, Ext JS, LLC.
10564  *
10565  * Originally Released Under LGPL - original licence link has changed is not relivant.
10566  *
10567  * Fork - LGPL
10568  * <script type="text/javascript">
10569  */
10570
10571 // as we use this in bootstrap.
10572 Roo.namespace('Roo.form');
10573  /**
10574  * @class Roo.form.Action
10575  * Internal Class used to handle form actions
10576  * @constructor
10577  * @param {Roo.form.BasicForm} el The form element or its id
10578  * @param {Object} config Configuration options
10579  */
10580
10581  
10582  
10583 // define the action interface
10584 Roo.form.Action = function(form, options){
10585     this.form = form;
10586     this.options = options || {};
10587 };
10588 /**
10589  * Client Validation Failed
10590  * @const 
10591  */
10592 Roo.form.Action.CLIENT_INVALID = 'client';
10593 /**
10594  * Server Validation Failed
10595  * @const 
10596  */
10597 Roo.form.Action.SERVER_INVALID = 'server';
10598  /**
10599  * Connect to Server Failed
10600  * @const 
10601  */
10602 Roo.form.Action.CONNECT_FAILURE = 'connect';
10603 /**
10604  * Reading Data from Server Failed
10605  * @const 
10606  */
10607 Roo.form.Action.LOAD_FAILURE = 'load';
10608
10609 Roo.form.Action.prototype = {
10610     type : 'default',
10611     failureType : undefined,
10612     response : undefined,
10613     result : undefined,
10614
10615     // interface method
10616     run : function(options){
10617
10618     },
10619
10620     // interface method
10621     success : function(response){
10622
10623     },
10624
10625     // interface method
10626     handleResponse : function(response){
10627
10628     },
10629
10630     // default connection failure
10631     failure : function(response){
10632         
10633         this.response = response;
10634         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10635         this.form.afterAction(this, false);
10636     },
10637
10638     processResponse : function(response){
10639         this.response = response;
10640         if(!response.responseText){
10641             return true;
10642         }
10643         this.result = this.handleResponse(response);
10644         return this.result;
10645     },
10646
10647     // utility functions used internally
10648     getUrl : function(appendParams){
10649         var url = this.options.url || this.form.url || this.form.el.dom.action;
10650         if(appendParams){
10651             var p = this.getParams();
10652             if(p){
10653                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10654             }
10655         }
10656         return url;
10657     },
10658
10659     getMethod : function(){
10660         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10661     },
10662
10663     getParams : function(){
10664         var bp = this.form.baseParams;
10665         var p = this.options.params;
10666         if(p){
10667             if(typeof p == "object"){
10668                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10669             }else if(typeof p == 'string' && bp){
10670                 p += '&' + Roo.urlEncode(bp);
10671             }
10672         }else if(bp){
10673             p = Roo.urlEncode(bp);
10674         }
10675         return p;
10676     },
10677
10678     createCallback : function(){
10679         return {
10680             success: this.success,
10681             failure: this.failure,
10682             scope: this,
10683             timeout: (this.form.timeout*1000),
10684             upload: this.form.fileUpload ? this.success : undefined
10685         };
10686     }
10687 };
10688
10689 Roo.form.Action.Submit = function(form, options){
10690     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10691 };
10692
10693 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10694     type : 'submit',
10695
10696     haveProgress : false,
10697     uploadComplete : false,
10698     
10699     // uploadProgress indicator.
10700     uploadProgress : function()
10701     {
10702         if (!this.form.progressUrl) {
10703             return;
10704         }
10705         
10706         if (!this.haveProgress) {
10707             Roo.MessageBox.progress("Uploading", "Uploading");
10708         }
10709         if (this.uploadComplete) {
10710            Roo.MessageBox.hide();
10711            return;
10712         }
10713         
10714         this.haveProgress = true;
10715    
10716         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10717         
10718         var c = new Roo.data.Connection();
10719         c.request({
10720             url : this.form.progressUrl,
10721             params: {
10722                 id : uid
10723             },
10724             method: 'GET',
10725             success : function(req){
10726                //console.log(data);
10727                 var rdata = false;
10728                 var edata;
10729                 try  {
10730                    rdata = Roo.decode(req.responseText)
10731                 } catch (e) {
10732                     Roo.log("Invalid data from server..");
10733                     Roo.log(edata);
10734                     return;
10735                 }
10736                 if (!rdata || !rdata.success) {
10737                     Roo.log(rdata);
10738                     Roo.MessageBox.alert(Roo.encode(rdata));
10739                     return;
10740                 }
10741                 var data = rdata.data;
10742                 
10743                 if (this.uploadComplete) {
10744                    Roo.MessageBox.hide();
10745                    return;
10746                 }
10747                    
10748                 if (data){
10749                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10750                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10751                     );
10752                 }
10753                 this.uploadProgress.defer(2000,this);
10754             },
10755        
10756             failure: function(data) {
10757                 Roo.log('progress url failed ');
10758                 Roo.log(data);
10759             },
10760             scope : this
10761         });
10762            
10763     },
10764     
10765     
10766     run : function()
10767     {
10768         // run get Values on the form, so it syncs any secondary forms.
10769         this.form.getValues();
10770         
10771         var o = this.options;
10772         var method = this.getMethod();
10773         var isPost = method == 'POST';
10774         if(o.clientValidation === false || this.form.isValid()){
10775             
10776             if (this.form.progressUrl) {
10777                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10778                     (new Date() * 1) + '' + Math.random());
10779                     
10780             } 
10781             
10782             
10783             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10784                 form:this.form.el.dom,
10785                 url:this.getUrl(!isPost),
10786                 method: method,
10787                 params:isPost ? this.getParams() : null,
10788                 isUpload: this.form.fileUpload,
10789                 formData : this.form.formData
10790             }));
10791             
10792             this.uploadProgress();
10793
10794         }else if (o.clientValidation !== false){ // client validation failed
10795             this.failureType = Roo.form.Action.CLIENT_INVALID;
10796             this.form.afterAction(this, false);
10797         }
10798     },
10799
10800     success : function(response)
10801     {
10802         this.uploadComplete= true;
10803         if (this.haveProgress) {
10804             Roo.MessageBox.hide();
10805         }
10806         
10807         
10808         var result = this.processResponse(response);
10809         if(result === true || result.success){
10810             this.form.afterAction(this, true);
10811             return;
10812         }
10813         if(result.errors){
10814             this.form.markInvalid(result.errors);
10815             this.failureType = Roo.form.Action.SERVER_INVALID;
10816         }
10817         this.form.afterAction(this, false);
10818     },
10819     failure : function(response)
10820     {
10821         this.uploadComplete= true;
10822         if (this.haveProgress) {
10823             Roo.MessageBox.hide();
10824         }
10825         
10826         this.response = response;
10827         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10828         this.form.afterAction(this, false);
10829     },
10830     
10831     handleResponse : function(response){
10832         if(this.form.errorReader){
10833             var rs = this.form.errorReader.read(response);
10834             var errors = [];
10835             if(rs.records){
10836                 for(var i = 0, len = rs.records.length; i < len; i++) {
10837                     var r = rs.records[i];
10838                     errors[i] = r.data;
10839                 }
10840             }
10841             if(errors.length < 1){
10842                 errors = null;
10843             }
10844             return {
10845                 success : rs.success,
10846                 errors : errors
10847             };
10848         }
10849         var ret = false;
10850         try {
10851             ret = Roo.decode(response.responseText);
10852         } catch (e) {
10853             ret = {
10854                 success: false,
10855                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10856                 errors : []
10857             };
10858         }
10859         return ret;
10860         
10861     }
10862 });
10863
10864
10865 Roo.form.Action.Load = function(form, options){
10866     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10867     this.reader = this.form.reader;
10868 };
10869
10870 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10871     type : 'load',
10872
10873     run : function(){
10874         
10875         Roo.Ajax.request(Roo.apply(
10876                 this.createCallback(), {
10877                     method:this.getMethod(),
10878                     url:this.getUrl(false),
10879                     params:this.getParams()
10880         }));
10881     },
10882
10883     success : function(response){
10884         
10885         var result = this.processResponse(response);
10886         if(result === true || !result.success || !result.data){
10887             this.failureType = Roo.form.Action.LOAD_FAILURE;
10888             this.form.afterAction(this, false);
10889             return;
10890         }
10891         this.form.clearInvalid();
10892         this.form.setValues(result.data);
10893         this.form.afterAction(this, true);
10894     },
10895
10896     handleResponse : function(response){
10897         if(this.form.reader){
10898             var rs = this.form.reader.read(response);
10899             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10900             return {
10901                 success : rs.success,
10902                 data : data
10903             };
10904         }
10905         return Roo.decode(response.responseText);
10906     }
10907 });
10908
10909 Roo.form.Action.ACTION_TYPES = {
10910     'load' : Roo.form.Action.Load,
10911     'submit' : Roo.form.Action.Submit
10912 };/*
10913  * - LGPL
10914  *
10915  * form
10916  *
10917  */
10918
10919 /**
10920  * @class Roo.bootstrap.Form
10921  * @extends Roo.bootstrap.Component
10922  * @children Roo.bootstrap.Component
10923  * Bootstrap Form class
10924  * @cfg {String} method  GET | POST (default POST)
10925  * @cfg {String} labelAlign top | left (default top)
10926  * @cfg {String} align left  | right - for navbars
10927  * @cfg {Boolean} loadMask load mask when submit (default true)
10928
10929  *
10930  * @constructor
10931  * Create a new Form
10932  * @param {Object} config The config object
10933  */
10934
10935
10936 Roo.bootstrap.Form = function(config){
10937     
10938     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10939     
10940     Roo.bootstrap.Form.popover.apply();
10941     
10942     this.addEvents({
10943         /**
10944          * @event clientvalidation
10945          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10946          * @param {Form} this
10947          * @param {Boolean} valid true if the form has passed client-side validation
10948          */
10949         clientvalidation: true,
10950         /**
10951          * @event beforeaction
10952          * Fires before any action is performed. Return false to cancel the action.
10953          * @param {Form} this
10954          * @param {Action} action The action to be performed
10955          */
10956         beforeaction: true,
10957         /**
10958          * @event actionfailed
10959          * Fires when an action fails.
10960          * @param {Form} this
10961          * @param {Action} action The action that failed
10962          */
10963         actionfailed : true,
10964         /**
10965          * @event actioncomplete
10966          * Fires when an action is completed.
10967          * @param {Form} this
10968          * @param {Action} action The action that completed
10969          */
10970         actioncomplete : true
10971     });
10972 };
10973
10974 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10975
10976      /**
10977      * @cfg {String} method
10978      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10979      */
10980     method : 'POST',
10981     /**
10982      * @cfg {String} url
10983      * The URL to use for form actions if one isn't supplied in the action options.
10984      */
10985     /**
10986      * @cfg {Boolean} fileUpload
10987      * Set to true if this form is a file upload.
10988      */
10989
10990     /**
10991      * @cfg {Object} baseParams
10992      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10993      */
10994
10995     /**
10996      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10997      */
10998     timeout: 30,
10999     /**
11000      * @cfg {Sting} align (left|right) for navbar forms
11001      */
11002     align : 'left',
11003
11004     // private
11005     activeAction : null,
11006
11007     /**
11008      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11009      * element by passing it or its id or mask the form itself by passing in true.
11010      * @type Mixed
11011      */
11012     waitMsgTarget : false,
11013
11014     loadMask : true,
11015     
11016     /**
11017      * @cfg {Boolean} errorMask (true|false) default false
11018      */
11019     errorMask : false,
11020     
11021     /**
11022      * @cfg {Number} maskOffset Default 100
11023      */
11024     maskOffset : 100,
11025     
11026     /**
11027      * @cfg {Boolean} maskBody
11028      */
11029     maskBody : false,
11030
11031     getAutoCreate : function(){
11032
11033         var cfg = {
11034             tag: 'form',
11035             method : this.method || 'POST',
11036             id : this.id || Roo.id(),
11037             cls : ''
11038         };
11039         if (this.parent().xtype.match(/^Nav/)) {
11040             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11041
11042         }
11043
11044         if (this.labelAlign == 'left' ) {
11045             cfg.cls += ' form-horizontal';
11046         }
11047
11048
11049         return cfg;
11050     },
11051     initEvents : function()
11052     {
11053         this.el.on('submit', this.onSubmit, this);
11054         // this was added as random key presses on the form where triggering form submit.
11055         this.el.on('keypress', function(e) {
11056             if (e.getCharCode() != 13) {
11057                 return true;
11058             }
11059             // we might need to allow it for textareas.. and some other items.
11060             // check e.getTarget().
11061
11062             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11063                 return true;
11064             }
11065
11066             Roo.log("keypress blocked");
11067
11068             e.preventDefault();
11069             return false;
11070         });
11071         
11072     },
11073     // private
11074     onSubmit : function(e){
11075         e.stopEvent();
11076     },
11077
11078      /**
11079      * Returns true if client-side validation on the form is successful.
11080      * @return Boolean
11081      */
11082     isValid : function(){
11083         var items = this.getItems();
11084         var valid = true;
11085         var target = false;
11086         
11087         items.each(function(f){
11088             
11089             if(f.validate()){
11090                 return;
11091             }
11092             
11093             Roo.log('invalid field: ' + f.name);
11094             
11095             valid = false;
11096
11097             if(!target && f.el.isVisible(true)){
11098                 target = f;
11099             }
11100            
11101         });
11102         
11103         if(this.errorMask && !valid){
11104             Roo.bootstrap.Form.popover.mask(this, target);
11105         }
11106         
11107         return valid;
11108     },
11109     
11110     /**
11111      * Returns true if any fields in this form have changed since their original load.
11112      * @return Boolean
11113      */
11114     isDirty : function(){
11115         var dirty = false;
11116         var items = this.getItems();
11117         items.each(function(f){
11118            if(f.isDirty()){
11119                dirty = true;
11120                return false;
11121            }
11122            return true;
11123         });
11124         return dirty;
11125     },
11126      /**
11127      * Performs a predefined action (submit or load) or custom actions you define on this form.
11128      * @param {String} actionName The name of the action type
11129      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11130      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11131      * accept other config options):
11132      * <pre>
11133 Property          Type             Description
11134 ----------------  ---------------  ----------------------------------------------------------------------------------
11135 url               String           The url for the action (defaults to the form's url)
11136 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11137 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11138 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11139                                    validate the form on the client (defaults to false)
11140      * </pre>
11141      * @return {BasicForm} this
11142      */
11143     doAction : function(action, options){
11144         if(typeof action == 'string'){
11145             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11146         }
11147         if(this.fireEvent('beforeaction', this, action) !== false){
11148             this.beforeAction(action);
11149             action.run.defer(100, action);
11150         }
11151         return this;
11152     },
11153
11154     // private
11155     beforeAction : function(action){
11156         var o = action.options;
11157         
11158         if(this.loadMask){
11159             
11160             if(this.maskBody){
11161                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11162             } else {
11163                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11164             }
11165         }
11166         // not really supported yet.. ??
11167
11168         //if(this.waitMsgTarget === true){
11169         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11170         //}else if(this.waitMsgTarget){
11171         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11172         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11173         //}else {
11174         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11175        // }
11176
11177     },
11178
11179     // private
11180     afterAction : function(action, success){
11181         this.activeAction = null;
11182         var o = action.options;
11183
11184         if(this.loadMask){
11185             
11186             if(this.maskBody){
11187                 Roo.get(document.body).unmask();
11188             } else {
11189                 this.el.unmask();
11190             }
11191         }
11192         
11193         //if(this.waitMsgTarget === true){
11194 //            this.el.unmask();
11195         //}else if(this.waitMsgTarget){
11196         //    this.waitMsgTarget.unmask();
11197         //}else{
11198         //    Roo.MessageBox.updateProgress(1);
11199         //    Roo.MessageBox.hide();
11200        // }
11201         //
11202         if(success){
11203             if(o.reset){
11204                 this.reset();
11205             }
11206             Roo.callback(o.success, o.scope, [this, action]);
11207             this.fireEvent('actioncomplete', this, action);
11208
11209         }else{
11210
11211             // failure condition..
11212             // we have a scenario where updates need confirming.
11213             // eg. if a locking scenario exists..
11214             // we look for { errors : { needs_confirm : true }} in the response.
11215             if (
11216                 (typeof(action.result) != 'undefined')  &&
11217                 (typeof(action.result.errors) != 'undefined')  &&
11218                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11219            ){
11220                 var _t = this;
11221                 Roo.log("not supported yet");
11222                  /*
11223
11224                 Roo.MessageBox.confirm(
11225                     "Change requires confirmation",
11226                     action.result.errorMsg,
11227                     function(r) {
11228                         if (r != 'yes') {
11229                             return;
11230                         }
11231                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11232                     }
11233
11234                 );
11235                 */
11236
11237
11238                 return;
11239             }
11240
11241             Roo.callback(o.failure, o.scope, [this, action]);
11242             // show an error message if no failed handler is set..
11243             if (!this.hasListener('actionfailed')) {
11244                 Roo.log("need to add dialog support");
11245                 /*
11246                 Roo.MessageBox.alert("Error",
11247                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11248                         action.result.errorMsg :
11249                         "Saving Failed, please check your entries or try again"
11250                 );
11251                 */
11252             }
11253
11254             this.fireEvent('actionfailed', this, action);
11255         }
11256
11257     },
11258     /**
11259      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11260      * @param {String} id The value to search for
11261      * @return Field
11262      */
11263     findField : function(id){
11264         var items = this.getItems();
11265         var field = items.get(id);
11266         if(!field){
11267              items.each(function(f){
11268                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11269                     field = f;
11270                     return false;
11271                 }
11272                 return true;
11273             });
11274         }
11275         return field || null;
11276     },
11277      /**
11278      * Mark fields in this form invalid in bulk.
11279      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11280      * @return {BasicForm} this
11281      */
11282     markInvalid : function(errors){
11283         if(errors instanceof Array){
11284             for(var i = 0, len = errors.length; i < len; i++){
11285                 var fieldError = errors[i];
11286                 var f = this.findField(fieldError.id);
11287                 if(f){
11288                     f.markInvalid(fieldError.msg);
11289                 }
11290             }
11291         }else{
11292             var field, id;
11293             for(id in errors){
11294                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11295                     field.markInvalid(errors[id]);
11296                 }
11297             }
11298         }
11299         //Roo.each(this.childForms || [], function (f) {
11300         //    f.markInvalid(errors);
11301         //});
11302
11303         return this;
11304     },
11305
11306     /**
11307      * Set values for fields in this form in bulk.
11308      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11309      * @return {BasicForm} this
11310      */
11311     setValues : function(values){
11312         if(values instanceof Array){ // array of objects
11313             for(var i = 0, len = values.length; i < len; i++){
11314                 var v = values[i];
11315                 var f = this.findField(v.id);
11316                 if(f){
11317                     f.setValue(v.value);
11318                     if(this.trackResetOnLoad){
11319                         f.originalValue = f.getValue();
11320                     }
11321                 }
11322             }
11323         }else{ // object hash
11324             var field, id;
11325             for(id in values){
11326                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11327
11328                     if (field.setFromData &&
11329                         field.valueField &&
11330                         field.displayField &&
11331                         // combos' with local stores can
11332                         // be queried via setValue()
11333                         // to set their value..
11334                         (field.store && !field.store.isLocal)
11335                         ) {
11336                         // it's a combo
11337                         var sd = { };
11338                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11339                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11340                         field.setFromData(sd);
11341
11342                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11343                         
11344                         field.setFromData(values);
11345                         
11346                     } else {
11347                         field.setValue(values[id]);
11348                     }
11349
11350
11351                     if(this.trackResetOnLoad){
11352                         field.originalValue = field.getValue();
11353                     }
11354                 }
11355             }
11356         }
11357
11358         //Roo.each(this.childForms || [], function (f) {
11359         //    f.setValues(values);
11360         //});
11361
11362         return this;
11363     },
11364
11365     /**
11366      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11367      * they are returned as an array.
11368      * @param {Boolean} asString
11369      * @return {Object}
11370      */
11371     getValues : function(asString){
11372         //if (this.childForms) {
11373             // copy values from the child forms
11374         //    Roo.each(this.childForms, function (f) {
11375         //        this.setValues(f.getValues());
11376         //    }, this);
11377         //}
11378
11379
11380
11381         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11382         if(asString === true){
11383             return fs;
11384         }
11385         return Roo.urlDecode(fs);
11386     },
11387
11388     /**
11389      * Returns the fields in this form as an object with key/value pairs.
11390      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11391      * @return {Object}
11392      */
11393     getFieldValues : function(with_hidden)
11394     {
11395         var items = this.getItems();
11396         var ret = {};
11397         items.each(function(f){
11398             
11399             if (!f.getName()) {
11400                 return;
11401             }
11402             
11403             var v = f.getValue();
11404             
11405             if (f.inputType =='radio') {
11406                 if (typeof(ret[f.getName()]) == 'undefined') {
11407                     ret[f.getName()] = ''; // empty..
11408                 }
11409
11410                 if (!f.el.dom.checked) {
11411                     return;
11412
11413                 }
11414                 v = f.el.dom.value;
11415
11416             }
11417             
11418             if(f.xtype == 'MoneyField'){
11419                 ret[f.currencyName] = f.getCurrency();
11420             }
11421
11422             // not sure if this supported any more..
11423             if ((typeof(v) == 'object') && f.getRawValue) {
11424                 v = f.getRawValue() ; // dates..
11425             }
11426             // combo boxes where name != hiddenName...
11427             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11428                 ret[f.name] = f.getRawValue();
11429             }
11430             ret[f.getName()] = v;
11431         });
11432
11433         return ret;
11434     },
11435
11436     /**
11437      * Clears all invalid messages in this form.
11438      * @return {BasicForm} this
11439      */
11440     clearInvalid : function(){
11441         var items = this.getItems();
11442
11443         items.each(function(f){
11444            f.clearInvalid();
11445         });
11446
11447         return this;
11448     },
11449
11450     /**
11451      * Resets this form.
11452      * @return {BasicForm} this
11453      */
11454     reset : function(){
11455         var items = this.getItems();
11456         items.each(function(f){
11457             f.reset();
11458         });
11459
11460         Roo.each(this.childForms || [], function (f) {
11461             f.reset();
11462         });
11463
11464
11465         return this;
11466     },
11467     
11468     getItems : function()
11469     {
11470         var r=new Roo.util.MixedCollection(false, function(o){
11471             return o.id || (o.id = Roo.id());
11472         });
11473         var iter = function(el) {
11474             if (el.inputEl) {
11475                 r.add(el);
11476             }
11477             if (!el.items) {
11478                 return;
11479             }
11480             Roo.each(el.items,function(e) {
11481                 iter(e);
11482             });
11483         };
11484
11485         iter(this);
11486         return r;
11487     },
11488     
11489     hideFields : function(items)
11490     {
11491         Roo.each(items, function(i){
11492             
11493             var f = this.findField(i);
11494             
11495             if(!f){
11496                 return;
11497             }
11498             
11499             f.hide();
11500             
11501         }, this);
11502     },
11503     
11504     showFields : function(items)
11505     {
11506         Roo.each(items, function(i){
11507             
11508             var f = this.findField(i);
11509             
11510             if(!f){
11511                 return;
11512             }
11513             
11514             f.show();
11515             
11516         }, this);
11517     }
11518
11519 });
11520
11521 Roo.apply(Roo.bootstrap.Form, {
11522     
11523     popover : {
11524         
11525         padding : 5,
11526         
11527         isApplied : false,
11528         
11529         isMasked : false,
11530         
11531         form : false,
11532         
11533         target : false,
11534         
11535         toolTip : false,
11536         
11537         intervalID : false,
11538         
11539         maskEl : false,
11540         
11541         apply : function()
11542         {
11543             if(this.isApplied){
11544                 return;
11545             }
11546             
11547             this.maskEl = {
11548                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11549                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11550                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11551                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11552             };
11553             
11554             this.maskEl.top.enableDisplayMode("block");
11555             this.maskEl.left.enableDisplayMode("block");
11556             this.maskEl.bottom.enableDisplayMode("block");
11557             this.maskEl.right.enableDisplayMode("block");
11558             
11559             this.toolTip = new Roo.bootstrap.Tooltip({
11560                 cls : 'roo-form-error-popover',
11561                 alignment : {
11562                     'left' : ['r-l', [-2,0], 'right'],
11563                     'right' : ['l-r', [2,0], 'left'],
11564                     'bottom' : ['tl-bl', [0,2], 'top'],
11565                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11566                 }
11567             });
11568             
11569             this.toolTip.render(Roo.get(document.body));
11570
11571             this.toolTip.el.enableDisplayMode("block");
11572             
11573             Roo.get(document.body).on('click', function(){
11574                 this.unmask();
11575             }, this);
11576             
11577             Roo.get(document.body).on('touchstart', function(){
11578                 this.unmask();
11579             }, this);
11580             
11581             this.isApplied = true
11582         },
11583         
11584         mask : function(form, target)
11585         {
11586             this.form = form;
11587             
11588             this.target = target;
11589             
11590             if(!this.form.errorMask || !target.el){
11591                 return;
11592             }
11593             
11594             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11595             
11596             Roo.log(scrollable);
11597             
11598             var ot = this.target.el.calcOffsetsTo(scrollable);
11599             
11600             var scrollTo = ot[1] - this.form.maskOffset;
11601             
11602             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11603             
11604             scrollable.scrollTo('top', scrollTo);
11605             
11606             var box = this.target.el.getBox();
11607             Roo.log(box);
11608             var zIndex = Roo.bootstrap.Modal.zIndex++;
11609
11610             
11611             this.maskEl.top.setStyle('position', 'absolute');
11612             this.maskEl.top.setStyle('z-index', zIndex);
11613             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11614             this.maskEl.top.setLeft(0);
11615             this.maskEl.top.setTop(0);
11616             this.maskEl.top.show();
11617             
11618             this.maskEl.left.setStyle('position', 'absolute');
11619             this.maskEl.left.setStyle('z-index', zIndex);
11620             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11621             this.maskEl.left.setLeft(0);
11622             this.maskEl.left.setTop(box.y - this.padding);
11623             this.maskEl.left.show();
11624
11625             this.maskEl.bottom.setStyle('position', 'absolute');
11626             this.maskEl.bottom.setStyle('z-index', zIndex);
11627             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11628             this.maskEl.bottom.setLeft(0);
11629             this.maskEl.bottom.setTop(box.bottom + this.padding);
11630             this.maskEl.bottom.show();
11631
11632             this.maskEl.right.setStyle('position', 'absolute');
11633             this.maskEl.right.setStyle('z-index', zIndex);
11634             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11635             this.maskEl.right.setLeft(box.right + this.padding);
11636             this.maskEl.right.setTop(box.y - this.padding);
11637             this.maskEl.right.show();
11638
11639             this.toolTip.bindEl = this.target.el;
11640
11641             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11642
11643             var tip = this.target.blankText;
11644
11645             if(this.target.getValue() !== '' ) {
11646                 
11647                 if (this.target.invalidText.length) {
11648                     tip = this.target.invalidText;
11649                 } else if (this.target.regexText.length){
11650                     tip = this.target.regexText;
11651                 }
11652             }
11653
11654             this.toolTip.show(tip);
11655
11656             this.intervalID = window.setInterval(function() {
11657                 Roo.bootstrap.Form.popover.unmask();
11658             }, 10000);
11659
11660             window.onwheel = function(){ return false;};
11661             
11662             (function(){ this.isMasked = true; }).defer(500, this);
11663             
11664         },
11665         
11666         unmask : function()
11667         {
11668             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11669                 return;
11670             }
11671             
11672             this.maskEl.top.setStyle('position', 'absolute');
11673             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11674             this.maskEl.top.hide();
11675
11676             this.maskEl.left.setStyle('position', 'absolute');
11677             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11678             this.maskEl.left.hide();
11679
11680             this.maskEl.bottom.setStyle('position', 'absolute');
11681             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11682             this.maskEl.bottom.hide();
11683
11684             this.maskEl.right.setStyle('position', 'absolute');
11685             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11686             this.maskEl.right.hide();
11687             
11688             this.toolTip.hide();
11689             
11690             this.toolTip.el.hide();
11691             
11692             window.onwheel = function(){ return true;};
11693             
11694             if(this.intervalID){
11695                 window.clearInterval(this.intervalID);
11696                 this.intervalID = false;
11697             }
11698             
11699             this.isMasked = false;
11700             
11701         }
11702         
11703     }
11704     
11705 });
11706
11707 /*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717 /**
11718  * @class Roo.form.VTypes
11719  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11720  * @singleton
11721  */
11722 Roo.form.VTypes = function(){
11723     // closure these in so they are only created once.
11724     var alpha = /^[a-zA-Z_]+$/;
11725     var alphanum = /^[a-zA-Z0-9_]+$/;
11726     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11727     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11728
11729     // All these messages and functions are configurable
11730     return {
11731         /**
11732          * The function used to validate email addresses
11733          * @param {String} value The email address
11734          */
11735         'email' : function(v){
11736             return email.test(v);
11737         },
11738         /**
11739          * The error text to display when the email validation function returns false
11740          * @type String
11741          */
11742         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11743         /**
11744          * The keystroke filter mask to be applied on email input
11745          * @type RegExp
11746          */
11747         'emailMask' : /[a-z0-9_\.\-@]/i,
11748
11749         /**
11750          * The function used to validate URLs
11751          * @param {String} value The URL
11752          */
11753         'url' : function(v){
11754             return url.test(v);
11755         },
11756         /**
11757          * The error text to display when the url validation function returns false
11758          * @type String
11759          */
11760         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11761         
11762         /**
11763          * The function used to validate alpha values
11764          * @param {String} value The value
11765          */
11766         'alpha' : function(v){
11767             return alpha.test(v);
11768         },
11769         /**
11770          * The error text to display when the alpha validation function returns false
11771          * @type String
11772          */
11773         'alphaText' : 'This field should only contain letters and _',
11774         /**
11775          * The keystroke filter mask to be applied on alpha input
11776          * @type RegExp
11777          */
11778         'alphaMask' : /[a-z_]/i,
11779
11780         /**
11781          * The function used to validate alphanumeric values
11782          * @param {String} value The value
11783          */
11784         'alphanum' : function(v){
11785             return alphanum.test(v);
11786         },
11787         /**
11788          * The error text to display when the alphanumeric validation function returns false
11789          * @type String
11790          */
11791         'alphanumText' : 'This field should only contain letters, numbers and _',
11792         /**
11793          * The keystroke filter mask to be applied on alphanumeric input
11794          * @type RegExp
11795          */
11796         'alphanumMask' : /[a-z0-9_]/i
11797     };
11798 }();/*
11799  * - LGPL
11800  *
11801  * Input
11802  * 
11803  */
11804
11805 /**
11806  * @class Roo.bootstrap.Input
11807  * @extends Roo.bootstrap.Component
11808  * Bootstrap Input class
11809  * @cfg {Boolean} disabled is it disabled
11810  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11811  * @cfg {String} name name of the input
11812  * @cfg {string} fieldLabel - the label associated
11813  * @cfg {string} placeholder - placeholder to put in text.
11814  * @cfg {string}  before - input group add on before
11815  * @cfg {string} after - input group add on after
11816  * @cfg {string} size - (lg|sm) or leave empty..
11817  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11818  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11819  * @cfg {Number} md colspan out of 12 for computer-sized screens
11820  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11821  * @cfg {string} value default value of the input
11822  * @cfg {Number} labelWidth set the width of label 
11823  * @cfg {Number} labellg set the width of label (1-12)
11824  * @cfg {Number} labelmd set the width of label (1-12)
11825  * @cfg {Number} labelsm set the width of label (1-12)
11826  * @cfg {Number} labelxs set the width of label (1-12)
11827  * @cfg {String} labelAlign (top|left)
11828  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11829  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11830  * @cfg {String} indicatorpos (left|right) default left
11831  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11832  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11833  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11834
11835  * @cfg {String} align (left|center|right) Default left
11836  * @cfg {Boolean} forceFeedback (true|false) Default false
11837  * 
11838  * @constructor
11839  * Create a new Input
11840  * @param {Object} config The config object
11841  */
11842
11843 Roo.bootstrap.Input = function(config){
11844     
11845     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11846     
11847     this.addEvents({
11848         /**
11849          * @event focus
11850          * Fires when this field receives input focus.
11851          * @param {Roo.form.Field} this
11852          */
11853         focus : true,
11854         /**
11855          * @event blur
11856          * Fires when this field loses input focus.
11857          * @param {Roo.form.Field} this
11858          */
11859         blur : true,
11860         /**
11861          * @event specialkey
11862          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11863          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11864          * @param {Roo.form.Field} this
11865          * @param {Roo.EventObject} e The event object
11866          */
11867         specialkey : true,
11868         /**
11869          * @event change
11870          * Fires just before the field blurs if the field value has changed.
11871          * @param {Roo.form.Field} this
11872          * @param {Mixed} newValue The new value
11873          * @param {Mixed} oldValue The original value
11874          */
11875         change : true,
11876         /**
11877          * @event invalid
11878          * Fires after the field has been marked as invalid.
11879          * @param {Roo.form.Field} this
11880          * @param {String} msg The validation message
11881          */
11882         invalid : true,
11883         /**
11884          * @event valid
11885          * Fires after the field has been validated with no errors.
11886          * @param {Roo.form.Field} this
11887          */
11888         valid : true,
11889          /**
11890          * @event keyup
11891          * Fires after the key up
11892          * @param {Roo.form.Field} this
11893          * @param {Roo.EventObject}  e The event Object
11894          */
11895         keyup : true,
11896         /**
11897          * @event paste
11898          * Fires after the user pastes into input
11899          * @param {Roo.form.Field} this
11900          * @param {Roo.EventObject}  e The event Object
11901          */
11902         paste : true
11903     });
11904 };
11905
11906 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11907      /**
11908      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11909       automatic validation (defaults to "keyup").
11910      */
11911     validationEvent : "keyup",
11912      /**
11913      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11914      */
11915     validateOnBlur : true,
11916     /**
11917      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11918      */
11919     validationDelay : 250,
11920      /**
11921      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11922      */
11923     focusClass : "x-form-focus",  // not needed???
11924     
11925        
11926     /**
11927      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11928      */
11929     invalidClass : "has-warning",
11930     
11931     /**
11932      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11933      */
11934     validClass : "has-success",
11935     
11936     /**
11937      * @cfg {Boolean} hasFeedback (true|false) default true
11938      */
11939     hasFeedback : true,
11940     
11941     /**
11942      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11943      */
11944     invalidFeedbackClass : "glyphicon-warning-sign",
11945     
11946     /**
11947      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11948      */
11949     validFeedbackClass : "glyphicon-ok",
11950     
11951     /**
11952      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11953      */
11954     selectOnFocus : false,
11955     
11956      /**
11957      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11958      */
11959     maskRe : null,
11960        /**
11961      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11962      */
11963     vtype : null,
11964     
11965       /**
11966      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11967      */
11968     disableKeyFilter : false,
11969     
11970        /**
11971      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11972      */
11973     disabled : false,
11974      /**
11975      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11976      */
11977     allowBlank : true,
11978     /**
11979      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11980      */
11981     blankText : "Please complete this mandatory field",
11982     
11983      /**
11984      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11985      */
11986     minLength : 0,
11987     /**
11988      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11989      */
11990     maxLength : Number.MAX_VALUE,
11991     /**
11992      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11993      */
11994     minLengthText : "The minimum length for this field is {0}",
11995     /**
11996      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11997      */
11998     maxLengthText : "The maximum length for this field is {0}",
11999   
12000     
12001     /**
12002      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12003      * If available, this function will be called only after the basic validators all return true, and will be passed the
12004      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12005      */
12006     validator : null,
12007     /**
12008      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12009      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12010      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12011      */
12012     regex : null,
12013     /**
12014      * @cfg {String} regexText -- Depricated - use Invalid Text
12015      */
12016     regexText : "",
12017     
12018     /**
12019      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12020      */
12021     invalidText : "",
12022     
12023     
12024     
12025     autocomplete: false,
12026     
12027     
12028     fieldLabel : '',
12029     inputType : 'text',
12030     
12031     name : false,
12032     placeholder: false,
12033     before : false,
12034     after : false,
12035     size : false,
12036     hasFocus : false,
12037     preventMark: false,
12038     isFormField : true,
12039     value : '',
12040     labelWidth : 2,
12041     labelAlign : false,
12042     readOnly : false,
12043     align : false,
12044     formatedValue : false,
12045     forceFeedback : false,
12046     
12047     indicatorpos : 'left',
12048     
12049     labellg : 0,
12050     labelmd : 0,
12051     labelsm : 0,
12052     labelxs : 0,
12053     
12054     capture : '',
12055     accept : '',
12056     
12057     parentLabelAlign : function()
12058     {
12059         var parent = this;
12060         while (parent.parent()) {
12061             parent = parent.parent();
12062             if (typeof(parent.labelAlign) !='undefined') {
12063                 return parent.labelAlign;
12064             }
12065         }
12066         return 'left';
12067         
12068     },
12069     
12070     getAutoCreate : function()
12071     {
12072         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12073         
12074         var id = Roo.id();
12075         
12076         var cfg = {};
12077         
12078         if(this.inputType != 'hidden'){
12079             cfg.cls = 'form-group' //input-group
12080         }
12081         
12082         var input =  {
12083             tag: 'input',
12084             id : id,
12085             type : this.inputType,
12086             value : this.value,
12087             cls : 'form-control',
12088             placeholder : this.placeholder || '',
12089             autocomplete : this.autocomplete || 'new-password'
12090         };
12091         if (this.inputType == 'file') {
12092             input.style = 'overflow:hidden'; // why not in CSS?
12093         }
12094         
12095         if(this.capture.length){
12096             input.capture = this.capture;
12097         }
12098         
12099         if(this.accept.length){
12100             input.accept = this.accept + "/*";
12101         }
12102         
12103         if(this.align){
12104             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12105         }
12106         
12107         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12108             input.maxLength = this.maxLength;
12109         }
12110         
12111         if (this.disabled) {
12112             input.disabled=true;
12113         }
12114         
12115         if (this.readOnly) {
12116             input.readonly=true;
12117         }
12118         
12119         if (this.name) {
12120             input.name = this.name;
12121         }
12122         
12123         if (this.size) {
12124             input.cls += ' input-' + this.size;
12125         }
12126         
12127         var settings=this;
12128         ['xs','sm','md','lg'].map(function(size){
12129             if (settings[size]) {
12130                 cfg.cls += ' col-' + size + '-' + settings[size];
12131             }
12132         });
12133         
12134         var inputblock = input;
12135         
12136         var feedback = {
12137             tag: 'span',
12138             cls: 'glyphicon form-control-feedback'
12139         };
12140             
12141         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12142             
12143             inputblock = {
12144                 cls : 'has-feedback',
12145                 cn :  [
12146                     input,
12147                     feedback
12148                 ] 
12149             };  
12150         }
12151         
12152         if (this.before || this.after) {
12153             
12154             inputblock = {
12155                 cls : 'input-group',
12156                 cn :  [] 
12157             };
12158             
12159             if (this.before && typeof(this.before) == 'string') {
12160                 
12161                 inputblock.cn.push({
12162                     tag :'span',
12163                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12164                     html : this.before
12165                 });
12166             }
12167             if (this.before && typeof(this.before) == 'object') {
12168                 this.before = Roo.factory(this.before);
12169                 
12170                 inputblock.cn.push({
12171                     tag :'span',
12172                     cls : 'roo-input-before input-group-prepend   input-group-' +
12173                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12174                 });
12175             }
12176             
12177             inputblock.cn.push(input);
12178             
12179             if (this.after && typeof(this.after) == 'string') {
12180                 inputblock.cn.push({
12181                     tag :'span',
12182                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12183                     html : this.after
12184                 });
12185             }
12186             if (this.after && typeof(this.after) == 'object') {
12187                 this.after = Roo.factory(this.after);
12188                 
12189                 inputblock.cn.push({
12190                     tag :'span',
12191                     cls : 'roo-input-after input-group-append  input-group-' +
12192                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12193                 });
12194             }
12195             
12196             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12197                 inputblock.cls += ' has-feedback';
12198                 inputblock.cn.push(feedback);
12199             }
12200         };
12201         var indicator = {
12202             tag : 'i',
12203             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12204             tooltip : 'This field is required'
12205         };
12206         if (this.allowBlank ) {
12207             indicator.style = this.allowBlank ? ' display:none' : '';
12208         }
12209         if (align ==='left' && this.fieldLabel.length) {
12210             
12211             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12212             
12213             cfg.cn = [
12214                 indicator,
12215                 {
12216                     tag: 'label',
12217                     'for' :  id,
12218                     cls : 'control-label col-form-label',
12219                     html : this.fieldLabel
12220
12221                 },
12222                 {
12223                     cls : "", 
12224                     cn: [
12225                         inputblock
12226                     ]
12227                 }
12228             ];
12229             
12230             var labelCfg = cfg.cn[1];
12231             var contentCfg = cfg.cn[2];
12232             
12233             if(this.indicatorpos == 'right'){
12234                 cfg.cn = [
12235                     {
12236                         tag: 'label',
12237                         'for' :  id,
12238                         cls : 'control-label col-form-label',
12239                         cn : [
12240                             {
12241                                 tag : 'span',
12242                                 html : this.fieldLabel
12243                             },
12244                             indicator
12245                         ]
12246                     },
12247                     {
12248                         cls : "",
12249                         cn: [
12250                             inputblock
12251                         ]
12252                     }
12253
12254                 ];
12255                 
12256                 labelCfg = cfg.cn[0];
12257                 contentCfg = cfg.cn[1];
12258             
12259             }
12260             
12261             if(this.labelWidth > 12){
12262                 labelCfg.style = "width: " + this.labelWidth + 'px';
12263             }
12264             
12265             if(this.labelWidth < 13 && this.labelmd == 0){
12266                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12267             }
12268             
12269             if(this.labellg > 0){
12270                 labelCfg.cls += ' col-lg-' + this.labellg;
12271                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12272             }
12273             
12274             if(this.labelmd > 0){
12275                 labelCfg.cls += ' col-md-' + this.labelmd;
12276                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12277             }
12278             
12279             if(this.labelsm > 0){
12280                 labelCfg.cls += ' col-sm-' + this.labelsm;
12281                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12282             }
12283             
12284             if(this.labelxs > 0){
12285                 labelCfg.cls += ' col-xs-' + this.labelxs;
12286                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12287             }
12288             
12289             
12290         } else if ( this.fieldLabel.length) {
12291                 
12292             
12293             
12294             cfg.cn = [
12295                 {
12296                     tag : 'i',
12297                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12298                     tooltip : 'This field is required',
12299                     style : this.allowBlank ? ' display:none' : '' 
12300                 },
12301                 {
12302                     tag: 'label',
12303                    //cls : 'input-group-addon',
12304                     html : this.fieldLabel
12305
12306                 },
12307
12308                inputblock
12309
12310            ];
12311            
12312            if(this.indicatorpos == 'right'){
12313        
12314                 cfg.cn = [
12315                     {
12316                         tag: 'label',
12317                        //cls : 'input-group-addon',
12318                         html : this.fieldLabel
12319
12320                     },
12321                     {
12322                         tag : 'i',
12323                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12324                         tooltip : 'This field is required',
12325                         style : this.allowBlank ? ' display:none' : '' 
12326                     },
12327
12328                    inputblock
12329
12330                ];
12331
12332             }
12333
12334         } else {
12335             
12336             cfg.cn = [
12337
12338                     inputblock
12339
12340             ];
12341                 
12342                 
12343         };
12344         
12345         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12346            cfg.cls += ' navbar-form';
12347         }
12348         
12349         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12350             // on BS4 we do this only if not form 
12351             cfg.cls += ' navbar-form';
12352             cfg.tag = 'li';
12353         }
12354         
12355         return cfg;
12356         
12357     },
12358     /**
12359      * return the real input element.
12360      */
12361     inputEl: function ()
12362     {
12363         return this.el.select('input.form-control',true).first();
12364     },
12365     
12366     tooltipEl : function()
12367     {
12368         return this.inputEl();
12369     },
12370     
12371     indicatorEl : function()
12372     {
12373         if (Roo.bootstrap.version == 4) {
12374             return false; // not enabled in v4 yet.
12375         }
12376         
12377         var indicator = this.el.select('i.roo-required-indicator',true).first();
12378         
12379         if(!indicator){
12380             return false;
12381         }
12382         
12383         return indicator;
12384         
12385     },
12386     
12387     setDisabled : function(v)
12388     {
12389         var i  = this.inputEl().dom;
12390         if (!v) {
12391             i.removeAttribute('disabled');
12392             return;
12393             
12394         }
12395         i.setAttribute('disabled','true');
12396     },
12397     initEvents : function()
12398     {
12399           
12400         this.inputEl().on("keydown" , this.fireKey,  this);
12401         this.inputEl().on("focus", this.onFocus,  this);
12402         this.inputEl().on("blur", this.onBlur,  this);
12403         
12404         this.inputEl().relayEvent('keyup', this);
12405         this.inputEl().relayEvent('paste', this);
12406         
12407         this.indicator = this.indicatorEl();
12408         
12409         if(this.indicator){
12410             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12411         }
12412  
12413         // reference to original value for reset
12414         this.originalValue = this.getValue();
12415         //Roo.form.TextField.superclass.initEvents.call(this);
12416         if(this.validationEvent == 'keyup'){
12417             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12418             this.inputEl().on('keyup', this.filterValidation, this);
12419         }
12420         else if(this.validationEvent !== false){
12421             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12422         }
12423         
12424         if(this.selectOnFocus){
12425             this.on("focus", this.preFocus, this);
12426             
12427         }
12428         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12429             this.inputEl().on("keypress", this.filterKeys, this);
12430         } else {
12431             this.inputEl().relayEvent('keypress', this);
12432         }
12433        /* if(this.grow){
12434             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12435             this.el.on("click", this.autoSize,  this);
12436         }
12437         */
12438         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12439             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12440         }
12441         
12442         if (typeof(this.before) == 'object') {
12443             this.before.render(this.el.select('.roo-input-before',true).first());
12444         }
12445         if (typeof(this.after) == 'object') {
12446             this.after.render(this.el.select('.roo-input-after',true).first());
12447         }
12448         
12449         this.inputEl().on('change', this.onChange, this);
12450         
12451     },
12452     filterValidation : function(e){
12453         if(!e.isNavKeyPress()){
12454             this.validationTask.delay(this.validationDelay);
12455         }
12456     },
12457      /**
12458      * Validates the field value
12459      * @return {Boolean} True if the value is valid, else false
12460      */
12461     validate : function(){
12462         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12463         if(this.disabled || this.validateValue(this.getRawValue())){
12464             this.markValid();
12465             return true;
12466         }
12467         
12468         this.markInvalid();
12469         return false;
12470     },
12471     
12472     
12473     /**
12474      * Validates a value according to the field's validation rules and marks the field as invalid
12475      * if the validation fails
12476      * @param {Mixed} value The value to validate
12477      * @return {Boolean} True if the value is valid, else false
12478      */
12479     validateValue : function(value)
12480     {
12481         if(this.getVisibilityEl().hasClass('hidden')){
12482             return true;
12483         }
12484         
12485         if(value.length < 1)  { // if it's blank
12486             if(this.allowBlank){
12487                 return true;
12488             }
12489             return false;
12490         }
12491         
12492         if(value.length < this.minLength){
12493             return false;
12494         }
12495         if(value.length > this.maxLength){
12496             return false;
12497         }
12498         if(this.vtype){
12499             var vt = Roo.form.VTypes;
12500             if(!vt[this.vtype](value, this)){
12501                 return false;
12502             }
12503         }
12504         if(typeof this.validator == "function"){
12505             var msg = this.validator(value);
12506             if(msg !== true){
12507                 return false;
12508             }
12509             if (typeof(msg) == 'string') {
12510                 this.invalidText = msg;
12511             }
12512         }
12513         
12514         if(this.regex && !this.regex.test(value)){
12515             return false;
12516         }
12517         
12518         return true;
12519     },
12520     
12521      // private
12522     fireKey : function(e){
12523         //Roo.log('field ' + e.getKey());
12524         if(e.isNavKeyPress()){
12525             this.fireEvent("specialkey", this, e);
12526         }
12527     },
12528     focus : function (selectText){
12529         if(this.rendered){
12530             this.inputEl().focus();
12531             if(selectText === true){
12532                 this.inputEl().dom.select();
12533             }
12534         }
12535         return this;
12536     } ,
12537     
12538     onFocus : function(){
12539         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12540            // this.el.addClass(this.focusClass);
12541         }
12542         if(!this.hasFocus){
12543             this.hasFocus = true;
12544             this.startValue = this.getValue();
12545             this.fireEvent("focus", this);
12546         }
12547     },
12548     
12549     beforeBlur : Roo.emptyFn,
12550
12551     
12552     // private
12553     onBlur : function(){
12554         this.beforeBlur();
12555         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12556             //this.el.removeClass(this.focusClass);
12557         }
12558         this.hasFocus = false;
12559         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12560             this.validate();
12561         }
12562         var v = this.getValue();
12563         if(String(v) !== String(this.startValue)){
12564             this.fireEvent('change', this, v, this.startValue);
12565         }
12566         this.fireEvent("blur", this);
12567     },
12568     
12569     onChange : function(e)
12570     {
12571         var v = this.getValue();
12572         if(String(v) !== String(this.startValue)){
12573             this.fireEvent('change', this, v, this.startValue);
12574         }
12575         
12576     },
12577     
12578     /**
12579      * Resets the current field value to the originally loaded value and clears any validation messages
12580      */
12581     reset : function(){
12582         this.setValue(this.originalValue);
12583         this.validate();
12584     },
12585      /**
12586      * Returns the name of the field
12587      * @return {Mixed} name The name field
12588      */
12589     getName: function(){
12590         return this.name;
12591     },
12592      /**
12593      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12594      * @return {Mixed} value The field value
12595      */
12596     getValue : function(){
12597         
12598         var v = this.inputEl().getValue();
12599         
12600         return v;
12601     },
12602     /**
12603      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12604      * @return {Mixed} value The field value
12605      */
12606     getRawValue : function(){
12607         var v = this.inputEl().getValue();
12608         
12609         return v;
12610     },
12611     
12612     /**
12613      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12614      * @param {Mixed} value The value to set
12615      */
12616     setRawValue : function(v){
12617         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12618     },
12619     
12620     selectText : function(start, end){
12621         var v = this.getRawValue();
12622         if(v.length > 0){
12623             start = start === undefined ? 0 : start;
12624             end = end === undefined ? v.length : end;
12625             var d = this.inputEl().dom;
12626             if(d.setSelectionRange){
12627                 d.setSelectionRange(start, end);
12628             }else if(d.createTextRange){
12629                 var range = d.createTextRange();
12630                 range.moveStart("character", start);
12631                 range.moveEnd("character", v.length-end);
12632                 range.select();
12633             }
12634         }
12635     },
12636     
12637     /**
12638      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12639      * @param {Mixed} value The value to set
12640      */
12641     setValue : function(v){
12642         this.value = v;
12643         if(this.rendered){
12644             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12645             this.validate();
12646         }
12647     },
12648     
12649     /*
12650     processValue : function(value){
12651         if(this.stripCharsRe){
12652             var newValue = value.replace(this.stripCharsRe, '');
12653             if(newValue !== value){
12654                 this.setRawValue(newValue);
12655                 return newValue;
12656             }
12657         }
12658         return value;
12659     },
12660   */
12661     preFocus : function(){
12662         
12663         if(this.selectOnFocus){
12664             this.inputEl().dom.select();
12665         }
12666     },
12667     filterKeys : function(e){
12668         var k = e.getKey();
12669         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12670             return;
12671         }
12672         var c = e.getCharCode(), cc = String.fromCharCode(c);
12673         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12674             return;
12675         }
12676         if(!this.maskRe.test(cc)){
12677             e.stopEvent();
12678         }
12679     },
12680      /**
12681      * Clear any invalid styles/messages for this field
12682      */
12683     clearInvalid : function(){
12684         
12685         if(!this.el || this.preventMark){ // not rendered
12686             return;
12687         }
12688         
12689         
12690         this.el.removeClass([this.invalidClass, 'is-invalid']);
12691         
12692         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12693             
12694             var feedback = this.el.select('.form-control-feedback', true).first();
12695             
12696             if(feedback){
12697                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12698             }
12699             
12700         }
12701         
12702         if(this.indicator){
12703             this.indicator.removeClass('visible');
12704             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12705         }
12706         
12707         this.fireEvent('valid', this);
12708     },
12709     
12710      /**
12711      * Mark this field as valid
12712      */
12713     markValid : function()
12714     {
12715         if(!this.el  || this.preventMark){ // not rendered...
12716             return;
12717         }
12718         
12719         this.el.removeClass([this.invalidClass, this.validClass]);
12720         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12721
12722         var feedback = this.el.select('.form-control-feedback', true).first();
12723             
12724         if(feedback){
12725             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12726         }
12727         
12728         if(this.indicator){
12729             this.indicator.removeClass('visible');
12730             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12731         }
12732         
12733         if(this.disabled){
12734             return;
12735         }
12736         
12737            
12738         if(this.allowBlank && !this.getRawValue().length){
12739             return;
12740         }
12741         if (Roo.bootstrap.version == 3) {
12742             this.el.addClass(this.validClass);
12743         } else {
12744             this.inputEl().addClass('is-valid');
12745         }
12746
12747         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12748             
12749             var feedback = this.el.select('.form-control-feedback', true).first();
12750             
12751             if(feedback){
12752                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12753                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12754             }
12755             
12756         }
12757         
12758         this.fireEvent('valid', this);
12759     },
12760     
12761      /**
12762      * Mark this field as invalid
12763      * @param {String} msg The validation message
12764      */
12765     markInvalid : function(msg)
12766     {
12767         if(!this.el  || this.preventMark){ // not rendered
12768             return;
12769         }
12770         
12771         this.el.removeClass([this.invalidClass, this.validClass]);
12772         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12773         
12774         var feedback = this.el.select('.form-control-feedback', true).first();
12775             
12776         if(feedback){
12777             this.el.select('.form-control-feedback', true).first().removeClass(
12778                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12779         }
12780
12781         if(this.disabled){
12782             return;
12783         }
12784         
12785         if(this.allowBlank && !this.getRawValue().length){
12786             return;
12787         }
12788         
12789         if(this.indicator){
12790             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12791             this.indicator.addClass('visible');
12792         }
12793         if (Roo.bootstrap.version == 3) {
12794             this.el.addClass(this.invalidClass);
12795         } else {
12796             this.inputEl().addClass('is-invalid');
12797         }
12798         
12799         
12800         
12801         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12802             
12803             var feedback = this.el.select('.form-control-feedback', true).first();
12804             
12805             if(feedback){
12806                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12807                 
12808                 if(this.getValue().length || this.forceFeedback){
12809                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12810                 }
12811                 
12812             }
12813             
12814         }
12815         
12816         this.fireEvent('invalid', this, msg);
12817     },
12818     // private
12819     SafariOnKeyDown : function(event)
12820     {
12821         // this is a workaround for a password hang bug on chrome/ webkit.
12822         if (this.inputEl().dom.type != 'password') {
12823             return;
12824         }
12825         
12826         var isSelectAll = false;
12827         
12828         if(this.inputEl().dom.selectionEnd > 0){
12829             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12830         }
12831         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12832             event.preventDefault();
12833             this.setValue('');
12834             return;
12835         }
12836         
12837         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12838             
12839             event.preventDefault();
12840             // this is very hacky as keydown always get's upper case.
12841             //
12842             var cc = String.fromCharCode(event.getCharCode());
12843             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12844             
12845         }
12846     },
12847     adjustWidth : function(tag, w){
12848         tag = tag.toLowerCase();
12849         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12850             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12851                 if(tag == 'input'){
12852                     return w + 2;
12853                 }
12854                 if(tag == 'textarea'){
12855                     return w-2;
12856                 }
12857             }else if(Roo.isOpera){
12858                 if(tag == 'input'){
12859                     return w + 2;
12860                 }
12861                 if(tag == 'textarea'){
12862                     return w-2;
12863                 }
12864             }
12865         }
12866         return w;
12867     },
12868     
12869     setFieldLabel : function(v)
12870     {
12871         if(!this.rendered){
12872             return;
12873         }
12874         
12875         if(this.indicatorEl()){
12876             var ar = this.el.select('label > span',true);
12877             
12878             if (ar.elements.length) {
12879                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12880                 this.fieldLabel = v;
12881                 return;
12882             }
12883             
12884             var br = this.el.select('label',true);
12885             
12886             if(br.elements.length) {
12887                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12888                 this.fieldLabel = v;
12889                 return;
12890             }
12891             
12892             Roo.log('Cannot Found any of label > span || label in input');
12893             return;
12894         }
12895         
12896         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12897         this.fieldLabel = v;
12898         
12899         
12900     }
12901 });
12902
12903  
12904 /*
12905  * - LGPL
12906  *
12907  * Input
12908  * 
12909  */
12910
12911 /**
12912  * @class Roo.bootstrap.TextArea
12913  * @extends Roo.bootstrap.Input
12914  * Bootstrap TextArea class
12915  * @cfg {Number} cols Specifies the visible width of a text area
12916  * @cfg {Number} rows Specifies the visible number of lines in a text area
12917  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12918  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12919  * @cfg {string} html text
12920  * 
12921  * @constructor
12922  * Create a new TextArea
12923  * @param {Object} config The config object
12924  */
12925
12926 Roo.bootstrap.TextArea = function(config){
12927     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12928    
12929 };
12930
12931 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12932      
12933     cols : false,
12934     rows : 5,
12935     readOnly : false,
12936     warp : 'soft',
12937     resize : false,
12938     value: false,
12939     html: false,
12940     
12941     getAutoCreate : function(){
12942         
12943         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12944         
12945         var id = Roo.id();
12946         
12947         var cfg = {};
12948         
12949         if(this.inputType != 'hidden'){
12950             cfg.cls = 'form-group' //input-group
12951         }
12952         
12953         var input =  {
12954             tag: 'textarea',
12955             id : id,
12956             warp : this.warp,
12957             rows : this.rows,
12958             value : this.value || '',
12959             html: this.html || '',
12960             cls : 'form-control',
12961             placeholder : this.placeholder || '' 
12962             
12963         };
12964         
12965         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12966             input.maxLength = this.maxLength;
12967         }
12968         
12969         if(this.resize){
12970             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12971         }
12972         
12973         if(this.cols){
12974             input.cols = this.cols;
12975         }
12976         
12977         if (this.readOnly) {
12978             input.readonly = true;
12979         }
12980         
12981         if (this.name) {
12982             input.name = this.name;
12983         }
12984         
12985         if (this.size) {
12986             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12987         }
12988         
12989         var settings=this;
12990         ['xs','sm','md','lg'].map(function(size){
12991             if (settings[size]) {
12992                 cfg.cls += ' col-' + size + '-' + settings[size];
12993             }
12994         });
12995         
12996         var inputblock = input;
12997         
12998         if(this.hasFeedback && !this.allowBlank){
12999             
13000             var feedback = {
13001                 tag: 'span',
13002                 cls: 'glyphicon form-control-feedback'
13003             };
13004
13005             inputblock = {
13006                 cls : 'has-feedback',
13007                 cn :  [
13008                     input,
13009                     feedback
13010                 ] 
13011             };  
13012         }
13013         
13014         
13015         if (this.before || this.after) {
13016             
13017             inputblock = {
13018                 cls : 'input-group',
13019                 cn :  [] 
13020             };
13021             if (this.before) {
13022                 inputblock.cn.push({
13023                     tag :'span',
13024                     cls : 'input-group-addon',
13025                     html : this.before
13026                 });
13027             }
13028             
13029             inputblock.cn.push(input);
13030             
13031             if(this.hasFeedback && !this.allowBlank){
13032                 inputblock.cls += ' has-feedback';
13033                 inputblock.cn.push(feedback);
13034             }
13035             
13036             if (this.after) {
13037                 inputblock.cn.push({
13038                     tag :'span',
13039                     cls : 'input-group-addon',
13040                     html : this.after
13041                 });
13042             }
13043             
13044         }
13045         
13046         if (align ==='left' && this.fieldLabel.length) {
13047             cfg.cn = [
13048                 {
13049                     tag: 'label',
13050                     'for' :  id,
13051                     cls : 'control-label',
13052                     html : this.fieldLabel
13053                 },
13054                 {
13055                     cls : "",
13056                     cn: [
13057                         inputblock
13058                     ]
13059                 }
13060
13061             ];
13062             
13063             if(this.labelWidth > 12){
13064                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13065             }
13066
13067             if(this.labelWidth < 13 && this.labelmd == 0){
13068                 this.labelmd = this.labelWidth;
13069             }
13070
13071             if(this.labellg > 0){
13072                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13073                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13074             }
13075
13076             if(this.labelmd > 0){
13077                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13078                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13079             }
13080
13081             if(this.labelsm > 0){
13082                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13083                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13084             }
13085
13086             if(this.labelxs > 0){
13087                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13088                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13089             }
13090             
13091         } else if ( this.fieldLabel.length) {
13092             cfg.cn = [
13093
13094                {
13095                    tag: 'label',
13096                    //cls : 'input-group-addon',
13097                    html : this.fieldLabel
13098
13099                },
13100
13101                inputblock
13102
13103            ];
13104
13105         } else {
13106
13107             cfg.cn = [
13108
13109                 inputblock
13110
13111             ];
13112                 
13113         }
13114         
13115         if (this.disabled) {
13116             input.disabled=true;
13117         }
13118         
13119         return cfg;
13120         
13121     },
13122     /**
13123      * return the real textarea element.
13124      */
13125     inputEl: function ()
13126     {
13127         return this.el.select('textarea.form-control',true).first();
13128     },
13129     
13130     /**
13131      * Clear any invalid styles/messages for this field
13132      */
13133     clearInvalid : function()
13134     {
13135         
13136         if(!this.el || this.preventMark){ // not rendered
13137             return;
13138         }
13139         
13140         var label = this.el.select('label', true).first();
13141         var icon = this.el.select('i.fa-star', true).first();
13142         
13143         if(label && icon){
13144             icon.remove();
13145         }
13146         this.el.removeClass( this.validClass);
13147         this.inputEl().removeClass('is-invalid');
13148          
13149         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13150             
13151             var feedback = this.el.select('.form-control-feedback', true).first();
13152             
13153             if(feedback){
13154                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13155             }
13156             
13157         }
13158         
13159         this.fireEvent('valid', this);
13160     },
13161     
13162      /**
13163      * Mark this field as valid
13164      */
13165     markValid : function()
13166     {
13167         if(!this.el  || this.preventMark){ // not rendered
13168             return;
13169         }
13170         
13171         this.el.removeClass([this.invalidClass, this.validClass]);
13172         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13173         
13174         var feedback = this.el.select('.form-control-feedback', true).first();
13175             
13176         if(feedback){
13177             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13178         }
13179
13180         if(this.disabled || this.allowBlank){
13181             return;
13182         }
13183         
13184         var label = this.el.select('label', true).first();
13185         var icon = this.el.select('i.fa-star', true).first();
13186         
13187         if(label && icon){
13188             icon.remove();
13189         }
13190         if (Roo.bootstrap.version == 3) {
13191             this.el.addClass(this.validClass);
13192         } else {
13193             this.inputEl().addClass('is-valid');
13194         }
13195         
13196         
13197         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13198             
13199             var feedback = this.el.select('.form-control-feedback', true).first();
13200             
13201             if(feedback){
13202                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13203                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13204             }
13205             
13206         }
13207         
13208         this.fireEvent('valid', this);
13209     },
13210     
13211      /**
13212      * Mark this field as invalid
13213      * @param {String} msg The validation message
13214      */
13215     markInvalid : function(msg)
13216     {
13217         if(!this.el  || this.preventMark){ // not rendered
13218             return;
13219         }
13220         
13221         this.el.removeClass([this.invalidClass, this.validClass]);
13222         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13223         
13224         var feedback = this.el.select('.form-control-feedback', true).first();
13225             
13226         if(feedback){
13227             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13228         }
13229
13230         if(this.disabled || this.allowBlank){
13231             return;
13232         }
13233         
13234         var label = this.el.select('label', true).first();
13235         var icon = this.el.select('i.fa-star', true).first();
13236         
13237         if(!this.getValue().length && label && !icon){
13238             this.el.createChild({
13239                 tag : 'i',
13240                 cls : 'text-danger fa fa-lg fa-star',
13241                 tooltip : 'This field is required',
13242                 style : 'margin-right:5px;'
13243             }, label, true);
13244         }
13245         
13246         if (Roo.bootstrap.version == 3) {
13247             this.el.addClass(this.invalidClass);
13248         } else {
13249             this.inputEl().addClass('is-invalid');
13250         }
13251         
13252         // fixme ... this may be depricated need to test..
13253         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13254             
13255             var feedback = this.el.select('.form-control-feedback', true).first();
13256             
13257             if(feedback){
13258                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13259                 
13260                 if(this.getValue().length || this.forceFeedback){
13261                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13262                 }
13263                 
13264             }
13265             
13266         }
13267         
13268         this.fireEvent('invalid', this, msg);
13269     }
13270 });
13271
13272  
13273 /*
13274  * - LGPL
13275  *
13276  * trigger field - base class for combo..
13277  * 
13278  */
13279  
13280 /**
13281  * @class Roo.bootstrap.TriggerField
13282  * @extends Roo.bootstrap.Input
13283  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13284  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13285  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13286  * for which you can provide a custom implementation.  For example:
13287  * <pre><code>
13288 var trigger = new Roo.bootstrap.TriggerField();
13289 trigger.onTriggerClick = myTriggerFn;
13290 trigger.applyTo('my-field');
13291 </code></pre>
13292  *
13293  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13294  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13295  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13296  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13297  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13298
13299  * @constructor
13300  * Create a new TriggerField.
13301  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13302  * to the base TextField)
13303  */
13304 Roo.bootstrap.TriggerField = function(config){
13305     this.mimicing = false;
13306     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13307 };
13308
13309 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13310     /**
13311      * @cfg {String} triggerClass A CSS class to apply to the trigger
13312      */
13313      /**
13314      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13315      */
13316     hideTrigger:false,
13317
13318     /**
13319      * @cfg {Boolean} removable (true|false) special filter default false
13320      */
13321     removable : false,
13322     
13323     /** @cfg {Boolean} grow @hide */
13324     /** @cfg {Number} growMin @hide */
13325     /** @cfg {Number} growMax @hide */
13326
13327     /**
13328      * @hide 
13329      * @method
13330      */
13331     autoSize: Roo.emptyFn,
13332     // private
13333     monitorTab : true,
13334     // private
13335     deferHeight : true,
13336
13337     
13338     actionMode : 'wrap',
13339     
13340     caret : false,
13341     
13342     
13343     getAutoCreate : function(){
13344        
13345         var align = this.labelAlign || this.parentLabelAlign();
13346         
13347         var id = Roo.id();
13348         
13349         var cfg = {
13350             cls: 'form-group' //input-group
13351         };
13352         
13353         
13354         var input =  {
13355             tag: 'input',
13356             id : id,
13357             type : this.inputType,
13358             cls : 'form-control',
13359             autocomplete: 'new-password',
13360             placeholder : this.placeholder || '' 
13361             
13362         };
13363         if (this.name) {
13364             input.name = this.name;
13365         }
13366         if (this.size) {
13367             input.cls += ' input-' + this.size;
13368         }
13369         
13370         if (this.disabled) {
13371             input.disabled=true;
13372         }
13373         
13374         var inputblock = input;
13375         
13376         if(this.hasFeedback && !this.allowBlank){
13377             
13378             var feedback = {
13379                 tag: 'span',
13380                 cls: 'glyphicon form-control-feedback'
13381             };
13382             
13383             if(this.removable && !this.editable  ){
13384                 inputblock = {
13385                     cls : 'has-feedback',
13386                     cn :  [
13387                         inputblock,
13388                         {
13389                             tag: 'button',
13390                             html : 'x',
13391                             cls : 'roo-combo-removable-btn close'
13392                         },
13393                         feedback
13394                     ] 
13395                 };
13396             } else {
13397                 inputblock = {
13398                     cls : 'has-feedback',
13399                     cn :  [
13400                         inputblock,
13401                         feedback
13402                     ] 
13403                 };
13404             }
13405
13406         } else {
13407             if(this.removable && !this.editable ){
13408                 inputblock = {
13409                     cls : 'roo-removable',
13410                     cn :  [
13411                         inputblock,
13412                         {
13413                             tag: 'button',
13414                             html : 'x',
13415                             cls : 'roo-combo-removable-btn close'
13416                         }
13417                     ] 
13418                 };
13419             }
13420         }
13421         
13422         if (this.before || this.after) {
13423             
13424             inputblock = {
13425                 cls : 'input-group',
13426                 cn :  [] 
13427             };
13428             if (this.before) {
13429                 inputblock.cn.push({
13430                     tag :'span',
13431                     cls : 'input-group-addon input-group-prepend input-group-text',
13432                     html : this.before
13433                 });
13434             }
13435             
13436             inputblock.cn.push(input);
13437             
13438             if(this.hasFeedback && !this.allowBlank){
13439                 inputblock.cls += ' has-feedback';
13440                 inputblock.cn.push(feedback);
13441             }
13442             
13443             if (this.after) {
13444                 inputblock.cn.push({
13445                     tag :'span',
13446                     cls : 'input-group-addon input-group-append input-group-text',
13447                     html : this.after
13448                 });
13449             }
13450             
13451         };
13452         
13453       
13454         
13455         var ibwrap = inputblock;
13456         
13457         if(this.multiple){
13458             ibwrap = {
13459                 tag: 'ul',
13460                 cls: 'roo-select2-choices',
13461                 cn:[
13462                     {
13463                         tag: 'li',
13464                         cls: 'roo-select2-search-field',
13465                         cn: [
13466
13467                             inputblock
13468                         ]
13469                     }
13470                 ]
13471             };
13472                 
13473         }
13474         
13475         var combobox = {
13476             cls: 'roo-select2-container input-group',
13477             cn: [
13478                  {
13479                     tag: 'input',
13480                     type : 'hidden',
13481                     cls: 'form-hidden-field'
13482                 },
13483                 ibwrap
13484             ]
13485         };
13486         
13487         if(!this.multiple && this.showToggleBtn){
13488             
13489             var caret = {
13490                         tag: 'span',
13491                         cls: 'caret'
13492              };
13493             if (this.caret != false) {
13494                 caret = {
13495                      tag: 'i',
13496                      cls: 'fa fa-' + this.caret
13497                 };
13498                 
13499             }
13500             
13501             combobox.cn.push({
13502                 tag :'span',
13503                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13504                 cn : [
13505                     Roo.bootstrap.version == 3 ? caret : '',
13506                     {
13507                         tag: 'span',
13508                         cls: 'combobox-clear',
13509                         cn  : [
13510                             {
13511                                 tag : 'i',
13512                                 cls: 'icon-remove'
13513                             }
13514                         ]
13515                     }
13516                 ]
13517
13518             })
13519         }
13520         
13521         if(this.multiple){
13522             combobox.cls += ' roo-select2-container-multi';
13523         }
13524          var indicator = {
13525             tag : 'i',
13526             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13527             tooltip : 'This field is required'
13528         };
13529         if (Roo.bootstrap.version == 4) {
13530             indicator = {
13531                 tag : 'i',
13532                 style : 'display:none'
13533             };
13534         }
13535         
13536         
13537         if (align ==='left' && this.fieldLabel.length) {
13538             
13539             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13540
13541             cfg.cn = [
13542                 indicator,
13543                 {
13544                     tag: 'label',
13545                     'for' :  id,
13546                     cls : 'control-label',
13547                     html : this.fieldLabel
13548
13549                 },
13550                 {
13551                     cls : "", 
13552                     cn: [
13553                         combobox
13554                     ]
13555                 }
13556
13557             ];
13558             
13559             var labelCfg = cfg.cn[1];
13560             var contentCfg = cfg.cn[2];
13561             
13562             if(this.indicatorpos == 'right'){
13563                 cfg.cn = [
13564                     {
13565                         tag: 'label',
13566                         'for' :  id,
13567                         cls : 'control-label',
13568                         cn : [
13569                             {
13570                                 tag : 'span',
13571                                 html : this.fieldLabel
13572                             },
13573                             indicator
13574                         ]
13575                     },
13576                     {
13577                         cls : "", 
13578                         cn: [
13579                             combobox
13580                         ]
13581                     }
13582
13583                 ];
13584                 
13585                 labelCfg = cfg.cn[0];
13586                 contentCfg = cfg.cn[1];
13587             }
13588             
13589             if(this.labelWidth > 12){
13590                 labelCfg.style = "width: " + this.labelWidth + 'px';
13591             }
13592             
13593             if(this.labelWidth < 13 && this.labelmd == 0){
13594                 this.labelmd = this.labelWidth;
13595             }
13596             
13597             if(this.labellg > 0){
13598                 labelCfg.cls += ' col-lg-' + this.labellg;
13599                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13600             }
13601             
13602             if(this.labelmd > 0){
13603                 labelCfg.cls += ' col-md-' + this.labelmd;
13604                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13605             }
13606             
13607             if(this.labelsm > 0){
13608                 labelCfg.cls += ' col-sm-' + this.labelsm;
13609                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13610             }
13611             
13612             if(this.labelxs > 0){
13613                 labelCfg.cls += ' col-xs-' + this.labelxs;
13614                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13615             }
13616             
13617         } else if ( this.fieldLabel.length) {
13618 //                Roo.log(" label");
13619             cfg.cn = [
13620                 indicator,
13621                {
13622                    tag: 'label',
13623                    //cls : 'input-group-addon',
13624                    html : this.fieldLabel
13625
13626                },
13627
13628                combobox
13629
13630             ];
13631             
13632             if(this.indicatorpos == 'right'){
13633                 
13634                 cfg.cn = [
13635                     {
13636                        tag: 'label',
13637                        cn : [
13638                            {
13639                                tag : 'span',
13640                                html : this.fieldLabel
13641                            },
13642                            indicator
13643                        ]
13644
13645                     },
13646                     combobox
13647
13648                 ];
13649
13650             }
13651
13652         } else {
13653             
13654 //                Roo.log(" no label && no align");
13655                 cfg = combobox
13656                      
13657                 
13658         }
13659         
13660         var settings=this;
13661         ['xs','sm','md','lg'].map(function(size){
13662             if (settings[size]) {
13663                 cfg.cls += ' col-' + size + '-' + settings[size];
13664             }
13665         });
13666         
13667         return cfg;
13668         
13669     },
13670     
13671     
13672     
13673     // private
13674     onResize : function(w, h){
13675 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13676 //        if(typeof w == 'number'){
13677 //            var x = w - this.trigger.getWidth();
13678 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13679 //            this.trigger.setStyle('left', x+'px');
13680 //        }
13681     },
13682
13683     // private
13684     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13685
13686     // private
13687     getResizeEl : function(){
13688         return this.inputEl();
13689     },
13690
13691     // private
13692     getPositionEl : function(){
13693         return this.inputEl();
13694     },
13695
13696     // private
13697     alignErrorIcon : function(){
13698         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13699     },
13700
13701     // private
13702     initEvents : function(){
13703         
13704         this.createList();
13705         
13706         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13707         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13708         if(!this.multiple && this.showToggleBtn){
13709             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13710             if(this.hideTrigger){
13711                 this.trigger.setDisplayed(false);
13712             }
13713             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13714         }
13715         
13716         if(this.multiple){
13717             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13718         }
13719         
13720         if(this.removable && !this.editable && !this.tickable){
13721             var close = this.closeTriggerEl();
13722             
13723             if(close){
13724                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13725                 close.on('click', this.removeBtnClick, this, close);
13726             }
13727         }
13728         
13729         //this.trigger.addClassOnOver('x-form-trigger-over');
13730         //this.trigger.addClassOnClick('x-form-trigger-click');
13731         
13732         //if(!this.width){
13733         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13734         //}
13735     },
13736     
13737     closeTriggerEl : function()
13738     {
13739         var close = this.el.select('.roo-combo-removable-btn', true).first();
13740         return close ? close : false;
13741     },
13742     
13743     removeBtnClick : function(e, h, el)
13744     {
13745         e.preventDefault();
13746         
13747         if(this.fireEvent("remove", this) !== false){
13748             this.reset();
13749             this.fireEvent("afterremove", this)
13750         }
13751     },
13752     
13753     createList : function()
13754     {
13755         this.list = Roo.get(document.body).createChild({
13756             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13757             cls: 'typeahead typeahead-long dropdown-menu shadow',
13758             style: 'display:none'
13759         });
13760         
13761         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13762         
13763     },
13764
13765     // private
13766     initTrigger : function(){
13767        
13768     },
13769
13770     // private
13771     onDestroy : function(){
13772         if(this.trigger){
13773             this.trigger.removeAllListeners();
13774           //  this.trigger.remove();
13775         }
13776         //if(this.wrap){
13777         //    this.wrap.remove();
13778         //}
13779         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13780     },
13781
13782     // private
13783     onFocus : function(){
13784         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13785         /*
13786         if(!this.mimicing){
13787             this.wrap.addClass('x-trigger-wrap-focus');
13788             this.mimicing = true;
13789             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13790             if(this.monitorTab){
13791                 this.el.on("keydown", this.checkTab, this);
13792             }
13793         }
13794         */
13795     },
13796
13797     // private
13798     checkTab : function(e){
13799         if(e.getKey() == e.TAB){
13800             this.triggerBlur();
13801         }
13802     },
13803
13804     // private
13805     onBlur : function(){
13806         // do nothing
13807     },
13808
13809     // private
13810     mimicBlur : function(e, t){
13811         /*
13812         if(!this.wrap.contains(t) && this.validateBlur()){
13813             this.triggerBlur();
13814         }
13815         */
13816     },
13817
13818     // private
13819     triggerBlur : function(){
13820         this.mimicing = false;
13821         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13822         if(this.monitorTab){
13823             this.el.un("keydown", this.checkTab, this);
13824         }
13825         //this.wrap.removeClass('x-trigger-wrap-focus');
13826         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13827     },
13828
13829     // private
13830     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13831     validateBlur : function(e, t){
13832         return true;
13833     },
13834
13835     // private
13836     onDisable : function(){
13837         this.inputEl().dom.disabled = true;
13838         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13839         //if(this.wrap){
13840         //    this.wrap.addClass('x-item-disabled');
13841         //}
13842     },
13843
13844     // private
13845     onEnable : function(){
13846         this.inputEl().dom.disabled = false;
13847         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13848         //if(this.wrap){
13849         //    this.el.removeClass('x-item-disabled');
13850         //}
13851     },
13852
13853     // private
13854     onShow : function(){
13855         var ae = this.getActionEl();
13856         
13857         if(ae){
13858             ae.dom.style.display = '';
13859             ae.dom.style.visibility = 'visible';
13860         }
13861     },
13862
13863     // private
13864     
13865     onHide : function(){
13866         var ae = this.getActionEl();
13867         ae.dom.style.display = 'none';
13868     },
13869
13870     /**
13871      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13872      * by an implementing function.
13873      * @method
13874      * @param {EventObject} e
13875      */
13876     onTriggerClick : Roo.emptyFn
13877 });
13878  
13879 /*
13880 * Licence: LGPL
13881 */
13882
13883 /**
13884  * @class Roo.bootstrap.CardUploader
13885  * @extends Roo.bootstrap.Button
13886  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13887  * @cfg {Number} errorTimeout default 3000
13888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13889  * @cfg {Array}  html The button text.
13890
13891  *
13892  * @constructor
13893  * Create a new CardUploader
13894  * @param {Object} config The config object
13895  */
13896
13897 Roo.bootstrap.CardUploader = function(config){
13898     
13899  
13900     
13901     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13902     
13903     
13904     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13905         return r.data.id
13906      });
13907     
13908      this.addEvents({
13909          // raw events
13910         /**
13911          * @event preview
13912          * When a image is clicked on - and needs to display a slideshow or similar..
13913          * @param {Roo.bootstrap.Card} this
13914          * @param {Object} The image information data 
13915          *
13916          */
13917         'preview' : true,
13918          /**
13919          * @event download
13920          * When a the download link is clicked
13921          * @param {Roo.bootstrap.Card} this
13922          * @param {Object} The image information data  contains 
13923          */
13924         'download' : true
13925         
13926     });
13927 };
13928  
13929 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13930     
13931      
13932     errorTimeout : 3000,
13933      
13934     images : false,
13935    
13936     fileCollection : false,
13937     allowBlank : true,
13938     
13939     getAutoCreate : function()
13940     {
13941         
13942         var cfg =  {
13943             cls :'form-group' ,
13944             cn : [
13945                
13946                 {
13947                     tag: 'label',
13948                    //cls : 'input-group-addon',
13949                     html : this.fieldLabel
13950
13951                 },
13952
13953                 {
13954                     tag: 'input',
13955                     type : 'hidden',
13956                     name : this.name,
13957                     value : this.value,
13958                     cls : 'd-none  form-control'
13959                 },
13960                 
13961                 {
13962                     tag: 'input',
13963                     multiple : 'multiple',
13964                     type : 'file',
13965                     cls : 'd-none  roo-card-upload-selector'
13966                 },
13967                 
13968                 {
13969                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13970                 },
13971                 {
13972                     cls : 'card-columns roo-card-uploader-container'
13973                 }
13974
13975             ]
13976         };
13977            
13978          
13979         return cfg;
13980     },
13981     
13982     getChildContainer : function() /// what children are added to.
13983     {
13984         return this.containerEl;
13985     },
13986    
13987     getButtonContainer : function() /// what children are added to.
13988     {
13989         return this.el.select(".roo-card-uploader-button-container").first();
13990     },
13991    
13992     initEvents : function()
13993     {
13994         
13995         Roo.bootstrap.Input.prototype.initEvents.call(this);
13996         
13997         var t = this;
13998         this.addxtype({
13999             xns: Roo.bootstrap,
14000
14001             xtype : 'Button',
14002             container_method : 'getButtonContainer' ,            
14003             html :  this.html, // fix changable?
14004             cls : 'w-100 ',
14005             listeners : {
14006                 'click' : function(btn, e) {
14007                     t.onClick(e);
14008                 }
14009             }
14010         });
14011         
14012         
14013         
14014         
14015         this.urlAPI = (window.createObjectURL && window) || 
14016                                 (window.URL && URL.revokeObjectURL && URL) || 
14017                                 (window.webkitURL && webkitURL);
14018                         
14019          
14020          
14021          
14022         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14023         
14024         this.selectorEl.on('change', this.onFileSelected, this);
14025         if (this.images) {
14026             var t = this;
14027             this.images.forEach(function(img) {
14028                 t.addCard(img)
14029             });
14030             this.images = false;
14031         }
14032         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14033          
14034        
14035     },
14036     
14037    
14038     onClick : function(e)
14039     {
14040         e.preventDefault();
14041          
14042         this.selectorEl.dom.click();
14043          
14044     },
14045     
14046     onFileSelected : function(e)
14047     {
14048         e.preventDefault();
14049         
14050         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14051             return;
14052         }
14053         
14054         Roo.each(this.selectorEl.dom.files, function(file){    
14055             this.addFile(file);
14056         }, this);
14057          
14058     },
14059     
14060       
14061     
14062       
14063     
14064     addFile : function(file)
14065     {
14066            
14067         if(typeof(file) === 'string'){
14068             throw "Add file by name?"; // should not happen
14069             return;
14070         }
14071         
14072         if(!file || !this.urlAPI){
14073             return;
14074         }
14075         
14076         // file;
14077         // file.type;
14078         
14079         var _this = this;
14080         
14081         
14082         var url = _this.urlAPI.createObjectURL( file);
14083            
14084         this.addCard({
14085             id : Roo.bootstrap.CardUploader.ID--,
14086             is_uploaded : false,
14087             src : url,
14088             srcfile : file,
14089             title : file.name,
14090             mimetype : file.type,
14091             preview : false,
14092             is_deleted : 0
14093         });
14094         
14095     },
14096     
14097     /**
14098      * addCard - add an Attachment to the uploader
14099      * @param data - the data about the image to upload
14100      *
14101      * {
14102           id : 123
14103           title : "Title of file",
14104           is_uploaded : false,
14105           src : "http://.....",
14106           srcfile : { the File upload object },
14107           mimetype : file.type,
14108           preview : false,
14109           is_deleted : 0
14110           .. any other data...
14111         }
14112      *
14113      * 
14114     */
14115     
14116     addCard : function (data)
14117     {
14118         // hidden input element?
14119         // if the file is not an image...
14120         //then we need to use something other that and header_image
14121         var t = this;
14122         //   remove.....
14123         var footer = [
14124             {
14125                 xns : Roo.bootstrap,
14126                 xtype : 'CardFooter',
14127                  items: [
14128                     {
14129                         xns : Roo.bootstrap,
14130                         xtype : 'Element',
14131                         cls : 'd-flex',
14132                         items : [
14133                             
14134                             {
14135                                 xns : Roo.bootstrap,
14136                                 xtype : 'Button',
14137                                 html : String.format("<small>{0}</small>", data.title),
14138                                 cls : 'col-10 text-left',
14139                                 size: 'sm',
14140                                 weight: 'link',
14141                                 fa : 'download',
14142                                 listeners : {
14143                                     click : function() {
14144                                      
14145                                         t.fireEvent( "download", t, data );
14146                                     }
14147                                 }
14148                             },
14149                           
14150                             {
14151                                 xns : Roo.bootstrap,
14152                                 xtype : 'Button',
14153                                 style: 'max-height: 28px; ',
14154                                 size : 'sm',
14155                                 weight: 'danger',
14156                                 cls : 'col-2',
14157                                 fa : 'times',
14158                                 listeners : {
14159                                     click : function() {
14160                                         t.removeCard(data.id)
14161                                     }
14162                                 }
14163                             }
14164                         ]
14165                     }
14166                     
14167                 ] 
14168             }
14169             
14170         ];
14171         
14172         var cn = this.addxtype(
14173             {
14174                  
14175                 xns : Roo.bootstrap,
14176                 xtype : 'Card',
14177                 closeable : true,
14178                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14179                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14180                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14181                 data : data,
14182                 html : false,
14183                  
14184                 items : footer,
14185                 initEvents : function() {
14186                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14187                     var card = this;
14188                     this.imgEl = this.el.select('.card-img-top').first();
14189                     if (this.imgEl) {
14190                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14191                         this.imgEl.set({ 'pointer' : 'cursor' });
14192                                   
14193                     }
14194                     this.getCardFooter().addClass('p-1');
14195                     
14196                   
14197                 }
14198                 
14199             }
14200         );
14201         // dont' really need ot update items.
14202         // this.items.push(cn);
14203         this.fileCollection.add(cn);
14204         
14205         if (!data.srcfile) {
14206             this.updateInput();
14207             return;
14208         }
14209             
14210         var _t = this;
14211         var reader = new FileReader();
14212         reader.addEventListener("load", function() {  
14213             data.srcdata =  reader.result;
14214             _t.updateInput();
14215         });
14216         reader.readAsDataURL(data.srcfile);
14217         
14218         
14219         
14220     },
14221     removeCard : function(id)
14222     {
14223         
14224         var card  = this.fileCollection.get(id);
14225         card.data.is_deleted = 1;
14226         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14227         //this.fileCollection.remove(card);
14228         //this.items = this.items.filter(function(e) { return e != card });
14229         // dont' really need ot update items.
14230         card.el.dom.parentNode.removeChild(card.el.dom);
14231         this.updateInput();
14232
14233         
14234     },
14235     reset: function()
14236     {
14237         this.fileCollection.each(function(card) {
14238             if (card.el.dom && card.el.dom.parentNode) {
14239                 card.el.dom.parentNode.removeChild(card.el.dom);
14240             }
14241         });
14242         this.fileCollection.clear();
14243         this.updateInput();
14244     },
14245     
14246     updateInput : function()
14247     {
14248          var data = [];
14249         this.fileCollection.each(function(e) {
14250             data.push(e.data);
14251             
14252         });
14253         this.inputEl().dom.value = JSON.stringify(data);
14254         
14255         
14256         
14257     }
14258     
14259     
14260 });
14261
14262
14263 Roo.bootstrap.CardUploader.ID = -1;/*
14264  * Based on:
14265  * Ext JS Library 1.1.1
14266  * Copyright(c) 2006-2007, Ext JS, LLC.
14267  *
14268  * Originally Released Under LGPL - original licence link has changed is not relivant.
14269  *
14270  * Fork - LGPL
14271  * <script type="text/javascript">
14272  */
14273
14274
14275 /**
14276  * @class Roo.data.SortTypes
14277  * @singleton
14278  * Defines the default sorting (casting?) comparison functions used when sorting data.
14279  */
14280 Roo.data.SortTypes = {
14281     /**
14282      * Default sort that does nothing
14283      * @param {Mixed} s The value being converted
14284      * @return {Mixed} The comparison value
14285      */
14286     none : function(s){
14287         return s;
14288     },
14289     
14290     /**
14291      * The regular expression used to strip tags
14292      * @type {RegExp}
14293      * @property
14294      */
14295     stripTagsRE : /<\/?[^>]+>/gi,
14296     
14297     /**
14298      * Strips all HTML tags to sort on text only
14299      * @param {Mixed} s The value being converted
14300      * @return {String} The comparison value
14301      */
14302     asText : function(s){
14303         return String(s).replace(this.stripTagsRE, "");
14304     },
14305     
14306     /**
14307      * Strips all HTML tags to sort on text only - Case insensitive
14308      * @param {Mixed} s The value being converted
14309      * @return {String} The comparison value
14310      */
14311     asUCText : function(s){
14312         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14313     },
14314     
14315     /**
14316      * Case insensitive string
14317      * @param {Mixed} s The value being converted
14318      * @return {String} The comparison value
14319      */
14320     asUCString : function(s) {
14321         return String(s).toUpperCase();
14322     },
14323     
14324     /**
14325      * Date sorting
14326      * @param {Mixed} s The value being converted
14327      * @return {Number} The comparison value
14328      */
14329     asDate : function(s) {
14330         if(!s){
14331             return 0;
14332         }
14333         if(s instanceof Date){
14334             return s.getTime();
14335         }
14336         return Date.parse(String(s));
14337     },
14338     
14339     /**
14340      * Float sorting
14341      * @param {Mixed} s The value being converted
14342      * @return {Float} The comparison value
14343      */
14344     asFloat : function(s) {
14345         var val = parseFloat(String(s).replace(/,/g, ""));
14346         if(isNaN(val)) {
14347             val = 0;
14348         }
14349         return val;
14350     },
14351     
14352     /**
14353      * Integer sorting
14354      * @param {Mixed} s The value being converted
14355      * @return {Number} The comparison value
14356      */
14357     asInt : function(s) {
14358         var val = parseInt(String(s).replace(/,/g, ""));
14359         if(isNaN(val)) {
14360             val = 0;
14361         }
14362         return val;
14363     }
14364 };/*
14365  * Based on:
14366  * Ext JS Library 1.1.1
14367  * Copyright(c) 2006-2007, Ext JS, LLC.
14368  *
14369  * Originally Released Under LGPL - original licence link has changed is not relivant.
14370  *
14371  * Fork - LGPL
14372  * <script type="text/javascript">
14373  */
14374
14375 /**
14376 * @class Roo.data.Record
14377  * Instances of this class encapsulate both record <em>definition</em> information, and record
14378  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14379  * to access Records cached in an {@link Roo.data.Store} object.<br>
14380  * <p>
14381  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14382  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14383  * objects.<br>
14384  * <p>
14385  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14386  * @constructor
14387  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14388  * {@link #create}. The parameters are the same.
14389  * @param {Array} data An associative Array of data values keyed by the field name.
14390  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14391  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14392  * not specified an integer id is generated.
14393  */
14394 Roo.data.Record = function(data, id){
14395     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14396     this.data = data;
14397 };
14398
14399 /**
14400  * Generate a constructor for a specific record layout.
14401  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14402  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14403  * Each field definition object may contain the following properties: <ul>
14404  * <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,
14405  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14406  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14407  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14408  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14409  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14410  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14411  * this may be omitted.</p></li>
14412  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14413  * <ul><li>auto (Default, implies no conversion)</li>
14414  * <li>string</li>
14415  * <li>int</li>
14416  * <li>float</li>
14417  * <li>boolean</li>
14418  * <li>date</li></ul></p></li>
14419  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14420  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14421  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14422  * by the Reader into an object that will be stored in the Record. It is passed the
14423  * following parameters:<ul>
14424  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14425  * </ul></p></li>
14426  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14427  * </ul>
14428  * <br>usage:<br><pre><code>
14429 var TopicRecord = Roo.data.Record.create(
14430     {name: 'title', mapping: 'topic_title'},
14431     {name: 'author', mapping: 'username'},
14432     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14433     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14434     {name: 'lastPoster', mapping: 'user2'},
14435     {name: 'excerpt', mapping: 'post_text'}
14436 );
14437
14438 var myNewRecord = new TopicRecord({
14439     title: 'Do my job please',
14440     author: 'noobie',
14441     totalPosts: 1,
14442     lastPost: new Date(),
14443     lastPoster: 'Animal',
14444     excerpt: 'No way dude!'
14445 });
14446 myStore.add(myNewRecord);
14447 </code></pre>
14448  * @method create
14449  * @static
14450  */
14451 Roo.data.Record.create = function(o){
14452     var f = function(){
14453         f.superclass.constructor.apply(this, arguments);
14454     };
14455     Roo.extend(f, Roo.data.Record);
14456     var p = f.prototype;
14457     p.fields = new Roo.util.MixedCollection(false, function(field){
14458         return field.name;
14459     });
14460     for(var i = 0, len = o.length; i < len; i++){
14461         p.fields.add(new Roo.data.Field(o[i]));
14462     }
14463     f.getField = function(name){
14464         return p.fields.get(name);  
14465     };
14466     return f;
14467 };
14468
14469 Roo.data.Record.AUTO_ID = 1000;
14470 Roo.data.Record.EDIT = 'edit';
14471 Roo.data.Record.REJECT = 'reject';
14472 Roo.data.Record.COMMIT = 'commit';
14473
14474 Roo.data.Record.prototype = {
14475     /**
14476      * Readonly flag - true if this record has been modified.
14477      * @type Boolean
14478      */
14479     dirty : false,
14480     editing : false,
14481     error: null,
14482     modified: null,
14483
14484     // private
14485     join : function(store){
14486         this.store = store;
14487     },
14488
14489     /**
14490      * Set the named field to the specified value.
14491      * @param {String} name The name of the field to set.
14492      * @param {Object} value The value to set the field to.
14493      */
14494     set : function(name, value){
14495         if(this.data[name] == value){
14496             return;
14497         }
14498         this.dirty = true;
14499         if(!this.modified){
14500             this.modified = {};
14501         }
14502         if(typeof this.modified[name] == 'undefined'){
14503             this.modified[name] = this.data[name];
14504         }
14505         this.data[name] = value;
14506         if(!this.editing && this.store){
14507             this.store.afterEdit(this);
14508         }       
14509     },
14510
14511     /**
14512      * Get the value of the named field.
14513      * @param {String} name The name of the field to get the value of.
14514      * @return {Object} The value of the field.
14515      */
14516     get : function(name){
14517         return this.data[name]; 
14518     },
14519
14520     // private
14521     beginEdit : function(){
14522         this.editing = true;
14523         this.modified = {}; 
14524     },
14525
14526     // private
14527     cancelEdit : function(){
14528         this.editing = false;
14529         delete this.modified;
14530     },
14531
14532     // private
14533     endEdit : function(){
14534         this.editing = false;
14535         if(this.dirty && this.store){
14536             this.store.afterEdit(this);
14537         }
14538     },
14539
14540     /**
14541      * Usually called by the {@link Roo.data.Store} which owns the Record.
14542      * Rejects all changes made to the Record since either creation, or the last commit operation.
14543      * Modified fields are reverted to their original values.
14544      * <p>
14545      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14546      * of reject operations.
14547      */
14548     reject : function(){
14549         var m = this.modified;
14550         for(var n in m){
14551             if(typeof m[n] != "function"){
14552                 this.data[n] = m[n];
14553             }
14554         }
14555         this.dirty = false;
14556         delete this.modified;
14557         this.editing = false;
14558         if(this.store){
14559             this.store.afterReject(this);
14560         }
14561     },
14562
14563     /**
14564      * Usually called by the {@link Roo.data.Store} which owns the Record.
14565      * Commits all changes made to the Record since either creation, or the last commit operation.
14566      * <p>
14567      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14568      * of commit operations.
14569      */
14570     commit : function(){
14571         this.dirty = false;
14572         delete this.modified;
14573         this.editing = false;
14574         if(this.store){
14575             this.store.afterCommit(this);
14576         }
14577     },
14578
14579     // private
14580     hasError : function(){
14581         return this.error != null;
14582     },
14583
14584     // private
14585     clearError : function(){
14586         this.error = null;
14587     },
14588
14589     /**
14590      * Creates a copy of this record.
14591      * @param {String} id (optional) A new record id if you don't want to use this record's id
14592      * @return {Record}
14593      */
14594     copy : function(newId) {
14595         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14596     }
14597 };/*
14598  * Based on:
14599  * Ext JS Library 1.1.1
14600  * Copyright(c) 2006-2007, Ext JS, LLC.
14601  *
14602  * Originally Released Under LGPL - original licence link has changed is not relivant.
14603  *
14604  * Fork - LGPL
14605  * <script type="text/javascript">
14606  */
14607
14608
14609
14610 /**
14611  * @class Roo.data.Store
14612  * @extends Roo.util.Observable
14613  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14614  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14615  * <p>
14616  * 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
14617  * has no knowledge of the format of the data returned by the Proxy.<br>
14618  * <p>
14619  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14620  * instances from the data object. These records are cached and made available through accessor functions.
14621  * @constructor
14622  * Creates a new Store.
14623  * @param {Object} config A config object containing the objects needed for the Store to access data,
14624  * and read the data into Records.
14625  */
14626 Roo.data.Store = function(config){
14627     this.data = new Roo.util.MixedCollection(false);
14628     this.data.getKey = function(o){
14629         return o.id;
14630     };
14631     this.baseParams = {};
14632     // private
14633     this.paramNames = {
14634         "start" : "start",
14635         "limit" : "limit",
14636         "sort" : "sort",
14637         "dir" : "dir",
14638         "multisort" : "_multisort"
14639     };
14640
14641     if(config && config.data){
14642         this.inlineData = config.data;
14643         delete config.data;
14644     }
14645
14646     Roo.apply(this, config);
14647     
14648     if(this.reader){ // reader passed
14649         this.reader = Roo.factory(this.reader, Roo.data);
14650         this.reader.xmodule = this.xmodule || false;
14651         if(!this.recordType){
14652             this.recordType = this.reader.recordType;
14653         }
14654         if(this.reader.onMetaChange){
14655             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14656         }
14657     }
14658
14659     if(this.recordType){
14660         this.fields = this.recordType.prototype.fields;
14661     }
14662     this.modified = [];
14663
14664     this.addEvents({
14665         /**
14666          * @event datachanged
14667          * Fires when the data cache has changed, and a widget which is using this Store
14668          * as a Record cache should refresh its view.
14669          * @param {Store} this
14670          */
14671         datachanged : true,
14672         /**
14673          * @event metachange
14674          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14675          * @param {Store} this
14676          * @param {Object} meta The JSON metadata
14677          */
14678         metachange : true,
14679         /**
14680          * @event add
14681          * Fires when Records have been added to the Store
14682          * @param {Store} this
14683          * @param {Roo.data.Record[]} records The array of Records added
14684          * @param {Number} index The index at which the record(s) were added
14685          */
14686         add : true,
14687         /**
14688          * @event remove
14689          * Fires when a Record has been removed from the Store
14690          * @param {Store} this
14691          * @param {Roo.data.Record} record The Record that was removed
14692          * @param {Number} index The index at which the record was removed
14693          */
14694         remove : true,
14695         /**
14696          * @event update
14697          * Fires when a Record has been updated
14698          * @param {Store} this
14699          * @param {Roo.data.Record} record The Record that was updated
14700          * @param {String} operation The update operation being performed.  Value may be one of:
14701          * <pre><code>
14702  Roo.data.Record.EDIT
14703  Roo.data.Record.REJECT
14704  Roo.data.Record.COMMIT
14705          * </code></pre>
14706          */
14707         update : true,
14708         /**
14709          * @event clear
14710          * Fires when the data cache has been cleared.
14711          * @param {Store} this
14712          */
14713         clear : true,
14714         /**
14715          * @event beforeload
14716          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14717          * the load action will be canceled.
14718          * @param {Store} this
14719          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14720          */
14721         beforeload : true,
14722         /**
14723          * @event beforeloadadd
14724          * Fires after a new set of Records has been loaded.
14725          * @param {Store} this
14726          * @param {Roo.data.Record[]} records The Records that were loaded
14727          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14728          */
14729         beforeloadadd : true,
14730         /**
14731          * @event load
14732          * Fires after a new set of Records has been loaded, before they are added to the store.
14733          * @param {Store} this
14734          * @param {Roo.data.Record[]} records The Records that were loaded
14735          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14736          * @params {Object} return from reader
14737          */
14738         load : true,
14739         /**
14740          * @event loadexception
14741          * Fires if an exception occurs in the Proxy during loading.
14742          * Called with the signature of the Proxy's "loadexception" event.
14743          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14744          * 
14745          * @param {Proxy} 
14746          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14747          * @param {Object} load options 
14748          * @param {Object} jsonData from your request (normally this contains the Exception)
14749          */
14750         loadexception : true
14751     });
14752     
14753     if(this.proxy){
14754         this.proxy = Roo.factory(this.proxy, Roo.data);
14755         this.proxy.xmodule = this.xmodule || false;
14756         this.relayEvents(this.proxy,  ["loadexception"]);
14757     }
14758     this.sortToggle = {};
14759     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14760
14761     Roo.data.Store.superclass.constructor.call(this);
14762
14763     if(this.inlineData){
14764         this.loadData(this.inlineData);
14765         delete this.inlineData;
14766     }
14767 };
14768
14769 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14770      /**
14771     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14772     * without a remote query - used by combo/forms at present.
14773     */
14774     
14775     /**
14776     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14777     */
14778     /**
14779     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14780     */
14781     /**
14782     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14783     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14784     */
14785     /**
14786     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14787     * on any HTTP request
14788     */
14789     /**
14790     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14791     */
14792     /**
14793     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14794     */
14795     multiSort: false,
14796     /**
14797     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14798     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14799     */
14800     remoteSort : false,
14801
14802     /**
14803     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14804      * loaded or when a record is removed. (defaults to false).
14805     */
14806     pruneModifiedRecords : false,
14807
14808     // private
14809     lastOptions : null,
14810
14811     /**
14812      * Add Records to the Store and fires the add event.
14813      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14814      */
14815     add : function(records){
14816         records = [].concat(records);
14817         for(var i = 0, len = records.length; i < len; i++){
14818             records[i].join(this);
14819         }
14820         var index = this.data.length;
14821         this.data.addAll(records);
14822         this.fireEvent("add", this, records, index);
14823     },
14824
14825     /**
14826      * Remove a Record from the Store and fires the remove event.
14827      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14828      */
14829     remove : function(record){
14830         var index = this.data.indexOf(record);
14831         this.data.removeAt(index);
14832  
14833         if(this.pruneModifiedRecords){
14834             this.modified.remove(record);
14835         }
14836         this.fireEvent("remove", this, record, index);
14837     },
14838
14839     /**
14840      * Remove all Records from the Store and fires the clear event.
14841      */
14842     removeAll : function(){
14843         this.data.clear();
14844         if(this.pruneModifiedRecords){
14845             this.modified = [];
14846         }
14847         this.fireEvent("clear", this);
14848     },
14849
14850     /**
14851      * Inserts Records to the Store at the given index and fires the add event.
14852      * @param {Number} index The start index at which to insert the passed Records.
14853      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14854      */
14855     insert : function(index, records){
14856         records = [].concat(records);
14857         for(var i = 0, len = records.length; i < len; i++){
14858             this.data.insert(index, records[i]);
14859             records[i].join(this);
14860         }
14861         this.fireEvent("add", this, records, index);
14862     },
14863
14864     /**
14865      * Get the index within the cache of the passed Record.
14866      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14867      * @return {Number} The index of the passed Record. Returns -1 if not found.
14868      */
14869     indexOf : function(record){
14870         return this.data.indexOf(record);
14871     },
14872
14873     /**
14874      * Get the index within the cache of the Record with the passed id.
14875      * @param {String} id The id of the Record to find.
14876      * @return {Number} The index of the Record. Returns -1 if not found.
14877      */
14878     indexOfId : function(id){
14879         return this.data.indexOfKey(id);
14880     },
14881
14882     /**
14883      * Get the Record with the specified id.
14884      * @param {String} id The id of the Record to find.
14885      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14886      */
14887     getById : function(id){
14888         return this.data.key(id);
14889     },
14890
14891     /**
14892      * Get the Record at the specified index.
14893      * @param {Number} index The index of the Record to find.
14894      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14895      */
14896     getAt : function(index){
14897         return this.data.itemAt(index);
14898     },
14899
14900     /**
14901      * Returns a range of Records between specified indices.
14902      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14903      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14904      * @return {Roo.data.Record[]} An array of Records
14905      */
14906     getRange : function(start, end){
14907         return this.data.getRange(start, end);
14908     },
14909
14910     // private
14911     storeOptions : function(o){
14912         o = Roo.apply({}, o);
14913         delete o.callback;
14914         delete o.scope;
14915         this.lastOptions = o;
14916     },
14917
14918     /**
14919      * Loads the Record cache from the configured Proxy using the configured Reader.
14920      * <p>
14921      * If using remote paging, then the first load call must specify the <em>start</em>
14922      * and <em>limit</em> properties in the options.params property to establish the initial
14923      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14924      * <p>
14925      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14926      * and this call will return before the new data has been loaded. Perform any post-processing
14927      * in a callback function, or in a "load" event handler.</strong>
14928      * <p>
14929      * @param {Object} options An object containing properties which control loading options:<ul>
14930      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14931      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14932      * passed the following arguments:<ul>
14933      * <li>r : Roo.data.Record[]</li>
14934      * <li>options: Options object from the load call</li>
14935      * <li>success: Boolean success indicator</li></ul></li>
14936      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14937      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14938      * </ul>
14939      */
14940     load : function(options){
14941         options = options || {};
14942         if(this.fireEvent("beforeload", this, options) !== false){
14943             this.storeOptions(options);
14944             var p = Roo.apply(options.params || {}, this.baseParams);
14945             // if meta was not loaded from remote source.. try requesting it.
14946             if (!this.reader.metaFromRemote) {
14947                 p._requestMeta = 1;
14948             }
14949             if(this.sortInfo && this.remoteSort){
14950                 var pn = this.paramNames;
14951                 p[pn["sort"]] = this.sortInfo.field;
14952                 p[pn["dir"]] = this.sortInfo.direction;
14953             }
14954             if (this.multiSort) {
14955                 var pn = this.paramNames;
14956                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14957             }
14958             
14959             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14960         }
14961     },
14962
14963     /**
14964      * Reloads the Record cache from the configured Proxy using the configured Reader and
14965      * the options from the last load operation performed.
14966      * @param {Object} options (optional) An object containing properties which may override the options
14967      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14968      * the most recently used options are reused).
14969      */
14970     reload : function(options){
14971         this.load(Roo.applyIf(options||{}, this.lastOptions));
14972     },
14973
14974     // private
14975     // Called as a callback by the Reader during a load operation.
14976     loadRecords : function(o, options, success){
14977         if(!o || success === false){
14978             if(success !== false){
14979                 this.fireEvent("load", this, [], options, o);
14980             }
14981             if(options.callback){
14982                 options.callback.call(options.scope || this, [], options, false);
14983             }
14984             return;
14985         }
14986         // if data returned failure - throw an exception.
14987         if (o.success === false) {
14988             // show a message if no listener is registered.
14989             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14990                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14991             }
14992             // loadmask wil be hooked into this..
14993             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14994             return;
14995         }
14996         var r = o.records, t = o.totalRecords || r.length;
14997         
14998         this.fireEvent("beforeloadadd", this, r, options, o);
14999         
15000         if(!options || options.add !== true){
15001             if(this.pruneModifiedRecords){
15002                 this.modified = [];
15003             }
15004             for(var i = 0, len = r.length; i < len; i++){
15005                 r[i].join(this);
15006             }
15007             if(this.snapshot){
15008                 this.data = this.snapshot;
15009                 delete this.snapshot;
15010             }
15011             this.data.clear();
15012             this.data.addAll(r);
15013             this.totalLength = t;
15014             this.applySort();
15015             this.fireEvent("datachanged", this);
15016         }else{
15017             this.totalLength = Math.max(t, this.data.length+r.length);
15018             this.add(r);
15019         }
15020         
15021         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15022                 
15023             var e = new Roo.data.Record({});
15024
15025             e.set(this.parent.displayField, this.parent.emptyTitle);
15026             e.set(this.parent.valueField, '');
15027
15028             this.insert(0, e);
15029         }
15030             
15031         this.fireEvent("load", this, r, options, o);
15032         if(options.callback){
15033             options.callback.call(options.scope || this, r, options, true);
15034         }
15035     },
15036
15037
15038     /**
15039      * Loads data from a passed data block. A Reader which understands the format of the data
15040      * must have been configured in the constructor.
15041      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15042      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15043      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15044      */
15045     loadData : function(o, append){
15046         var r = this.reader.readRecords(o);
15047         this.loadRecords(r, {add: append}, true);
15048     },
15049     
15050      /**
15051      * using 'cn' the nested child reader read the child array into it's child stores.
15052      * @param {Object} rec The record with a 'children array
15053      */
15054     loadDataFromChildren : function(rec)
15055     {
15056         this.loadData(this.reader.toLoadData(rec));
15057     },
15058     
15059
15060     /**
15061      * Gets the number of cached records.
15062      * <p>
15063      * <em>If using paging, this may not be the total size of the dataset. If the data object
15064      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15065      * the data set size</em>
15066      */
15067     getCount : function(){
15068         return this.data.length || 0;
15069     },
15070
15071     /**
15072      * Gets the total number of records in the dataset as returned by the server.
15073      * <p>
15074      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15075      * the dataset size</em>
15076      */
15077     getTotalCount : function(){
15078         return this.totalLength || 0;
15079     },
15080
15081     /**
15082      * Returns the sort state of the Store as an object with two properties:
15083      * <pre><code>
15084  field {String} The name of the field by which the Records are sorted
15085  direction {String} The sort order, "ASC" or "DESC"
15086      * </code></pre>
15087      */
15088     getSortState : function(){
15089         return this.sortInfo;
15090     },
15091
15092     // private
15093     applySort : function(){
15094         if(this.sortInfo && !this.remoteSort){
15095             var s = this.sortInfo, f = s.field;
15096             var st = this.fields.get(f).sortType;
15097             var fn = function(r1, r2){
15098                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15099                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15100             };
15101             this.data.sort(s.direction, fn);
15102             if(this.snapshot && this.snapshot != this.data){
15103                 this.snapshot.sort(s.direction, fn);
15104             }
15105         }
15106     },
15107
15108     /**
15109      * Sets the default sort column and order to be used by the next load operation.
15110      * @param {String} fieldName The name of the field to sort by.
15111      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15112      */
15113     setDefaultSort : function(field, dir){
15114         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15115     },
15116
15117     /**
15118      * Sort the Records.
15119      * If remote sorting is used, the sort is performed on the server, and the cache is
15120      * reloaded. If local sorting is used, the cache is sorted internally.
15121      * @param {String} fieldName The name of the field to sort by.
15122      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15123      */
15124     sort : function(fieldName, dir){
15125         var f = this.fields.get(fieldName);
15126         if(!dir){
15127             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15128             
15129             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15130                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15131             }else{
15132                 dir = f.sortDir;
15133             }
15134         }
15135         this.sortToggle[f.name] = dir;
15136         this.sortInfo = {field: f.name, direction: dir};
15137         if(!this.remoteSort){
15138             this.applySort();
15139             this.fireEvent("datachanged", this);
15140         }else{
15141             this.load(this.lastOptions);
15142         }
15143     },
15144
15145     /**
15146      * Calls the specified function for each of the Records in the cache.
15147      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15148      * Returning <em>false</em> aborts and exits the iteration.
15149      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15150      */
15151     each : function(fn, scope){
15152         this.data.each(fn, scope);
15153     },
15154
15155     /**
15156      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15157      * (e.g., during paging).
15158      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15159      */
15160     getModifiedRecords : function(){
15161         return this.modified;
15162     },
15163
15164     // private
15165     createFilterFn : function(property, value, anyMatch){
15166         if(!value.exec){ // not a regex
15167             value = String(value);
15168             if(value.length == 0){
15169                 return false;
15170             }
15171             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15172         }
15173         return function(r){
15174             return value.test(r.data[property]);
15175         };
15176     },
15177
15178     /**
15179      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15180      * @param {String} property A field on your records
15181      * @param {Number} start The record index to start at (defaults to 0)
15182      * @param {Number} end The last record index to include (defaults to length - 1)
15183      * @return {Number} The sum
15184      */
15185     sum : function(property, start, end){
15186         var rs = this.data.items, v = 0;
15187         start = start || 0;
15188         end = (end || end === 0) ? end : rs.length-1;
15189
15190         for(var i = start; i <= end; i++){
15191             v += (rs[i].data[property] || 0);
15192         }
15193         return v;
15194     },
15195
15196     /**
15197      * Filter the records by a specified property.
15198      * @param {String} field A field on your records
15199      * @param {String/RegExp} value Either a string that the field
15200      * should start with or a RegExp to test against the field
15201      * @param {Boolean} anyMatch True to match any part not just the beginning
15202      */
15203     filter : function(property, value, anyMatch){
15204         var fn = this.createFilterFn(property, value, anyMatch);
15205         return fn ? this.filterBy(fn) : this.clearFilter();
15206     },
15207
15208     /**
15209      * Filter by a function. The specified function will be called with each
15210      * record in this data source. If the function returns true the record is included,
15211      * otherwise it is filtered.
15212      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15213      * @param {Object} scope (optional) The scope of the function (defaults to this)
15214      */
15215     filterBy : function(fn, scope){
15216         this.snapshot = this.snapshot || this.data;
15217         this.data = this.queryBy(fn, scope||this);
15218         this.fireEvent("datachanged", this);
15219     },
15220
15221     /**
15222      * Query the records by a specified property.
15223      * @param {String} field A field on your records
15224      * @param {String/RegExp} value Either a string that the field
15225      * should start with or a RegExp to test against the field
15226      * @param {Boolean} anyMatch True to match any part not just the beginning
15227      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15228      */
15229     query : function(property, value, anyMatch){
15230         var fn = this.createFilterFn(property, value, anyMatch);
15231         return fn ? this.queryBy(fn) : this.data.clone();
15232     },
15233
15234     /**
15235      * Query by a function. The specified function will be called with each
15236      * record in this data source. If the function returns true the record is included
15237      * in the results.
15238      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15239      * @param {Object} scope (optional) The scope of the function (defaults to this)
15240       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15241      **/
15242     queryBy : function(fn, scope){
15243         var data = this.snapshot || this.data;
15244         return data.filterBy(fn, scope||this);
15245     },
15246
15247     /**
15248      * Collects unique values for a particular dataIndex from this store.
15249      * @param {String} dataIndex The property to collect
15250      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15251      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15252      * @return {Array} An array of the unique values
15253      **/
15254     collect : function(dataIndex, allowNull, bypassFilter){
15255         var d = (bypassFilter === true && this.snapshot) ?
15256                 this.snapshot.items : this.data.items;
15257         var v, sv, r = [], l = {};
15258         for(var i = 0, len = d.length; i < len; i++){
15259             v = d[i].data[dataIndex];
15260             sv = String(v);
15261             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15262                 l[sv] = true;
15263                 r[r.length] = v;
15264             }
15265         }
15266         return r;
15267     },
15268
15269     /**
15270      * Revert to a view of the Record cache with no filtering applied.
15271      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15272      */
15273     clearFilter : function(suppressEvent){
15274         if(this.snapshot && this.snapshot != this.data){
15275             this.data = this.snapshot;
15276             delete this.snapshot;
15277             if(suppressEvent !== true){
15278                 this.fireEvent("datachanged", this);
15279             }
15280         }
15281     },
15282
15283     // private
15284     afterEdit : function(record){
15285         if(this.modified.indexOf(record) == -1){
15286             this.modified.push(record);
15287         }
15288         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15289     },
15290     
15291     // private
15292     afterReject : function(record){
15293         this.modified.remove(record);
15294         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15295     },
15296
15297     // private
15298     afterCommit : function(record){
15299         this.modified.remove(record);
15300         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15301     },
15302
15303     /**
15304      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15305      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15306      */
15307     commitChanges : function(){
15308         var m = this.modified.slice(0);
15309         this.modified = [];
15310         for(var i = 0, len = m.length; i < len; i++){
15311             m[i].commit();
15312         }
15313     },
15314
15315     /**
15316      * Cancel outstanding changes on all changed records.
15317      */
15318     rejectChanges : function(){
15319         var m = this.modified.slice(0);
15320         this.modified = [];
15321         for(var i = 0, len = m.length; i < len; i++){
15322             m[i].reject();
15323         }
15324     },
15325
15326     onMetaChange : function(meta, rtype, o){
15327         this.recordType = rtype;
15328         this.fields = rtype.prototype.fields;
15329         delete this.snapshot;
15330         this.sortInfo = meta.sortInfo || this.sortInfo;
15331         this.modified = [];
15332         this.fireEvent('metachange', this, this.reader.meta);
15333     },
15334     
15335     moveIndex : function(data, type)
15336     {
15337         var index = this.indexOf(data);
15338         
15339         var newIndex = index + type;
15340         
15341         this.remove(data);
15342         
15343         this.insert(newIndex, data);
15344         
15345     }
15346 });/*
15347  * Based on:
15348  * Ext JS Library 1.1.1
15349  * Copyright(c) 2006-2007, Ext JS, LLC.
15350  *
15351  * Originally Released Under LGPL - original licence link has changed is not relivant.
15352  *
15353  * Fork - LGPL
15354  * <script type="text/javascript">
15355  */
15356
15357 /**
15358  * @class Roo.data.SimpleStore
15359  * @extends Roo.data.Store
15360  * Small helper class to make creating Stores from Array data easier.
15361  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15362  * @cfg {Array} fields An array of field definition objects, or field name strings.
15363  * @cfg {Object} an existing reader (eg. copied from another store)
15364  * @cfg {Array} data The multi-dimensional array of data
15365  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15366  * @cfg {Roo.data.Reader} reader  [not-required] 
15367  * @constructor
15368  * @param {Object} config
15369  */
15370 Roo.data.SimpleStore = function(config)
15371 {
15372     Roo.data.SimpleStore.superclass.constructor.call(this, {
15373         isLocal : true,
15374         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15375                 id: config.id
15376             },
15377             Roo.data.Record.create(config.fields)
15378         ),
15379         proxy : new Roo.data.MemoryProxy(config.data)
15380     });
15381     this.load();
15382 };
15383 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15384  * Based on:
15385  * Ext JS Library 1.1.1
15386  * Copyright(c) 2006-2007, Ext JS, LLC.
15387  *
15388  * Originally Released Under LGPL - original licence link has changed is not relivant.
15389  *
15390  * Fork - LGPL
15391  * <script type="text/javascript">
15392  */
15393
15394 /**
15395 /**
15396  * @extends Roo.data.Store
15397  * @class Roo.data.JsonStore
15398  * Small helper class to make creating Stores for JSON data easier. <br/>
15399 <pre><code>
15400 var store = new Roo.data.JsonStore({
15401     url: 'get-images.php',
15402     root: 'images',
15403     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15404 });
15405 </code></pre>
15406  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15407  * JsonReader and HttpProxy (unless inline data is provided).</b>
15408  * @cfg {Array} fields An array of field definition objects, or field name strings.
15409  * @constructor
15410  * @param {Object} config
15411  */
15412 Roo.data.JsonStore = function(c){
15413     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15414         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15415         reader: new Roo.data.JsonReader(c, c.fields)
15416     }));
15417 };
15418 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15419  * Based on:
15420  * Ext JS Library 1.1.1
15421  * Copyright(c) 2006-2007, Ext JS, LLC.
15422  *
15423  * Originally Released Under LGPL - original licence link has changed is not relivant.
15424  *
15425  * Fork - LGPL
15426  * <script type="text/javascript">
15427  */
15428
15429  
15430 Roo.data.Field = function(config){
15431     if(typeof config == "string"){
15432         config = {name: config};
15433     }
15434     Roo.apply(this, config);
15435     
15436     if(!this.type){
15437         this.type = "auto";
15438     }
15439     
15440     var st = Roo.data.SortTypes;
15441     // named sortTypes are supported, here we look them up
15442     if(typeof this.sortType == "string"){
15443         this.sortType = st[this.sortType];
15444     }
15445     
15446     // set default sortType for strings and dates
15447     if(!this.sortType){
15448         switch(this.type){
15449             case "string":
15450                 this.sortType = st.asUCString;
15451                 break;
15452             case "date":
15453                 this.sortType = st.asDate;
15454                 break;
15455             default:
15456                 this.sortType = st.none;
15457         }
15458     }
15459
15460     // define once
15461     var stripRe = /[\$,%]/g;
15462
15463     // prebuilt conversion function for this field, instead of
15464     // switching every time we're reading a value
15465     if(!this.convert){
15466         var cv, dateFormat = this.dateFormat;
15467         switch(this.type){
15468             case "":
15469             case "auto":
15470             case undefined:
15471                 cv = function(v){ return v; };
15472                 break;
15473             case "string":
15474                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15475                 break;
15476             case "int":
15477                 cv = function(v){
15478                     return v !== undefined && v !== null && v !== '' ?
15479                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15480                     };
15481                 break;
15482             case "float":
15483                 cv = function(v){
15484                     return v !== undefined && v !== null && v !== '' ?
15485                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15486                     };
15487                 break;
15488             case "bool":
15489             case "boolean":
15490                 cv = function(v){ return v === true || v === "true" || v == 1; };
15491                 break;
15492             case "date":
15493                 cv = function(v){
15494                     if(!v){
15495                         return '';
15496                     }
15497                     if(v instanceof Date){
15498                         return v;
15499                     }
15500                     if(dateFormat){
15501                         if(dateFormat == "timestamp"){
15502                             return new Date(v*1000);
15503                         }
15504                         return Date.parseDate(v, dateFormat);
15505                     }
15506                     var parsed = Date.parse(v);
15507                     return parsed ? new Date(parsed) : null;
15508                 };
15509              break;
15510             
15511         }
15512         this.convert = cv;
15513     }
15514 };
15515
15516 Roo.data.Field.prototype = {
15517     dateFormat: null,
15518     defaultValue: "",
15519     mapping: null,
15520     sortType : null,
15521     sortDir : "ASC"
15522 };/*
15523  * Based on:
15524  * Ext JS Library 1.1.1
15525  * Copyright(c) 2006-2007, Ext JS, LLC.
15526  *
15527  * Originally Released Under LGPL - original licence link has changed is not relivant.
15528  *
15529  * Fork - LGPL
15530  * <script type="text/javascript">
15531  */
15532  
15533 // Base class for reading structured data from a data source.  This class is intended to be
15534 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15535
15536 /**
15537  * @class Roo.data.DataReader
15538  * @abstract
15539  * Base class for reading structured data from a data source.  This class is intended to be
15540  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15541  */
15542
15543 Roo.data.DataReader = function(meta, recordType){
15544     
15545     this.meta = meta;
15546     
15547     this.recordType = recordType instanceof Array ? 
15548         Roo.data.Record.create(recordType) : recordType;
15549 };
15550
15551 Roo.data.DataReader.prototype = {
15552     
15553     
15554     readerType : 'Data',
15555      /**
15556      * Create an empty record
15557      * @param {Object} data (optional) - overlay some values
15558      * @return {Roo.data.Record} record created.
15559      */
15560     newRow :  function(d) {
15561         var da =  {};
15562         this.recordType.prototype.fields.each(function(c) {
15563             switch( c.type) {
15564                 case 'int' : da[c.name] = 0; break;
15565                 case 'date' : da[c.name] = new Date(); break;
15566                 case 'float' : da[c.name] = 0.0; break;
15567                 case 'boolean' : da[c.name] = false; break;
15568                 default : da[c.name] = ""; break;
15569             }
15570             
15571         });
15572         return new this.recordType(Roo.apply(da, d));
15573     }
15574     
15575     
15576 };/*
15577  * Based on:
15578  * Ext JS Library 1.1.1
15579  * Copyright(c) 2006-2007, Ext JS, LLC.
15580  *
15581  * Originally Released Under LGPL - original licence link has changed is not relivant.
15582  *
15583  * Fork - LGPL
15584  * <script type="text/javascript">
15585  */
15586
15587 /**
15588  * @class Roo.data.DataProxy
15589  * @extends Roo.data.Observable
15590  * @abstract
15591  * This class is an abstract base class for implementations which provide retrieval of
15592  * unformatted data objects.<br>
15593  * <p>
15594  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15595  * (of the appropriate type which knows how to parse the data object) to provide a block of
15596  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15597  * <p>
15598  * Custom implementations must implement the load method as described in
15599  * {@link Roo.data.HttpProxy#load}.
15600  */
15601 Roo.data.DataProxy = function(){
15602     this.addEvents({
15603         /**
15604          * @event beforeload
15605          * Fires before a network request is made to retrieve a data object.
15606          * @param {Object} This DataProxy object.
15607          * @param {Object} params The params parameter to the load function.
15608          */
15609         beforeload : true,
15610         /**
15611          * @event load
15612          * Fires before the load method's callback is called.
15613          * @param {Object} This DataProxy object.
15614          * @param {Object} o The data object.
15615          * @param {Object} arg The callback argument object passed to the load function.
15616          */
15617         load : true,
15618         /**
15619          * @event loadexception
15620          * Fires if an Exception occurs during data retrieval.
15621          * @param {Object} This DataProxy object.
15622          * @param {Object} o The data object.
15623          * @param {Object} arg The callback argument object passed to the load function.
15624          * @param {Object} e The Exception.
15625          */
15626         loadexception : true
15627     });
15628     Roo.data.DataProxy.superclass.constructor.call(this);
15629 };
15630
15631 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15632
15633     /**
15634      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15635      */
15636 /*
15637  * Based on:
15638  * Ext JS Library 1.1.1
15639  * Copyright(c) 2006-2007, Ext JS, LLC.
15640  *
15641  * Originally Released Under LGPL - original licence link has changed is not relivant.
15642  *
15643  * Fork - LGPL
15644  * <script type="text/javascript">
15645  */
15646 /**
15647  * @class Roo.data.MemoryProxy
15648  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15649  * to the Reader when its load method is called.
15650  * @constructor
15651  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15652  */
15653 Roo.data.MemoryProxy = function(data){
15654     if (data.data) {
15655         data = data.data;
15656     }
15657     Roo.data.MemoryProxy.superclass.constructor.call(this);
15658     this.data = data;
15659 };
15660
15661 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15662     
15663     /**
15664      * Load data from the requested source (in this case an in-memory
15665      * data object passed to the constructor), read the data object into
15666      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15667      * process that block using the passed callback.
15668      * @param {Object} params This parameter is not used by the MemoryProxy class.
15669      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15670      * object into a block of Roo.data.Records.
15671      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15672      * The function must be passed <ul>
15673      * <li>The Record block object</li>
15674      * <li>The "arg" argument from the load function</li>
15675      * <li>A boolean success indicator</li>
15676      * </ul>
15677      * @param {Object} scope The scope in which to call the callback
15678      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15679      */
15680     load : function(params, reader, callback, scope, arg){
15681         params = params || {};
15682         var result;
15683         try {
15684             result = reader.readRecords(params.data ? params.data :this.data);
15685         }catch(e){
15686             this.fireEvent("loadexception", this, arg, null, e);
15687             callback.call(scope, null, arg, false);
15688             return;
15689         }
15690         callback.call(scope, result, arg, true);
15691     },
15692     
15693     // private
15694     update : function(params, records){
15695         
15696     }
15697 });/*
15698  * Based on:
15699  * Ext JS Library 1.1.1
15700  * Copyright(c) 2006-2007, Ext JS, LLC.
15701  *
15702  * Originally Released Under LGPL - original licence link has changed is not relivant.
15703  *
15704  * Fork - LGPL
15705  * <script type="text/javascript">
15706  */
15707 /**
15708  * @class Roo.data.HttpProxy
15709  * @extends Roo.data.DataProxy
15710  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15711  * configured to reference a certain URL.<br><br>
15712  * <p>
15713  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15714  * from which the running page was served.<br><br>
15715  * <p>
15716  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15717  * <p>
15718  * Be aware that to enable the browser to parse an XML document, the server must set
15719  * the Content-Type header in the HTTP response to "text/xml".
15720  * @constructor
15721  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15722  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15723  * will be used to make the request.
15724  */
15725 Roo.data.HttpProxy = function(conn){
15726     Roo.data.HttpProxy.superclass.constructor.call(this);
15727     // is conn a conn config or a real conn?
15728     this.conn = conn;
15729     this.useAjax = !conn || !conn.events;
15730   
15731 };
15732
15733 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15734     // thse are take from connection...
15735     
15736     /**
15737      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15738      */
15739     /**
15740      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15741      * extra parameters to each request made by this object. (defaults to undefined)
15742      */
15743     /**
15744      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15745      *  to each request made by this object. (defaults to undefined)
15746      */
15747     /**
15748      * @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)
15749      */
15750     /**
15751      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15752      */
15753      /**
15754      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15755      * @type Boolean
15756      */
15757   
15758
15759     /**
15760      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15761      * @type Boolean
15762      */
15763     /**
15764      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15765      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15766      * a finer-grained basis than the DataProxy events.
15767      */
15768     getConnection : function(){
15769         return this.useAjax ? Roo.Ajax : this.conn;
15770     },
15771
15772     /**
15773      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15774      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15775      * process that block using the passed callback.
15776      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15777      * for the request to the remote server.
15778      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15779      * object into a block of Roo.data.Records.
15780      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15781      * The function must be passed <ul>
15782      * <li>The Record block object</li>
15783      * <li>The "arg" argument from the load function</li>
15784      * <li>A boolean success indicator</li>
15785      * </ul>
15786      * @param {Object} scope The scope in which to call the callback
15787      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15788      */
15789     load : function(params, reader, callback, scope, arg){
15790         if(this.fireEvent("beforeload", this, params) !== false){
15791             var  o = {
15792                 params : params || {},
15793                 request: {
15794                     callback : callback,
15795                     scope : scope,
15796                     arg : arg
15797                 },
15798                 reader: reader,
15799                 callback : this.loadResponse,
15800                 scope: this
15801             };
15802             if(this.useAjax){
15803                 Roo.applyIf(o, this.conn);
15804                 if(this.activeRequest){
15805                     Roo.Ajax.abort(this.activeRequest);
15806                 }
15807                 this.activeRequest = Roo.Ajax.request(o);
15808             }else{
15809                 this.conn.request(o);
15810             }
15811         }else{
15812             callback.call(scope||this, null, arg, false);
15813         }
15814     },
15815
15816     // private
15817     loadResponse : function(o, success, response){
15818         delete this.activeRequest;
15819         if(!success){
15820             this.fireEvent("loadexception", this, o, response);
15821             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15822             return;
15823         }
15824         var result;
15825         try {
15826             result = o.reader.read(response);
15827         }catch(e){
15828             this.fireEvent("loadexception", this, o, response, e);
15829             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15830             return;
15831         }
15832         
15833         this.fireEvent("load", this, o, o.request.arg);
15834         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15835     },
15836
15837     // private
15838     update : function(dataSet){
15839
15840     },
15841
15842     // private
15843     updateResponse : function(dataSet){
15844
15845     }
15846 });/*
15847  * Based on:
15848  * Ext JS Library 1.1.1
15849  * Copyright(c) 2006-2007, Ext JS, LLC.
15850  *
15851  * Originally Released Under LGPL - original licence link has changed is not relivant.
15852  *
15853  * Fork - LGPL
15854  * <script type="text/javascript">
15855  */
15856
15857 /**
15858  * @class Roo.data.ScriptTagProxy
15859  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15860  * other than the originating domain of the running page.<br><br>
15861  * <p>
15862  * <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
15863  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15864  * <p>
15865  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15866  * source code that is used as the source inside a &lt;script> tag.<br><br>
15867  * <p>
15868  * In order for the browser to process the returned data, the server must wrap the data object
15869  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15870  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15871  * depending on whether the callback name was passed:
15872  * <p>
15873  * <pre><code>
15874 boolean scriptTag = false;
15875 String cb = request.getParameter("callback");
15876 if (cb != null) {
15877     scriptTag = true;
15878     response.setContentType("text/javascript");
15879 } else {
15880     response.setContentType("application/x-json");
15881 }
15882 Writer out = response.getWriter();
15883 if (scriptTag) {
15884     out.write(cb + "(");
15885 }
15886 out.print(dataBlock.toJsonString());
15887 if (scriptTag) {
15888     out.write(");");
15889 }
15890 </pre></code>
15891  *
15892  * @constructor
15893  * @param {Object} config A configuration object.
15894  */
15895 Roo.data.ScriptTagProxy = function(config){
15896     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15897     Roo.apply(this, config);
15898     this.head = document.getElementsByTagName("head")[0];
15899 };
15900
15901 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15902
15903 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15904     /**
15905      * @cfg {String} url The URL from which to request the data object.
15906      */
15907     /**
15908      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15909      */
15910     timeout : 30000,
15911     /**
15912      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15913      * the server the name of the callback function set up by the load call to process the returned data object.
15914      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15915      * javascript output which calls this named function passing the data object as its only parameter.
15916      */
15917     callbackParam : "callback",
15918     /**
15919      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15920      * name to the request.
15921      */
15922     nocache : true,
15923
15924     /**
15925      * Load data from the configured URL, read the data object into
15926      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15927      * process that block using the passed callback.
15928      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15929      * for the request to the remote server.
15930      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15931      * object into a block of Roo.data.Records.
15932      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15933      * The function must be passed <ul>
15934      * <li>The Record block object</li>
15935      * <li>The "arg" argument from the load function</li>
15936      * <li>A boolean success indicator</li>
15937      * </ul>
15938      * @param {Object} scope The scope in which to call the callback
15939      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15940      */
15941     load : function(params, reader, callback, scope, arg){
15942         if(this.fireEvent("beforeload", this, params) !== false){
15943
15944             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15945
15946             var url = this.url;
15947             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15948             if(this.nocache){
15949                 url += "&_dc=" + (new Date().getTime());
15950             }
15951             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15952             var trans = {
15953                 id : transId,
15954                 cb : "stcCallback"+transId,
15955                 scriptId : "stcScript"+transId,
15956                 params : params,
15957                 arg : arg,
15958                 url : url,
15959                 callback : callback,
15960                 scope : scope,
15961                 reader : reader
15962             };
15963             var conn = this;
15964
15965             window[trans.cb] = function(o){
15966                 conn.handleResponse(o, trans);
15967             };
15968
15969             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15970
15971             if(this.autoAbort !== false){
15972                 this.abort();
15973             }
15974
15975             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15976
15977             var script = document.createElement("script");
15978             script.setAttribute("src", url);
15979             script.setAttribute("type", "text/javascript");
15980             script.setAttribute("id", trans.scriptId);
15981             this.head.appendChild(script);
15982
15983             this.trans = trans;
15984         }else{
15985             callback.call(scope||this, null, arg, false);
15986         }
15987     },
15988
15989     // private
15990     isLoading : function(){
15991         return this.trans ? true : false;
15992     },
15993
15994     /**
15995      * Abort the current server request.
15996      */
15997     abort : function(){
15998         if(this.isLoading()){
15999             this.destroyTrans(this.trans);
16000         }
16001     },
16002
16003     // private
16004     destroyTrans : function(trans, isLoaded){
16005         this.head.removeChild(document.getElementById(trans.scriptId));
16006         clearTimeout(trans.timeoutId);
16007         if(isLoaded){
16008             window[trans.cb] = undefined;
16009             try{
16010                 delete window[trans.cb];
16011             }catch(e){}
16012         }else{
16013             // if hasn't been loaded, wait for load to remove it to prevent script error
16014             window[trans.cb] = function(){
16015                 window[trans.cb] = undefined;
16016                 try{
16017                     delete window[trans.cb];
16018                 }catch(e){}
16019             };
16020         }
16021     },
16022
16023     // private
16024     handleResponse : function(o, trans){
16025         this.trans = false;
16026         this.destroyTrans(trans, true);
16027         var result;
16028         try {
16029             result = trans.reader.readRecords(o);
16030         }catch(e){
16031             this.fireEvent("loadexception", this, o, trans.arg, e);
16032             trans.callback.call(trans.scope||window, null, trans.arg, false);
16033             return;
16034         }
16035         this.fireEvent("load", this, o, trans.arg);
16036         trans.callback.call(trans.scope||window, result, trans.arg, true);
16037     },
16038
16039     // private
16040     handleFailure : function(trans){
16041         this.trans = false;
16042         this.destroyTrans(trans, false);
16043         this.fireEvent("loadexception", this, null, trans.arg);
16044         trans.callback.call(trans.scope||window, null, trans.arg, false);
16045     }
16046 });/*
16047  * Based on:
16048  * Ext JS Library 1.1.1
16049  * Copyright(c) 2006-2007, Ext JS, LLC.
16050  *
16051  * Originally Released Under LGPL - original licence link has changed is not relivant.
16052  *
16053  * Fork - LGPL
16054  * <script type="text/javascript">
16055  */
16056
16057 /**
16058  * @class Roo.data.JsonReader
16059  * @extends Roo.data.DataReader
16060  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16061  * based on mappings in a provided Roo.data.Record constructor.
16062  * 
16063  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16064  * in the reply previously. 
16065  * 
16066  * <p>
16067  * Example code:
16068  * <pre><code>
16069 var RecordDef = Roo.data.Record.create([
16070     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16071     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16072 ]);
16073 var myReader = new Roo.data.JsonReader({
16074     totalProperty: "results",    // The property which contains the total dataset size (optional)
16075     root: "rows",                // The property which contains an Array of row objects
16076     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16077 }, RecordDef);
16078 </code></pre>
16079  * <p>
16080  * This would consume a JSON file like this:
16081  * <pre><code>
16082 { 'results': 2, 'rows': [
16083     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16084     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16085 }
16086 </code></pre>
16087  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16088  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16089  * paged from the remote server.
16090  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16091  * @cfg {String} root name of the property which contains the Array of row objects.
16092  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16093  * @cfg {Array} fields Array of field definition objects
16094  * @constructor
16095  * Create a new JsonReader
16096  * @param {Object} meta Metadata configuration options
16097  * @param {Object} recordType Either an Array of field definition objects,
16098  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16099  */
16100 Roo.data.JsonReader = function(meta, recordType){
16101     
16102     meta = meta || {};
16103     // set some defaults:
16104     Roo.applyIf(meta, {
16105         totalProperty: 'total',
16106         successProperty : 'success',
16107         root : 'data',
16108         id : 'id'
16109     });
16110     
16111     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16112 };
16113 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16114     
16115     readerType : 'Json',
16116     
16117     /**
16118      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16119      * Used by Store query builder to append _requestMeta to params.
16120      * 
16121      */
16122     metaFromRemote : false,
16123     /**
16124      * This method is only used by a DataProxy which has retrieved data from a remote server.
16125      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16126      * @return {Object} data A data block which is used by an Roo.data.Store object as
16127      * a cache of Roo.data.Records.
16128      */
16129     read : function(response){
16130         var json = response.responseText;
16131        
16132         var o = /* eval:var:o */ eval("("+json+")");
16133         if(!o) {
16134             throw {message: "JsonReader.read: Json object not found"};
16135         }
16136         
16137         if(o.metaData){
16138             
16139             delete this.ef;
16140             this.metaFromRemote = true;
16141             this.meta = o.metaData;
16142             this.recordType = Roo.data.Record.create(o.metaData.fields);
16143             this.onMetaChange(this.meta, this.recordType, o);
16144         }
16145         return this.readRecords(o);
16146     },
16147
16148     // private function a store will implement
16149     onMetaChange : function(meta, recordType, o){
16150
16151     },
16152
16153     /**
16154          * @ignore
16155          */
16156     simpleAccess: function(obj, subsc) {
16157         return obj[subsc];
16158     },
16159
16160         /**
16161          * @ignore
16162          */
16163     getJsonAccessor: function(){
16164         var re = /[\[\.]/;
16165         return function(expr) {
16166             try {
16167                 return(re.test(expr))
16168                     ? new Function("obj", "return obj." + expr)
16169                     : function(obj){
16170                         return obj[expr];
16171                     };
16172             } catch(e){}
16173             return Roo.emptyFn;
16174         };
16175     }(),
16176
16177     /**
16178      * Create a data block containing Roo.data.Records from an XML document.
16179      * @param {Object} o An object which contains an Array of row objects in the property specified
16180      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16181      * which contains the total size of the dataset.
16182      * @return {Object} data A data block which is used by an Roo.data.Store object as
16183      * a cache of Roo.data.Records.
16184      */
16185     readRecords : function(o){
16186         /**
16187          * After any data loads, the raw JSON data is available for further custom processing.
16188          * @type Object
16189          */
16190         this.o = o;
16191         var s = this.meta, Record = this.recordType,
16192             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16193
16194 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16195         if (!this.ef) {
16196             if(s.totalProperty) {
16197                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16198                 }
16199                 if(s.successProperty) {
16200                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16201                 }
16202                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16203                 if (s.id) {
16204                         var g = this.getJsonAccessor(s.id);
16205                         this.getId = function(rec) {
16206                                 var r = g(rec);  
16207                                 return (r === undefined || r === "") ? null : r;
16208                         };
16209                 } else {
16210                         this.getId = function(){return null;};
16211                 }
16212             this.ef = [];
16213             for(var jj = 0; jj < fl; jj++){
16214                 f = fi[jj];
16215                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16216                 this.ef[jj] = this.getJsonAccessor(map);
16217             }
16218         }
16219
16220         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16221         if(s.totalProperty){
16222             var vt = parseInt(this.getTotal(o), 10);
16223             if(!isNaN(vt)){
16224                 totalRecords = vt;
16225             }
16226         }
16227         if(s.successProperty){
16228             var vs = this.getSuccess(o);
16229             if(vs === false || vs === 'false'){
16230                 success = false;
16231             }
16232         }
16233         var records = [];
16234         for(var i = 0; i < c; i++){
16235                 var n = root[i];
16236             var values = {};
16237             var id = this.getId(n);
16238             for(var j = 0; j < fl; j++){
16239                 f = fi[j];
16240             var v = this.ef[j](n);
16241             if (!f.convert) {
16242                 Roo.log('missing convert for ' + f.name);
16243                 Roo.log(f);
16244                 continue;
16245             }
16246             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16247             }
16248             var record = new Record(values, id);
16249             record.json = n;
16250             records[i] = record;
16251         }
16252         return {
16253             raw : o,
16254             success : success,
16255             records : records,
16256             totalRecords : totalRecords
16257         };
16258     },
16259     // used when loading children.. @see loadDataFromChildren
16260     toLoadData: function(rec)
16261     {
16262         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16263         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16264         return { data : data, total : data.length };
16265         
16266     }
16267 });/*
16268  * Based on:
16269  * Ext JS Library 1.1.1
16270  * Copyright(c) 2006-2007, Ext JS, LLC.
16271  *
16272  * Originally Released Under LGPL - original licence link has changed is not relivant.
16273  *
16274  * Fork - LGPL
16275  * <script type="text/javascript">
16276  */
16277
16278 /**
16279  * @class Roo.data.ArrayReader
16280  * @extends Roo.data.DataReader
16281  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16282  * Each element of that Array represents a row of data fields. The
16283  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16284  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16285  * <p>
16286  * Example code:.
16287  * <pre><code>
16288 var RecordDef = Roo.data.Record.create([
16289     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16290     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16291 ]);
16292 var myReader = new Roo.data.ArrayReader({
16293     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16294 }, RecordDef);
16295 </code></pre>
16296  * <p>
16297  * This would consume an Array like this:
16298  * <pre><code>
16299 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16300   </code></pre>
16301  
16302  * @constructor
16303  * Create a new JsonReader
16304  * @param {Object} meta Metadata configuration options.
16305  * @param {Object|Array} recordType Either an Array of field definition objects
16306  * 
16307  * @cfg {Array} fields Array of field definition objects
16308  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16309  * as specified to {@link Roo.data.Record#create},
16310  * or an {@link Roo.data.Record} object
16311  *
16312  * 
16313  * created using {@link Roo.data.Record#create}.
16314  */
16315 Roo.data.ArrayReader = function(meta, recordType)
16316 {    
16317     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16318 };
16319
16320 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16321     
16322       /**
16323      * Create a data block containing Roo.data.Records from an XML document.
16324      * @param {Object} o An Array of row objects which represents the dataset.
16325      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16326      * a cache of Roo.data.Records.
16327      */
16328     readRecords : function(o)
16329     {
16330         var sid = this.meta ? this.meta.id : null;
16331         var recordType = this.recordType, fields = recordType.prototype.fields;
16332         var records = [];
16333         var root = o;
16334         for(var i = 0; i < root.length; i++){
16335             var n = root[i];
16336             var values = {};
16337             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16338             for(var j = 0, jlen = fields.length; j < jlen; j++){
16339                 var f = fields.items[j];
16340                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16341                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16342                 v = f.convert(v);
16343                 values[f.name] = v;
16344             }
16345             var record = new recordType(values, id);
16346             record.json = n;
16347             records[records.length] = record;
16348         }
16349         return {
16350             records : records,
16351             totalRecords : records.length
16352         };
16353     },
16354     // used when loading children.. @see loadDataFromChildren
16355     toLoadData: function(rec)
16356     {
16357         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16358         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16359         
16360     }
16361     
16362     
16363 });/*
16364  * - LGPL
16365  * * 
16366  */
16367
16368 /**
16369  * @class Roo.bootstrap.ComboBox
16370  * @extends Roo.bootstrap.TriggerField
16371  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16372  * @cfg {Boolean} append (true|false) default false
16373  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16374  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16375  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16376  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16377  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16378  * @cfg {Boolean} animate default true
16379  * @cfg {Boolean} emptyResultText only for touch device
16380  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16381  * @cfg {String} emptyTitle default ''
16382  * @cfg {Number} width fixed with? experimental
16383  * @constructor
16384  * Create a new ComboBox.
16385  * @param {Object} config Configuration options
16386  */
16387 Roo.bootstrap.ComboBox = function(config){
16388     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16389     this.addEvents({
16390         /**
16391          * @event expand
16392          * Fires when the dropdown list is expanded
16393         * @param {Roo.bootstrap.ComboBox} combo This combo box
16394         */
16395         'expand' : true,
16396         /**
16397          * @event collapse
16398          * Fires when the dropdown list is collapsed
16399         * @param {Roo.bootstrap.ComboBox} combo This combo box
16400         */
16401         'collapse' : true,
16402         /**
16403          * @event beforeselect
16404          * Fires before a list item is selected. Return false to cancel the selection.
16405         * @param {Roo.bootstrap.ComboBox} combo This combo box
16406         * @param {Roo.data.Record} record The data record returned from the underlying store
16407         * @param {Number} index The index of the selected item in the dropdown list
16408         */
16409         'beforeselect' : true,
16410         /**
16411          * @event select
16412          * Fires when a list item is selected
16413         * @param {Roo.bootstrap.ComboBox} combo This combo box
16414         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16415         * @param {Number} index The index of the selected item in the dropdown list
16416         */
16417         'select' : true,
16418         /**
16419          * @event beforequery
16420          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16421          * The event object passed has these properties:
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         * @param {String} query The query
16424         * @param {Boolean} forceAll true to force "all" query
16425         * @param {Boolean} cancel true to cancel the query
16426         * @param {Object} e The query event object
16427         */
16428         'beforequery': true,
16429          /**
16430          * @event add
16431          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16432         * @param {Roo.bootstrap.ComboBox} combo This combo box
16433         */
16434         'add' : true,
16435         /**
16436          * @event edit
16437          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16438         * @param {Roo.bootstrap.ComboBox} combo This combo box
16439         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16440         */
16441         'edit' : true,
16442         /**
16443          * @event remove
16444          * Fires when the remove value from the combobox array
16445         * @param {Roo.bootstrap.ComboBox} combo This combo box
16446         */
16447         'remove' : true,
16448         /**
16449          * @event afterremove
16450          * Fires when the remove value from the combobox array
16451         * @param {Roo.bootstrap.ComboBox} combo This combo box
16452         */
16453         'afterremove' : true,
16454         /**
16455          * @event specialfilter
16456          * Fires when specialfilter
16457             * @param {Roo.bootstrap.ComboBox} combo This combo box
16458             */
16459         'specialfilter' : true,
16460         /**
16461          * @event tick
16462          * Fires when tick the element
16463             * @param {Roo.bootstrap.ComboBox} combo This combo box
16464             */
16465         'tick' : true,
16466         /**
16467          * @event touchviewdisplay
16468          * Fires when touch view require special display (default is using displayField)
16469             * @param {Roo.bootstrap.ComboBox} combo This combo box
16470             * @param {Object} cfg set html .
16471             */
16472         'touchviewdisplay' : true
16473         
16474     });
16475     
16476     this.item = [];
16477     this.tickItems = [];
16478     
16479     this.selectedIndex = -1;
16480     if(this.mode == 'local'){
16481         if(config.queryDelay === undefined){
16482             this.queryDelay = 10;
16483         }
16484         if(config.minChars === undefined){
16485             this.minChars = 0;
16486         }
16487     }
16488 };
16489
16490 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16491      
16492     /**
16493      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16494      * rendering into an Roo.Editor, defaults to false)
16495      */
16496     /**
16497      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16498      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16499      */
16500     /**
16501      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16502      */
16503     /**
16504      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16505      * the dropdown list (defaults to undefined, with no header element)
16506      */
16507
16508      /**
16509      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16510      */
16511      
16512      /**
16513      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16514      */
16515     listWidth: undefined,
16516     /**
16517      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16518      * mode = 'remote' or 'text' if mode = 'local')
16519      */
16520     displayField: undefined,
16521     
16522     /**
16523      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16524      * mode = 'remote' or 'value' if mode = 'local'). 
16525      * Note: use of a valueField requires the user make a selection
16526      * in order for a value to be mapped.
16527      */
16528     valueField: undefined,
16529     /**
16530      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16531      */
16532     modalTitle : '',
16533     
16534     /**
16535      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16536      * field's data value (defaults to the underlying DOM element's name)
16537      */
16538     hiddenName: undefined,
16539     /**
16540      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16541      */
16542     listClass: '',
16543     /**
16544      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16545      */
16546     selectedClass: 'active',
16547     
16548     /**
16549      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16550      */
16551     shadow:'sides',
16552     /**
16553      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16554      * anchor positions (defaults to 'tl-bl')
16555      */
16556     listAlign: 'tl-bl?',
16557     /**
16558      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16559      */
16560     maxHeight: 300,
16561     /**
16562      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16563      * query specified by the allQuery config option (defaults to 'query')
16564      */
16565     triggerAction: 'query',
16566     /**
16567      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16568      * (defaults to 4, does not apply if editable = false)
16569      */
16570     minChars : 4,
16571     /**
16572      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16573      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16574      */
16575     typeAhead: false,
16576     /**
16577      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16578      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16579      */
16580     queryDelay: 500,
16581     /**
16582      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16583      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16584      */
16585     pageSize: 0,
16586     /**
16587      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16588      * when editable = true (defaults to false)
16589      */
16590     selectOnFocus:false,
16591     /**
16592      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16593      */
16594     queryParam: 'query',
16595     /**
16596      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16597      * when mode = 'remote' (defaults to 'Loading...')
16598      */
16599     loadingText: 'Loading...',
16600     /**
16601      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16602      */
16603     resizable: false,
16604     /**
16605      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16606      */
16607     handleHeight : 8,
16608     /**
16609      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16610      * traditional select (defaults to true)
16611      */
16612     editable: true,
16613     /**
16614      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16615      */
16616     allQuery: '',
16617     /**
16618      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16619      */
16620     mode: 'remote',
16621     /**
16622      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16623      * listWidth has a higher value)
16624      */
16625     minListWidth : 70,
16626     /**
16627      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16628      * allow the user to set arbitrary text into the field (defaults to false)
16629      */
16630     forceSelection:false,
16631     /**
16632      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16633      * if typeAhead = true (defaults to 250)
16634      */
16635     typeAheadDelay : 250,
16636     /**
16637      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16638      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16639      */
16640     valueNotFoundText : undefined,
16641     /**
16642      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16643      */
16644     blockFocus : false,
16645     
16646     /**
16647      * @cfg {Boolean} disableClear Disable showing of clear button.
16648      */
16649     disableClear : false,
16650     /**
16651      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16652      */
16653     alwaysQuery : false,
16654     
16655     /**
16656      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16657      */
16658     multiple : false,
16659     
16660     /**
16661      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16662      */
16663     invalidClass : "has-warning",
16664     
16665     /**
16666      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16667      */
16668     validClass : "has-success",
16669     
16670     /**
16671      * @cfg {Boolean} specialFilter (true|false) special filter default false
16672      */
16673     specialFilter : false,
16674     
16675     /**
16676      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16677      */
16678     mobileTouchView : true,
16679     
16680     /**
16681      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16682      */
16683     useNativeIOS : false,
16684     
16685     /**
16686      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16687      */
16688     mobile_restrict_height : false,
16689     
16690     ios_options : false,
16691     
16692     //private
16693     addicon : false,
16694     editicon: false,
16695     
16696     page: 0,
16697     hasQuery: false,
16698     append: false,
16699     loadNext: false,
16700     autoFocus : true,
16701     tickable : false,
16702     btnPosition : 'right',
16703     triggerList : true,
16704     showToggleBtn : true,
16705     animate : true,
16706     emptyResultText: 'Empty',
16707     triggerText : 'Select',
16708     emptyTitle : '',
16709     width : false,
16710     
16711     // element that contains real text value.. (when hidden is used..)
16712     
16713     getAutoCreate : function()
16714     {   
16715         var cfg = false;
16716         //render
16717         /*
16718          * Render classic select for iso
16719          */
16720         
16721         if(Roo.isIOS && this.useNativeIOS){
16722             cfg = this.getAutoCreateNativeIOS();
16723             return cfg;
16724         }
16725         
16726         /*
16727          * Touch Devices
16728          */
16729         
16730         if(Roo.isTouch && this.mobileTouchView){
16731             cfg = this.getAutoCreateTouchView();
16732             return cfg;;
16733         }
16734         
16735         /*
16736          *  Normal ComboBox
16737          */
16738         if(!this.tickable){
16739             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16740             return cfg;
16741         }
16742         
16743         /*
16744          *  ComboBox with tickable selections
16745          */
16746              
16747         var align = this.labelAlign || this.parentLabelAlign();
16748         
16749         cfg = {
16750             cls : 'form-group roo-combobox-tickable' //input-group
16751         };
16752         
16753         var btn_text_select = '';
16754         var btn_text_done = '';
16755         var btn_text_cancel = '';
16756         
16757         if (this.btn_text_show) {
16758             btn_text_select = 'Select';
16759             btn_text_done = 'Done';
16760             btn_text_cancel = 'Cancel'; 
16761         }
16762         
16763         var buttons = {
16764             tag : 'div',
16765             cls : 'tickable-buttons',
16766             cn : [
16767                 {
16768                     tag : 'button',
16769                     type : 'button',
16770                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16771                     //html : this.triggerText
16772                     html: btn_text_select
16773                 },
16774                 {
16775                     tag : 'button',
16776                     type : 'button',
16777                     name : 'ok',
16778                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16779                     //html : 'Done'
16780                     html: btn_text_done
16781                 },
16782                 {
16783                     tag : 'button',
16784                     type : 'button',
16785                     name : 'cancel',
16786                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16787                     //html : 'Cancel'
16788                     html: btn_text_cancel
16789                 }
16790             ]
16791         };
16792         
16793         if(this.editable){
16794             buttons.cn.unshift({
16795                 tag: 'input',
16796                 cls: 'roo-select2-search-field-input'
16797             });
16798         }
16799         
16800         var _this = this;
16801         
16802         Roo.each(buttons.cn, function(c){
16803             if (_this.size) {
16804                 c.cls += ' btn-' + _this.size;
16805             }
16806
16807             if (_this.disabled) {
16808                 c.disabled = true;
16809             }
16810         });
16811         
16812         var box = {
16813             tag: 'div',
16814             style : 'display: contents',
16815             cn: [
16816                 {
16817                     tag: 'input',
16818                     type : 'hidden',
16819                     cls: 'form-hidden-field'
16820                 },
16821                 {
16822                     tag: 'ul',
16823                     cls: 'roo-select2-choices',
16824                     cn:[
16825                         {
16826                             tag: 'li',
16827                             cls: 'roo-select2-search-field',
16828                             cn: [
16829                                 buttons
16830                             ]
16831                         }
16832                     ]
16833                 }
16834             ]
16835         };
16836         
16837         var combobox = {
16838             cls: 'roo-select2-container input-group roo-select2-container-multi',
16839             cn: [
16840                 
16841                 box
16842 //                {
16843 //                    tag: 'ul',
16844 //                    cls: 'typeahead typeahead-long dropdown-menu',
16845 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16846 //                }
16847             ]
16848         };
16849         
16850         if(this.hasFeedback && !this.allowBlank){
16851             
16852             var feedback = {
16853                 tag: 'span',
16854                 cls: 'glyphicon form-control-feedback'
16855             };
16856
16857             combobox.cn.push(feedback);
16858         }
16859         
16860         
16861         
16862         var indicator = {
16863             tag : 'i',
16864             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16865             tooltip : 'This field is required'
16866         };
16867         if (Roo.bootstrap.version == 4) {
16868             indicator = {
16869                 tag : 'i',
16870                 style : 'display:none'
16871             };
16872         }
16873         if (align ==='left' && this.fieldLabel.length) {
16874             
16875             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16876             
16877             cfg.cn = [
16878                 indicator,
16879                 {
16880                     tag: 'label',
16881                     'for' :  id,
16882                     cls : 'control-label col-form-label',
16883                     html : this.fieldLabel
16884
16885                 },
16886                 {
16887                     cls : "", 
16888                     cn: [
16889                         combobox
16890                     ]
16891                 }
16892
16893             ];
16894             
16895             var labelCfg = cfg.cn[1];
16896             var contentCfg = cfg.cn[2];
16897             
16898
16899             if(this.indicatorpos == 'right'){
16900                 
16901                 cfg.cn = [
16902                     {
16903                         tag: 'label',
16904                         'for' :  id,
16905                         cls : 'control-label col-form-label',
16906                         cn : [
16907                             {
16908                                 tag : 'span',
16909                                 html : this.fieldLabel
16910                             },
16911                             indicator
16912                         ]
16913                     },
16914                     {
16915                         cls : "",
16916                         cn: [
16917                             combobox
16918                         ]
16919                     }
16920
16921                 ];
16922                 
16923                 
16924                 
16925                 labelCfg = cfg.cn[0];
16926                 contentCfg = cfg.cn[1];
16927             
16928             }
16929             
16930             if(this.labelWidth > 12){
16931                 labelCfg.style = "width: " + this.labelWidth + 'px';
16932             }
16933             if(this.width * 1 > 0){
16934                 contentCfg.style = "width: " + this.width + 'px';
16935             }
16936             if(this.labelWidth < 13 && this.labelmd == 0){
16937                 this.labelmd = this.labelWidth;
16938             }
16939             
16940             if(this.labellg > 0){
16941                 labelCfg.cls += ' col-lg-' + this.labellg;
16942                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16943             }
16944             
16945             if(this.labelmd > 0){
16946                 labelCfg.cls += ' col-md-' + this.labelmd;
16947                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16948             }
16949             
16950             if(this.labelsm > 0){
16951                 labelCfg.cls += ' col-sm-' + this.labelsm;
16952                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16953             }
16954             
16955             if(this.labelxs > 0){
16956                 labelCfg.cls += ' col-xs-' + this.labelxs;
16957                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16958             }
16959                 
16960                 
16961         } else if ( this.fieldLabel.length) {
16962 //                Roo.log(" label");
16963                  cfg.cn = [
16964                    indicator,
16965                     {
16966                         tag: 'label',
16967                         //cls : 'input-group-addon',
16968                         html : this.fieldLabel
16969                     },
16970                     combobox
16971                 ];
16972                 
16973                 if(this.indicatorpos == 'right'){
16974                     cfg.cn = [
16975                         {
16976                             tag: 'label',
16977                             //cls : 'input-group-addon',
16978                             html : this.fieldLabel
16979                         },
16980                         indicator,
16981                         combobox
16982                     ];
16983                     
16984                 }
16985
16986         } else {
16987             
16988 //                Roo.log(" no label && no align");
16989                 cfg = combobox
16990                      
16991                 
16992         }
16993          
16994         var settings=this;
16995         ['xs','sm','md','lg'].map(function(size){
16996             if (settings[size]) {
16997                 cfg.cls += ' col-' + size + '-' + settings[size];
16998             }
16999         });
17000         
17001         return cfg;
17002         
17003     },
17004     
17005     _initEventsCalled : false,
17006     
17007     // private
17008     initEvents: function()
17009     {   
17010         if (this._initEventsCalled) { // as we call render... prevent looping...
17011             return;
17012         }
17013         this._initEventsCalled = true;
17014         
17015         if (!this.store) {
17016             throw "can not find store for combo";
17017         }
17018         
17019         this.indicator = this.indicatorEl();
17020         
17021         this.store = Roo.factory(this.store, Roo.data);
17022         this.store.parent = this;
17023         
17024         // if we are building from html. then this element is so complex, that we can not really
17025         // use the rendered HTML.
17026         // so we have to trash and replace the previous code.
17027         if (Roo.XComponent.build_from_html) {
17028             // remove this element....
17029             var e = this.el.dom, k=0;
17030             while (e ) { e = e.previousSibling;  ++k;}
17031
17032             this.el.remove();
17033             
17034             this.el=false;
17035             this.rendered = false;
17036             
17037             this.render(this.parent().getChildContainer(true), k);
17038         }
17039         
17040         if(Roo.isIOS && this.useNativeIOS){
17041             this.initIOSView();
17042             return;
17043         }
17044         
17045         /*
17046          * Touch Devices
17047          */
17048         
17049         if(Roo.isTouch && this.mobileTouchView){
17050             this.initTouchView();
17051             return;
17052         }
17053         
17054         if(this.tickable){
17055             this.initTickableEvents();
17056             return;
17057         }
17058         
17059         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17060         
17061         if(this.hiddenName){
17062             
17063             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17064             
17065             this.hiddenField.dom.value =
17066                 this.hiddenValue !== undefined ? this.hiddenValue :
17067                 this.value !== undefined ? this.value : '';
17068
17069             // prevent input submission
17070             this.el.dom.removeAttribute('name');
17071             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17072              
17073              
17074         }
17075         //if(Roo.isGecko){
17076         //    this.el.dom.setAttribute('autocomplete', 'off');
17077         //}
17078         
17079         var cls = 'x-combo-list';
17080         
17081         //this.list = new Roo.Layer({
17082         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17083         //});
17084         
17085         var _this = this;
17086         
17087         (function(){
17088             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17089             _this.list.setWidth(lw);
17090         }).defer(100);
17091         
17092         this.list.on('mouseover', this.onViewOver, this);
17093         this.list.on('mousemove', this.onViewMove, this);
17094         this.list.on('scroll', this.onViewScroll, this);
17095         
17096         /*
17097         this.list.swallowEvent('mousewheel');
17098         this.assetHeight = 0;
17099
17100         if(this.title){
17101             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17102             this.assetHeight += this.header.getHeight();
17103         }
17104
17105         this.innerList = this.list.createChild({cls:cls+'-inner'});
17106         this.innerList.on('mouseover', this.onViewOver, this);
17107         this.innerList.on('mousemove', this.onViewMove, this);
17108         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17109         
17110         if(this.allowBlank && !this.pageSize && !this.disableClear){
17111             this.footer = this.list.createChild({cls:cls+'-ft'});
17112             this.pageTb = new Roo.Toolbar(this.footer);
17113            
17114         }
17115         if(this.pageSize){
17116             this.footer = this.list.createChild({cls:cls+'-ft'});
17117             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17118                     {pageSize: this.pageSize});
17119             
17120         }
17121         
17122         if (this.pageTb && this.allowBlank && !this.disableClear) {
17123             var _this = this;
17124             this.pageTb.add(new Roo.Toolbar.Fill(), {
17125                 cls: 'x-btn-icon x-btn-clear',
17126                 text: '&#160;',
17127                 handler: function()
17128                 {
17129                     _this.collapse();
17130                     _this.clearValue();
17131                     _this.onSelect(false, -1);
17132                 }
17133             });
17134         }
17135         if (this.footer) {
17136             this.assetHeight += this.footer.getHeight();
17137         }
17138         */
17139             
17140         if(!this.tpl){
17141             this.tpl = Roo.bootstrap.version == 4 ?
17142                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17143                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17144         }
17145
17146         this.view = new Roo.View(this.list, this.tpl, {
17147             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17148         });
17149         //this.view.wrapEl.setDisplayed(false);
17150         this.view.on('click', this.onViewClick, this);
17151         
17152         
17153         this.store.on('beforeload', this.onBeforeLoad, this);
17154         this.store.on('load', this.onLoad, this);
17155         this.store.on('loadexception', this.onLoadException, this);
17156         /*
17157         if(this.resizable){
17158             this.resizer = new Roo.Resizable(this.list,  {
17159                pinned:true, handles:'se'
17160             });
17161             this.resizer.on('resize', function(r, w, h){
17162                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17163                 this.listWidth = w;
17164                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17165                 this.restrictHeight();
17166             }, this);
17167             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17168         }
17169         */
17170         if(!this.editable){
17171             this.editable = true;
17172             this.setEditable(false);
17173         }
17174         
17175         /*
17176         
17177         if (typeof(this.events.add.listeners) != 'undefined') {
17178             
17179             this.addicon = this.wrap.createChild(
17180                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17181        
17182             this.addicon.on('click', function(e) {
17183                 this.fireEvent('add', this);
17184             }, this);
17185         }
17186         if (typeof(this.events.edit.listeners) != 'undefined') {
17187             
17188             this.editicon = this.wrap.createChild(
17189                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17190             if (this.addicon) {
17191                 this.editicon.setStyle('margin-left', '40px');
17192             }
17193             this.editicon.on('click', function(e) {
17194                 
17195                 // we fire even  if inothing is selected..
17196                 this.fireEvent('edit', this, this.lastData );
17197                 
17198             }, this);
17199         }
17200         */
17201         
17202         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17203             "up" : function(e){
17204                 this.inKeyMode = true;
17205                 this.selectPrev();
17206             },
17207
17208             "down" : function(e){
17209                 if(!this.isExpanded()){
17210                     this.onTriggerClick();
17211                 }else{
17212                     this.inKeyMode = true;
17213                     this.selectNext();
17214                 }
17215             },
17216
17217             "enter" : function(e){
17218 //                this.onViewClick();
17219                 //return true;
17220                 this.collapse();
17221                 
17222                 if(this.fireEvent("specialkey", this, e)){
17223                     this.onViewClick(false);
17224                 }
17225                 
17226                 return true;
17227             },
17228
17229             "esc" : function(e){
17230                 this.collapse();
17231             },
17232
17233             "tab" : function(e){
17234                 this.collapse();
17235                 
17236                 if(this.fireEvent("specialkey", this, e)){
17237                     this.onViewClick(false);
17238                 }
17239                 
17240                 return true;
17241             },
17242
17243             scope : this,
17244
17245             doRelay : function(foo, bar, hname){
17246                 if(hname == 'down' || this.scope.isExpanded()){
17247                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17248                 }
17249                 return true;
17250             },
17251
17252             forceKeyDown: true
17253         });
17254         
17255         
17256         this.queryDelay = Math.max(this.queryDelay || 10,
17257                 this.mode == 'local' ? 10 : 250);
17258         
17259         
17260         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17261         
17262         if(this.typeAhead){
17263             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17264         }
17265         if(this.editable !== false){
17266             this.inputEl().on("keyup", this.onKeyUp, this);
17267         }
17268         if(this.forceSelection){
17269             this.inputEl().on('blur', this.doForce, this);
17270         }
17271         
17272         if(this.multiple){
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         }
17276     },
17277     
17278     initTickableEvents: function()
17279     {   
17280         this.createList();
17281         
17282         if(this.hiddenName){
17283             
17284             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17285             
17286             this.hiddenField.dom.value =
17287                 this.hiddenValue !== undefined ? this.hiddenValue :
17288                 this.value !== undefined ? this.value : '';
17289
17290             // prevent input submission
17291             this.el.dom.removeAttribute('name');
17292             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17293              
17294              
17295         }
17296         
17297 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17298         
17299         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17300         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17301         if(this.triggerList){
17302             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17303         }
17304          
17305         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17306         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17307         
17308         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17309         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17310         
17311         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17312         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17313         
17314         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17315         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17316         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17317         
17318         this.okBtn.hide();
17319         this.cancelBtn.hide();
17320         
17321         var _this = this;
17322         
17323         (function(){
17324             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17325             _this.list.setWidth(lw);
17326         }).defer(100);
17327         
17328         this.list.on('mouseover', this.onViewOver, this);
17329         this.list.on('mousemove', this.onViewMove, this);
17330         
17331         this.list.on('scroll', this.onViewScroll, this);
17332         
17333         if(!this.tpl){
17334             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17335                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17336         }
17337
17338         this.view = new Roo.View(this.list, this.tpl, {
17339             singleSelect:true,
17340             tickable:true,
17341             parent:this,
17342             store: this.store,
17343             selectedClass: this.selectedClass
17344         });
17345         
17346         //this.view.wrapEl.setDisplayed(false);
17347         this.view.on('click', this.onViewClick, this);
17348         
17349         
17350         
17351         this.store.on('beforeload', this.onBeforeLoad, this);
17352         this.store.on('load', this.onLoad, this);
17353         this.store.on('loadexception', this.onLoadException, this);
17354         
17355         if(this.editable){
17356             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17357                 "up" : function(e){
17358                     this.inKeyMode = true;
17359                     this.selectPrev();
17360                 },
17361
17362                 "down" : function(e){
17363                     this.inKeyMode = true;
17364                     this.selectNext();
17365                 },
17366
17367                 "enter" : function(e){
17368                     if(this.fireEvent("specialkey", this, e)){
17369                         this.onViewClick(false);
17370                     }
17371                     
17372                     return true;
17373                 },
17374
17375                 "esc" : function(e){
17376                     this.onTickableFooterButtonClick(e, false, false);
17377                 },
17378
17379                 "tab" : function(e){
17380                     this.fireEvent("specialkey", this, e);
17381                     
17382                     this.onTickableFooterButtonClick(e, false, false);
17383                     
17384                     return true;
17385                 },
17386
17387                 scope : this,
17388
17389                 doRelay : function(e, fn, key){
17390                     if(this.scope.isExpanded()){
17391                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17392                     }
17393                     return true;
17394                 },
17395
17396                 forceKeyDown: true
17397             });
17398         }
17399         
17400         this.queryDelay = Math.max(this.queryDelay || 10,
17401                 this.mode == 'local' ? 10 : 250);
17402         
17403         
17404         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17405         
17406         if(this.typeAhead){
17407             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17408         }
17409         
17410         if(this.editable !== false){
17411             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17412         }
17413         
17414         this.indicator = this.indicatorEl();
17415         
17416         if(this.indicator){
17417             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17418             this.indicator.hide();
17419         }
17420         
17421     },
17422
17423     onDestroy : function(){
17424         if(this.view){
17425             this.view.setStore(null);
17426             this.view.el.removeAllListeners();
17427             this.view.el.remove();
17428             this.view.purgeListeners();
17429         }
17430         if(this.list){
17431             this.list.dom.innerHTML  = '';
17432         }
17433         
17434         if(this.store){
17435             this.store.un('beforeload', this.onBeforeLoad, this);
17436             this.store.un('load', this.onLoad, this);
17437             this.store.un('loadexception', this.onLoadException, this);
17438         }
17439         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17440     },
17441
17442     // private
17443     fireKey : function(e){
17444         if(e.isNavKeyPress() && !this.list.isVisible()){
17445             this.fireEvent("specialkey", this, e);
17446         }
17447     },
17448
17449     // private
17450     onResize: function(w, h)
17451     {
17452         
17453         
17454 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17455 //        
17456 //        if(typeof w != 'number'){
17457 //            // we do not handle it!?!?
17458 //            return;
17459 //        }
17460 //        var tw = this.trigger.getWidth();
17461 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17462 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17463 //        var x = w - tw;
17464 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17465 //            
17466 //        //this.trigger.setStyle('left', x+'px');
17467 //        
17468 //        if(this.list && this.listWidth === undefined){
17469 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17470 //            this.list.setWidth(lw);
17471 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17472 //        }
17473         
17474     
17475         
17476     },
17477
17478     /**
17479      * Allow or prevent the user from directly editing the field text.  If false is passed,
17480      * the user will only be able to select from the items defined in the dropdown list.  This method
17481      * is the runtime equivalent of setting the 'editable' config option at config time.
17482      * @param {Boolean} value True to allow the user to directly edit the field text
17483      */
17484     setEditable : function(value){
17485         if(value == this.editable){
17486             return;
17487         }
17488         this.editable = value;
17489         if(!value){
17490             this.inputEl().dom.setAttribute('readOnly', true);
17491             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17492             this.inputEl().addClass('x-combo-noedit');
17493         }else{
17494             this.inputEl().dom.removeAttribute('readOnly');
17495             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17496             this.inputEl().removeClass('x-combo-noedit');
17497         }
17498     },
17499
17500     // private
17501     
17502     onBeforeLoad : function(combo,opts){
17503         if(!this.hasFocus){
17504             return;
17505         }
17506          if (!opts.add) {
17507             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17508          }
17509         this.restrictHeight();
17510         this.selectedIndex = -1;
17511     },
17512
17513     // private
17514     onLoad : function(){
17515         
17516         this.hasQuery = false;
17517         
17518         if(!this.hasFocus){
17519             return;
17520         }
17521         
17522         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17523             this.loading.hide();
17524         }
17525         
17526         if(this.store.getCount() > 0){
17527             
17528             this.expand();
17529             this.restrictHeight();
17530             if(this.lastQuery == this.allQuery){
17531                 if(this.editable && !this.tickable){
17532                     this.inputEl().dom.select();
17533                 }
17534                 
17535                 if(
17536                     !this.selectByValue(this.value, true) &&
17537                     this.autoFocus && 
17538                     (
17539                         !this.store.lastOptions ||
17540                         typeof(this.store.lastOptions.add) == 'undefined' || 
17541                         this.store.lastOptions.add != true
17542                     )
17543                 ){
17544                     this.select(0, true);
17545                 }
17546             }else{
17547                 if(this.autoFocus){
17548                     this.selectNext();
17549                 }
17550                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17551                     this.taTask.delay(this.typeAheadDelay);
17552                 }
17553             }
17554         }else{
17555             this.onEmptyResults();
17556         }
17557         
17558         //this.el.focus();
17559     },
17560     // private
17561     onLoadException : function()
17562     {
17563         this.hasQuery = false;
17564         
17565         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17566             this.loading.hide();
17567         }
17568         
17569         if(this.tickable && this.editable){
17570             return;
17571         }
17572         
17573         this.collapse();
17574         // only causes errors at present
17575         //Roo.log(this.store.reader.jsonData);
17576         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17577             // fixme
17578             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17579         //}
17580         
17581         
17582     },
17583     // private
17584     onTypeAhead : function(){
17585         if(this.store.getCount() > 0){
17586             var r = this.store.getAt(0);
17587             var newValue = r.data[this.displayField];
17588             var len = newValue.length;
17589             var selStart = this.getRawValue().length;
17590             
17591             if(selStart != len){
17592                 this.setRawValue(newValue);
17593                 this.selectText(selStart, newValue.length);
17594             }
17595         }
17596     },
17597
17598     // private
17599     onSelect : function(record, index){
17600         
17601         if(this.fireEvent('beforeselect', this, record, index) !== false){
17602         
17603             this.setFromData(index > -1 ? record.data : false);
17604             
17605             this.collapse();
17606             this.fireEvent('select', this, record, index);
17607         }
17608     },
17609
17610     /**
17611      * Returns the currently selected field value or empty string if no value is set.
17612      * @return {String} value The selected value
17613      */
17614     getValue : function()
17615     {
17616         if(Roo.isIOS && this.useNativeIOS){
17617             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17618         }
17619         
17620         if(this.multiple){
17621             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17622         }
17623         
17624         if(this.valueField){
17625             return typeof this.value != 'undefined' ? this.value : '';
17626         }else{
17627             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17628         }
17629     },
17630     
17631     getRawValue : function()
17632     {
17633         if(Roo.isIOS && this.useNativeIOS){
17634             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17635         }
17636         
17637         var v = this.inputEl().getValue();
17638         
17639         return v;
17640     },
17641
17642     /**
17643      * Clears any text/value currently set in the field
17644      */
17645     clearValue : function(){
17646         
17647         if(this.hiddenField){
17648             this.hiddenField.dom.value = '';
17649         }
17650         this.value = '';
17651         this.setRawValue('');
17652         this.lastSelectionText = '';
17653         this.lastData = false;
17654         
17655         var close = this.closeTriggerEl();
17656         
17657         if(close){
17658             close.hide();
17659         }
17660         
17661         this.validate();
17662         
17663     },
17664
17665     /**
17666      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17667      * will be displayed in the field.  If the value does not match the data value of an existing item,
17668      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17669      * Otherwise the field will be blank (although the value will still be set).
17670      * @param {String} value The value to match
17671      */
17672     setValue : function(v)
17673     {
17674         if(Roo.isIOS && this.useNativeIOS){
17675             this.setIOSValue(v);
17676             return;
17677         }
17678         
17679         if(this.multiple){
17680             this.syncValue();
17681             return;
17682         }
17683         
17684         var text = v;
17685         if(this.valueField){
17686             var r = this.findRecord(this.valueField, v);
17687             if(r){
17688                 text = r.data[this.displayField];
17689             }else if(this.valueNotFoundText !== undefined){
17690                 text = this.valueNotFoundText;
17691             }
17692         }
17693         this.lastSelectionText = text;
17694         if(this.hiddenField){
17695             this.hiddenField.dom.value = v;
17696         }
17697         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17698         this.value = v;
17699         
17700         var close = this.closeTriggerEl();
17701         
17702         if(close){
17703             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17704         }
17705         
17706         this.validate();
17707     },
17708     /**
17709      * @property {Object} the last set data for the element
17710      */
17711     
17712     lastData : false,
17713     /**
17714      * Sets the value of the field based on a object which is related to the record format for the store.
17715      * @param {Object} value the value to set as. or false on reset?
17716      */
17717     setFromData : function(o){
17718         
17719         if(this.multiple){
17720             this.addItem(o);
17721             return;
17722         }
17723             
17724         var dv = ''; // display value
17725         var vv = ''; // value value..
17726         this.lastData = o;
17727         if (this.displayField) {
17728             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17729         } else {
17730             // this is an error condition!!!
17731             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17732         }
17733         
17734         if(this.valueField){
17735             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17736         }
17737         
17738         var close = this.closeTriggerEl();
17739         
17740         if(close){
17741             if(dv.length || vv * 1 > 0){
17742                 close.show() ;
17743                 this.blockFocus=true;
17744             } else {
17745                 close.hide();
17746             }             
17747         }
17748         
17749         if(this.hiddenField){
17750             this.hiddenField.dom.value = vv;
17751             
17752             this.lastSelectionText = dv;
17753             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17754             this.value = vv;
17755             return;
17756         }
17757         // no hidden field.. - we store the value in 'value', but still display
17758         // display field!!!!
17759         this.lastSelectionText = dv;
17760         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17761         this.value = vv;
17762         
17763         
17764         
17765     },
17766     // private
17767     reset : function(){
17768         // overridden so that last data is reset..
17769         
17770         if(this.multiple){
17771             this.clearItem();
17772             return;
17773         }
17774         
17775         this.setValue(this.originalValue);
17776         //this.clearInvalid();
17777         this.lastData = false;
17778         if (this.view) {
17779             this.view.clearSelections();
17780         }
17781         
17782         this.validate();
17783     },
17784     // private
17785     findRecord : function(prop, value){
17786         var record;
17787         if(this.store.getCount() > 0){
17788             this.store.each(function(r){
17789                 if(r.data[prop] == value){
17790                     record = r;
17791                     return false;
17792                 }
17793                 return true;
17794             });
17795         }
17796         return record;
17797     },
17798     
17799     getName: function()
17800     {
17801         // returns hidden if it's set..
17802         if (!this.rendered) {return ''};
17803         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17804         
17805     },
17806     // private
17807     onViewMove : function(e, t){
17808         this.inKeyMode = false;
17809     },
17810
17811     // private
17812     onViewOver : function(e, t){
17813         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17814             return;
17815         }
17816         var item = this.view.findItemFromChild(t);
17817         
17818         if(item){
17819             var index = this.view.indexOf(item);
17820             this.select(index, false);
17821         }
17822     },
17823
17824     // private
17825     onViewClick : function(view, doFocus, el, e)
17826     {
17827         var index = this.view.getSelectedIndexes()[0];
17828         
17829         var r = this.store.getAt(index);
17830         
17831         if(this.tickable){
17832             
17833             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17834                 return;
17835             }
17836             
17837             var rm = false;
17838             var _this = this;
17839             
17840             Roo.each(this.tickItems, function(v,k){
17841                 
17842                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17843                     Roo.log(v);
17844                     _this.tickItems.splice(k, 1);
17845                     
17846                     if(typeof(e) == 'undefined' && view == false){
17847                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17848                     }
17849                     
17850                     rm = true;
17851                     return;
17852                 }
17853             });
17854             
17855             if(rm){
17856                 return;
17857             }
17858             
17859             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17860                 this.tickItems.push(r.data);
17861             }
17862             
17863             if(typeof(e) == 'undefined' && view == false){
17864                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17865             }
17866                     
17867             return;
17868         }
17869         
17870         if(r){
17871             this.onSelect(r, index);
17872         }
17873         if(doFocus !== false && !this.blockFocus){
17874             this.inputEl().focus();
17875         }
17876     },
17877
17878     // private
17879     restrictHeight : function(){
17880         //this.innerList.dom.style.height = '';
17881         //var inner = this.innerList.dom;
17882         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17883         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17884         //this.list.beginUpdate();
17885         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17886         this.list.alignTo(this.inputEl(), this.listAlign);
17887         this.list.alignTo(this.inputEl(), this.listAlign);
17888         //this.list.endUpdate();
17889     },
17890
17891     // private
17892     onEmptyResults : function(){
17893         
17894         if(this.tickable && this.editable){
17895             this.hasFocus = false;
17896             this.restrictHeight();
17897             return;
17898         }
17899         
17900         this.collapse();
17901     },
17902
17903     /**
17904      * Returns true if the dropdown list is expanded, else false.
17905      */
17906     isExpanded : function(){
17907         return this.list.isVisible();
17908     },
17909
17910     /**
17911      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17912      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17913      * @param {String} value The data value of the item to select
17914      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17915      * selected item if it is not currently in view (defaults to true)
17916      * @return {Boolean} True if the value matched an item in the list, else false
17917      */
17918     selectByValue : function(v, scrollIntoView){
17919         if(v !== undefined && v !== null){
17920             var r = this.findRecord(this.valueField || this.displayField, v);
17921             if(r){
17922                 this.select(this.store.indexOf(r), scrollIntoView);
17923                 return true;
17924             }
17925         }
17926         return false;
17927     },
17928
17929     /**
17930      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17931      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17932      * @param {Number} index The zero-based index of the list item to select
17933      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17934      * selected item if it is not currently in view (defaults to true)
17935      */
17936     select : function(index, scrollIntoView){
17937         this.selectedIndex = index;
17938         this.view.select(index);
17939         if(scrollIntoView !== false){
17940             var el = this.view.getNode(index);
17941             /*
17942              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17943              */
17944             if(el){
17945                 this.list.scrollChildIntoView(el, false);
17946             }
17947         }
17948     },
17949
17950     // private
17951     selectNext : function(){
17952         var ct = this.store.getCount();
17953         if(ct > 0){
17954             if(this.selectedIndex == -1){
17955                 this.select(0);
17956             }else if(this.selectedIndex < ct-1){
17957                 this.select(this.selectedIndex+1);
17958             }
17959         }
17960     },
17961
17962     // private
17963     selectPrev : function(){
17964         var ct = this.store.getCount();
17965         if(ct > 0){
17966             if(this.selectedIndex == -1){
17967                 this.select(0);
17968             }else if(this.selectedIndex != 0){
17969                 this.select(this.selectedIndex-1);
17970             }
17971         }
17972     },
17973
17974     // private
17975     onKeyUp : function(e){
17976         if(this.editable !== false && !e.isSpecialKey()){
17977             this.lastKey = e.getKey();
17978             this.dqTask.delay(this.queryDelay);
17979         }
17980     },
17981
17982     // private
17983     validateBlur : function(){
17984         return !this.list || !this.list.isVisible();   
17985     },
17986
17987     // private
17988     initQuery : function(){
17989         
17990         var v = this.getRawValue();
17991         
17992         if(this.tickable && this.editable){
17993             v = this.tickableInputEl().getValue();
17994         }
17995         
17996         this.doQuery(v);
17997     },
17998
17999     // private
18000     doForce : function(){
18001         if(this.inputEl().dom.value.length > 0){
18002             this.inputEl().dom.value =
18003                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18004              
18005         }
18006     },
18007
18008     /**
18009      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18010      * query allowing the query action to be canceled if needed.
18011      * @param {String} query The SQL query to execute
18012      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18013      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18014      * saved in the current store (defaults to false)
18015      */
18016     doQuery : function(q, forceAll){
18017         
18018         if(q === undefined || q === null){
18019             q = '';
18020         }
18021         var qe = {
18022             query: q,
18023             forceAll: forceAll,
18024             combo: this,
18025             cancel:false
18026         };
18027         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18028             return false;
18029         }
18030         q = qe.query;
18031         
18032         forceAll = qe.forceAll;
18033         if(forceAll === true || (q.length >= this.minChars)){
18034             
18035             this.hasQuery = true;
18036             
18037             if(this.lastQuery != q || this.alwaysQuery){
18038                 this.lastQuery = q;
18039                 if(this.mode == 'local'){
18040                     this.selectedIndex = -1;
18041                     if(forceAll){
18042                         this.store.clearFilter();
18043                     }else{
18044                         
18045                         if(this.specialFilter){
18046                             this.fireEvent('specialfilter', this);
18047                             this.onLoad();
18048                             return;
18049                         }
18050                         
18051                         this.store.filter(this.displayField, q);
18052                     }
18053                     
18054                     this.store.fireEvent("datachanged", this.store);
18055                     
18056                     this.onLoad();
18057                     
18058                     
18059                 }else{
18060                     
18061                     this.store.baseParams[this.queryParam] = q;
18062                     
18063                     var options = {params : this.getParams(q)};
18064                     
18065                     if(this.loadNext){
18066                         options.add = true;
18067                         options.params.start = this.page * this.pageSize;
18068                     }
18069                     
18070                     this.store.load(options);
18071                     
18072                     /*
18073                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18074                      *  we should expand the list on onLoad
18075                      *  so command out it
18076                      */
18077 //                    this.expand();
18078                 }
18079             }else{
18080                 this.selectedIndex = -1;
18081                 this.onLoad();   
18082             }
18083         }
18084         
18085         this.loadNext = false;
18086     },
18087     
18088     // private
18089     getParams : function(q){
18090         var p = {};
18091         //p[this.queryParam] = q;
18092         
18093         if(this.pageSize){
18094             p.start = 0;
18095             p.limit = this.pageSize;
18096         }
18097         return p;
18098     },
18099
18100     /**
18101      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18102      */
18103     collapse : function(){
18104         if(!this.isExpanded()){
18105             return;
18106         }
18107         
18108         this.list.hide();
18109         
18110         this.hasFocus = false;
18111         
18112         if(this.tickable){
18113             this.okBtn.hide();
18114             this.cancelBtn.hide();
18115             this.trigger.show();
18116             
18117             if(this.editable){
18118                 this.tickableInputEl().dom.value = '';
18119                 this.tickableInputEl().blur();
18120             }
18121             
18122         }
18123         
18124         Roo.get(document).un('mousedown', this.collapseIf, this);
18125         Roo.get(document).un('mousewheel', this.collapseIf, this);
18126         if (!this.editable) {
18127             Roo.get(document).un('keydown', this.listKeyPress, this);
18128         }
18129         this.fireEvent('collapse', this);
18130         
18131         this.validate();
18132     },
18133
18134     // private
18135     collapseIf : function(e){
18136         var in_combo  = e.within(this.el);
18137         var in_list =  e.within(this.list);
18138         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18139         
18140         if (in_combo || in_list || is_list) {
18141             //e.stopPropagation();
18142             return;
18143         }
18144         
18145         if(this.tickable){
18146             this.onTickableFooterButtonClick(e, false, false);
18147         }
18148
18149         this.collapse();
18150         
18151     },
18152
18153     /**
18154      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18155      */
18156     expand : function(){
18157        
18158         if(this.isExpanded() || !this.hasFocus){
18159             return;
18160         }
18161         
18162         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18163         this.list.setWidth(lw);
18164         
18165         Roo.log('expand');
18166         
18167         this.list.show();
18168         
18169         this.restrictHeight();
18170         
18171         if(this.tickable){
18172             
18173             this.tickItems = Roo.apply([], this.item);
18174             
18175             this.okBtn.show();
18176             this.cancelBtn.show();
18177             this.trigger.hide();
18178             
18179             if(this.editable){
18180                 this.tickableInputEl().focus();
18181             }
18182             
18183         }
18184         
18185         Roo.get(document).on('mousedown', this.collapseIf, this);
18186         Roo.get(document).on('mousewheel', this.collapseIf, this);
18187         if (!this.editable) {
18188             Roo.get(document).on('keydown', this.listKeyPress, this);
18189         }
18190         
18191         this.fireEvent('expand', this);
18192     },
18193
18194     // private
18195     // Implements the default empty TriggerField.onTriggerClick function
18196     onTriggerClick : function(e)
18197     {
18198         Roo.log('trigger click');
18199         
18200         if(this.disabled || !this.triggerList){
18201             return;
18202         }
18203         
18204         this.page = 0;
18205         this.loadNext = false;
18206         
18207         if(this.isExpanded()){
18208             this.collapse();
18209             if (!this.blockFocus) {
18210                 this.inputEl().focus();
18211             }
18212             
18213         }else {
18214             this.hasFocus = true;
18215             if(this.triggerAction == 'all') {
18216                 this.doQuery(this.allQuery, true);
18217             } else {
18218                 this.doQuery(this.getRawValue());
18219             }
18220             if (!this.blockFocus) {
18221                 this.inputEl().focus();
18222             }
18223         }
18224     },
18225     
18226     onTickableTriggerClick : function(e)
18227     {
18228         if(this.disabled){
18229             return;
18230         }
18231         
18232         this.page = 0;
18233         this.loadNext = false;
18234         this.hasFocus = true;
18235         
18236         if(this.triggerAction == 'all') {
18237             this.doQuery(this.allQuery, true);
18238         } else {
18239             this.doQuery(this.getRawValue());
18240         }
18241     },
18242     
18243     onSearchFieldClick : function(e)
18244     {
18245         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18246             this.onTickableFooterButtonClick(e, false, false);
18247             return;
18248         }
18249         
18250         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18251             return;
18252         }
18253         
18254         this.page = 0;
18255         this.loadNext = false;
18256         this.hasFocus = true;
18257         
18258         if(this.triggerAction == 'all') {
18259             this.doQuery(this.allQuery, true);
18260         } else {
18261             this.doQuery(this.getRawValue());
18262         }
18263     },
18264     
18265     listKeyPress : function(e)
18266     {
18267         //Roo.log('listkeypress');
18268         // scroll to first matching element based on key pres..
18269         if (e.isSpecialKey()) {
18270             return false;
18271         }
18272         var k = String.fromCharCode(e.getKey()).toUpperCase();
18273         //Roo.log(k);
18274         var match  = false;
18275         var csel = this.view.getSelectedNodes();
18276         var cselitem = false;
18277         if (csel.length) {
18278             var ix = this.view.indexOf(csel[0]);
18279             cselitem  = this.store.getAt(ix);
18280             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18281                 cselitem = false;
18282             }
18283             
18284         }
18285         
18286         this.store.each(function(v) { 
18287             if (cselitem) {
18288                 // start at existing selection.
18289                 if (cselitem.id == v.id) {
18290                     cselitem = false;
18291                 }
18292                 return true;
18293             }
18294                 
18295             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18296                 match = this.store.indexOf(v);
18297                 return false;
18298             }
18299             return true;
18300         }, this);
18301         
18302         if (match === false) {
18303             return true; // no more action?
18304         }
18305         // scroll to?
18306         this.view.select(match);
18307         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18308         sn.scrollIntoView(sn.dom.parentNode, false);
18309     },
18310     
18311     onViewScroll : function(e, t){
18312         
18313         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){
18314             return;
18315         }
18316         
18317         this.hasQuery = true;
18318         
18319         this.loading = this.list.select('.loading', true).first();
18320         
18321         if(this.loading === null){
18322             this.list.createChild({
18323                 tag: 'div',
18324                 cls: 'loading roo-select2-more-results roo-select2-active',
18325                 html: 'Loading more results...'
18326             });
18327             
18328             this.loading = this.list.select('.loading', true).first();
18329             
18330             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18331             
18332             this.loading.hide();
18333         }
18334         
18335         this.loading.show();
18336         
18337         var _combo = this;
18338         
18339         this.page++;
18340         this.loadNext = true;
18341         
18342         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18343         
18344         return;
18345     },
18346     
18347     addItem : function(o)
18348     {   
18349         var dv = ''; // display value
18350         
18351         if (this.displayField) {
18352             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18353         } else {
18354             // this is an error condition!!!
18355             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18356         }
18357         
18358         if(!dv.length){
18359             return;
18360         }
18361         
18362         var choice = this.choices.createChild({
18363             tag: 'li',
18364             cls: 'roo-select2-search-choice',
18365             cn: [
18366                 {
18367                     tag: 'div',
18368                     html: dv
18369                 },
18370                 {
18371                     tag: 'a',
18372                     href: '#',
18373                     cls: 'roo-select2-search-choice-close fa fa-times',
18374                     tabindex: '-1'
18375                 }
18376             ]
18377             
18378         }, this.searchField);
18379         
18380         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18381         
18382         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18383         
18384         this.item.push(o);
18385         
18386         this.lastData = o;
18387         
18388         this.syncValue();
18389         
18390         this.inputEl().dom.value = '';
18391         
18392         this.validate();
18393     },
18394     
18395     onRemoveItem : function(e, _self, o)
18396     {
18397         e.preventDefault();
18398         
18399         this.lastItem = Roo.apply([], this.item);
18400         
18401         var index = this.item.indexOf(o.data) * 1;
18402         
18403         if( index < 0){
18404             Roo.log('not this item?!');
18405             return;
18406         }
18407         
18408         this.item.splice(index, 1);
18409         o.item.remove();
18410         
18411         this.syncValue();
18412         
18413         this.fireEvent('remove', this, e);
18414         
18415         this.validate();
18416         
18417     },
18418     
18419     syncValue : function()
18420     {
18421         if(!this.item.length){
18422             this.clearValue();
18423             return;
18424         }
18425             
18426         var value = [];
18427         var _this = this;
18428         Roo.each(this.item, function(i){
18429             if(_this.valueField){
18430                 value.push(i[_this.valueField]);
18431                 return;
18432             }
18433
18434             value.push(i);
18435         });
18436
18437         this.value = value.join(',');
18438
18439         if(this.hiddenField){
18440             this.hiddenField.dom.value = this.value;
18441         }
18442         
18443         this.store.fireEvent("datachanged", this.store);
18444         
18445         this.validate();
18446     },
18447     
18448     clearItem : function()
18449     {
18450         if(!this.multiple){
18451             return;
18452         }
18453         
18454         this.item = [];
18455         
18456         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18457            c.remove();
18458         });
18459         
18460         this.syncValue();
18461         
18462         this.validate();
18463         
18464         if(this.tickable && !Roo.isTouch){
18465             this.view.refresh();
18466         }
18467     },
18468     
18469     inputEl: function ()
18470     {
18471         if(Roo.isIOS && this.useNativeIOS){
18472             return this.el.select('select.roo-ios-select', true).first();
18473         }
18474         
18475         if(Roo.isTouch && this.mobileTouchView){
18476             return this.el.select('input.form-control',true).first();
18477         }
18478         
18479         if(this.tickable){
18480             return this.searchField;
18481         }
18482         
18483         return this.el.select('input.form-control',true).first();
18484     },
18485     
18486     onTickableFooterButtonClick : function(e, btn, el)
18487     {
18488         e.preventDefault();
18489         
18490         this.lastItem = Roo.apply([], this.item);
18491         
18492         if(btn && btn.name == 'cancel'){
18493             this.tickItems = Roo.apply([], this.item);
18494             this.collapse();
18495             return;
18496         }
18497         
18498         this.clearItem();
18499         
18500         var _this = this;
18501         
18502         Roo.each(this.tickItems, function(o){
18503             _this.addItem(o);
18504         });
18505         
18506         this.collapse();
18507         
18508     },
18509     
18510     validate : function()
18511     {
18512         if(this.getVisibilityEl().hasClass('hidden')){
18513             return true;
18514         }
18515         
18516         var v = this.getRawValue();
18517         
18518         if(this.multiple){
18519             v = this.getValue();
18520         }
18521         
18522         if(this.disabled || this.allowBlank || v.length){
18523             this.markValid();
18524             return true;
18525         }
18526         
18527         this.markInvalid();
18528         return false;
18529     },
18530     
18531     tickableInputEl : function()
18532     {
18533         if(!this.tickable || !this.editable){
18534             return this.inputEl();
18535         }
18536         
18537         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18538     },
18539     
18540     
18541     getAutoCreateTouchView : function()
18542     {
18543         var id = Roo.id();
18544         
18545         var cfg = {
18546             cls: 'form-group' //input-group
18547         };
18548         
18549         var input =  {
18550             tag: 'input',
18551             id : id,
18552             type : this.inputType,
18553             cls : 'form-control x-combo-noedit',
18554             autocomplete: 'new-password',
18555             placeholder : this.placeholder || '',
18556             readonly : true
18557         };
18558         
18559         if (this.name) {
18560             input.name = this.name;
18561         }
18562         
18563         if (this.size) {
18564             input.cls += ' input-' + this.size;
18565         }
18566         
18567         if (this.disabled) {
18568             input.disabled = true;
18569         }
18570         
18571         var inputblock = {
18572             cls : 'roo-combobox-wrap',
18573             cn : [
18574                 input
18575             ]
18576         };
18577         
18578         if(this.before){
18579             inputblock.cls += ' input-group';
18580             
18581             inputblock.cn.unshift({
18582                 tag :'span',
18583                 cls : 'input-group-addon input-group-prepend input-group-text',
18584                 html : this.before
18585             });
18586         }
18587         
18588         if(this.removable && !this.multiple){
18589             inputblock.cls += ' roo-removable';
18590             
18591             inputblock.cn.push({
18592                 tag: 'button',
18593                 html : 'x',
18594                 cls : 'roo-combo-removable-btn close'
18595             });
18596         }
18597
18598         if(this.hasFeedback && !this.allowBlank){
18599             
18600             inputblock.cls += ' has-feedback';
18601             
18602             inputblock.cn.push({
18603                 tag: 'span',
18604                 cls: 'glyphicon form-control-feedback'
18605             });
18606             
18607         }
18608         
18609         if (this.after) {
18610             
18611             inputblock.cls += (this.before) ? '' : ' input-group';
18612             
18613             inputblock.cn.push({
18614                 tag :'span',
18615                 cls : 'input-group-addon input-group-append input-group-text',
18616                 html : this.after
18617             });
18618         }
18619
18620         
18621         var ibwrap = inputblock;
18622         
18623         if(this.multiple){
18624             ibwrap = {
18625                 tag: 'ul',
18626                 cls: 'roo-select2-choices',
18627                 cn:[
18628                     {
18629                         tag: 'li',
18630                         cls: 'roo-select2-search-field',
18631                         cn: [
18632
18633                             inputblock
18634                         ]
18635                     }
18636                 ]
18637             };
18638         
18639             
18640         }
18641         
18642         var combobox = {
18643             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18644             cn: [
18645                 {
18646                     tag: 'input',
18647                     type : 'hidden',
18648                     cls: 'form-hidden-field'
18649                 },
18650                 ibwrap
18651             ]
18652         };
18653         
18654         if(!this.multiple && this.showToggleBtn){
18655             
18656             var caret = {
18657                 cls: 'caret'
18658             };
18659             
18660             if (this.caret != false) {
18661                 caret = {
18662                      tag: 'i',
18663                      cls: 'fa fa-' + this.caret
18664                 };
18665                 
18666             }
18667             
18668             combobox.cn.push({
18669                 tag :'span',
18670                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18671                 cn : [
18672                     Roo.bootstrap.version == 3 ? caret : '',
18673                     {
18674                         tag: 'span',
18675                         cls: 'combobox-clear',
18676                         cn  : [
18677                             {
18678                                 tag : 'i',
18679                                 cls: 'icon-remove'
18680                             }
18681                         ]
18682                     }
18683                 ]
18684
18685             })
18686         }
18687         
18688         if(this.multiple){
18689             combobox.cls += ' roo-select2-container-multi';
18690         }
18691         
18692         var required =  this.allowBlank ?  {
18693                     tag : 'i',
18694                     style: 'display: none'
18695                 } : {
18696                    tag : 'i',
18697                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18698                    tooltip : 'This field is required'
18699                 };
18700         
18701         var align = this.labelAlign || this.parentLabelAlign();
18702         
18703         if (align ==='left' && this.fieldLabel.length) {
18704
18705             cfg.cn = [
18706                 required,
18707                 {
18708                     tag: 'label',
18709                     cls : 'control-label col-form-label',
18710                     html : this.fieldLabel
18711
18712                 },
18713                 {
18714                     cls : 'roo-combobox-wrap ', 
18715                     cn: [
18716                         combobox
18717                     ]
18718                 }
18719             ];
18720             
18721             var labelCfg = cfg.cn[1];
18722             var contentCfg = cfg.cn[2];
18723             
18724
18725             if(this.indicatorpos == 'right'){
18726                 cfg.cn = [
18727                     {
18728                         tag: 'label',
18729                         'for' :  id,
18730                         cls : 'control-label col-form-label',
18731                         cn : [
18732                             {
18733                                 tag : 'span',
18734                                 html : this.fieldLabel
18735                             },
18736                             required
18737                         ]
18738                     },
18739                     {
18740                         cls : "roo-combobox-wrap ",
18741                         cn: [
18742                             combobox
18743                         ]
18744                     }
18745
18746                 ];
18747                 
18748                 labelCfg = cfg.cn[0];
18749                 contentCfg = cfg.cn[1];
18750             }
18751             
18752            
18753             
18754             if(this.labelWidth > 12){
18755                 labelCfg.style = "width: " + this.labelWidth + 'px';
18756             }
18757            
18758             if(this.labelWidth < 13 && this.labelmd == 0){
18759                 this.labelmd = this.labelWidth;
18760             }
18761             
18762             if(this.labellg > 0){
18763                 labelCfg.cls += ' col-lg-' + this.labellg;
18764                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18765             }
18766             
18767             if(this.labelmd > 0){
18768                 labelCfg.cls += ' col-md-' + this.labelmd;
18769                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18770             }
18771             
18772             if(this.labelsm > 0){
18773                 labelCfg.cls += ' col-sm-' + this.labelsm;
18774                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18775             }
18776             
18777             if(this.labelxs > 0){
18778                 labelCfg.cls += ' col-xs-' + this.labelxs;
18779                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18780             }
18781                 
18782                 
18783         } else if ( this.fieldLabel.length) {
18784             cfg.cn = [
18785                required,
18786                 {
18787                     tag: 'label',
18788                     cls : 'control-label',
18789                     html : this.fieldLabel
18790
18791                 },
18792                 {
18793                     cls : '', 
18794                     cn: [
18795                         combobox
18796                     ]
18797                 }
18798             ];
18799             
18800             if(this.indicatorpos == 'right'){
18801                 cfg.cn = [
18802                     {
18803                         tag: 'label',
18804                         cls : 'control-label',
18805                         html : this.fieldLabel,
18806                         cn : [
18807                             required
18808                         ]
18809                     },
18810                     {
18811                         cls : '', 
18812                         cn: [
18813                             combobox
18814                         ]
18815                     }
18816                 ];
18817             }
18818         } else {
18819             cfg.cn = combobox;    
18820         }
18821         
18822         
18823         var settings = this;
18824         
18825         ['xs','sm','md','lg'].map(function(size){
18826             if (settings[size]) {
18827                 cfg.cls += ' col-' + size + '-' + settings[size];
18828             }
18829         });
18830         
18831         return cfg;
18832     },
18833     
18834     initTouchView : function()
18835     {
18836         this.renderTouchView();
18837         
18838         this.touchViewEl.on('scroll', function(){
18839             this.el.dom.scrollTop = 0;
18840         }, this);
18841         
18842         this.originalValue = this.getValue();
18843         
18844         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18845         
18846         this.inputEl().on("click", this.showTouchView, this);
18847         if (this.triggerEl) {
18848             this.triggerEl.on("click", this.showTouchView, this);
18849         }
18850         
18851         
18852         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18853         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18854         
18855         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18856         
18857         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18858         this.store.on('load', this.onTouchViewLoad, this);
18859         this.store.on('loadexception', this.onTouchViewLoadException, this);
18860         
18861         if(this.hiddenName){
18862             
18863             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18864             
18865             this.hiddenField.dom.value =
18866                 this.hiddenValue !== undefined ? this.hiddenValue :
18867                 this.value !== undefined ? this.value : '';
18868         
18869             this.el.dom.removeAttribute('name');
18870             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18871         }
18872         
18873         if(this.multiple){
18874             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18875             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18876         }
18877         
18878         if(this.removable && !this.multiple){
18879             var close = this.closeTriggerEl();
18880             if(close){
18881                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18882                 close.on('click', this.removeBtnClick, this, close);
18883             }
18884         }
18885         /*
18886          * fix the bug in Safari iOS8
18887          */
18888         this.inputEl().on("focus", function(e){
18889             document.activeElement.blur();
18890         }, this);
18891         
18892         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18893         
18894         return;
18895         
18896         
18897     },
18898     
18899     renderTouchView : function()
18900     {
18901         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18902         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18903         
18904         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18905         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18906         
18907         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18908         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18909         this.touchViewBodyEl.setStyle('overflow', 'auto');
18910         
18911         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18912         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18913         
18914         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18915         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18916         
18917     },
18918     
18919     showTouchView : function()
18920     {
18921         if(this.disabled){
18922             return;
18923         }
18924         
18925         this.touchViewHeaderEl.hide();
18926
18927         if(this.modalTitle.length){
18928             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18929             this.touchViewHeaderEl.show();
18930         }
18931
18932         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18933         this.touchViewEl.show();
18934
18935         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18936         
18937         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18938         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18939
18940         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18941
18942         if(this.modalTitle.length){
18943             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18944         }
18945         
18946         this.touchViewBodyEl.setHeight(bodyHeight);
18947
18948         if(this.animate){
18949             var _this = this;
18950             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18951         }else{
18952             this.touchViewEl.addClass(['in','show']);
18953         }
18954         
18955         if(this._touchViewMask){
18956             Roo.get(document.body).addClass("x-body-masked");
18957             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18958             this._touchViewMask.setStyle('z-index', 10000);
18959             this._touchViewMask.addClass('show');
18960         }
18961         
18962         this.doTouchViewQuery();
18963         
18964     },
18965     
18966     hideTouchView : function()
18967     {
18968         this.touchViewEl.removeClass(['in','show']);
18969
18970         if(this.animate){
18971             var _this = this;
18972             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18973         }else{
18974             this.touchViewEl.setStyle('display', 'none');
18975         }
18976         
18977         if(this._touchViewMask){
18978             this._touchViewMask.removeClass('show');
18979             Roo.get(document.body).removeClass("x-body-masked");
18980         }
18981     },
18982     
18983     setTouchViewValue : function()
18984     {
18985         if(this.multiple){
18986             this.clearItem();
18987         
18988             var _this = this;
18989
18990             Roo.each(this.tickItems, function(o){
18991                 this.addItem(o);
18992             }, this);
18993         }
18994         
18995         this.hideTouchView();
18996     },
18997     
18998     doTouchViewQuery : function()
18999     {
19000         var qe = {
19001             query: '',
19002             forceAll: true,
19003             combo: this,
19004             cancel:false
19005         };
19006         
19007         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19008             return false;
19009         }
19010         
19011         if(!this.alwaysQuery || this.mode == 'local'){
19012             this.onTouchViewLoad();
19013             return;
19014         }
19015         
19016         this.store.load();
19017     },
19018     
19019     onTouchViewBeforeLoad : function(combo,opts)
19020     {
19021         return;
19022     },
19023
19024     // private
19025     onTouchViewLoad : function()
19026     {
19027         if(this.store.getCount() < 1){
19028             this.onTouchViewEmptyResults();
19029             return;
19030         }
19031         
19032         this.clearTouchView();
19033         
19034         var rawValue = this.getRawValue();
19035         
19036         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19037         
19038         this.tickItems = [];
19039         
19040         this.store.data.each(function(d, rowIndex){
19041             var row = this.touchViewListGroup.createChild(template);
19042             
19043             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19044                 row.addClass(d.data.cls);
19045             }
19046             
19047             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19048                 var cfg = {
19049                     data : d.data,
19050                     html : d.data[this.displayField]
19051                 };
19052                 
19053                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19054                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19055                 }
19056             }
19057             row.removeClass('selected');
19058             if(!this.multiple && this.valueField &&
19059                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19060             {
19061                 // radio buttons..
19062                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19063                 row.addClass('selected');
19064             }
19065             
19066             if(this.multiple && this.valueField &&
19067                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19068             {
19069                 
19070                 // checkboxes...
19071                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19072                 this.tickItems.push(d.data);
19073             }
19074             
19075             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19076             
19077         }, this);
19078         
19079         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19080         
19081         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19082
19083         if(this.modalTitle.length){
19084             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19085         }
19086
19087         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19088         
19089         if(this.mobile_restrict_height && listHeight < bodyHeight){
19090             this.touchViewBodyEl.setHeight(listHeight);
19091         }
19092         
19093         var _this = this;
19094         
19095         if(firstChecked && listHeight > bodyHeight){
19096             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19097         }
19098         
19099     },
19100     
19101     onTouchViewLoadException : function()
19102     {
19103         this.hideTouchView();
19104     },
19105     
19106     onTouchViewEmptyResults : function()
19107     {
19108         this.clearTouchView();
19109         
19110         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19111         
19112         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19113         
19114     },
19115     
19116     clearTouchView : function()
19117     {
19118         this.touchViewListGroup.dom.innerHTML = '';
19119     },
19120     
19121     onTouchViewClick : function(e, el, o)
19122     {
19123         e.preventDefault();
19124         
19125         var row = o.row;
19126         var rowIndex = o.rowIndex;
19127         
19128         var r = this.store.getAt(rowIndex);
19129         
19130         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19131             
19132             if(!this.multiple){
19133                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19134                     c.dom.removeAttribute('checked');
19135                 }, this);
19136
19137                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19138
19139                 this.setFromData(r.data);
19140
19141                 var close = this.closeTriggerEl();
19142
19143                 if(close){
19144                     close.show();
19145                 }
19146
19147                 this.hideTouchView();
19148
19149                 this.fireEvent('select', this, r, rowIndex);
19150
19151                 return;
19152             }
19153
19154             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19155                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19156                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19157                 return;
19158             }
19159
19160             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19161             this.addItem(r.data);
19162             this.tickItems.push(r.data);
19163         }
19164     },
19165     
19166     getAutoCreateNativeIOS : function()
19167     {
19168         var cfg = {
19169             cls: 'form-group' //input-group,
19170         };
19171         
19172         var combobox =  {
19173             tag: 'select',
19174             cls : 'roo-ios-select'
19175         };
19176         
19177         if (this.name) {
19178             combobox.name = this.name;
19179         }
19180         
19181         if (this.disabled) {
19182             combobox.disabled = true;
19183         }
19184         
19185         var settings = this;
19186         
19187         ['xs','sm','md','lg'].map(function(size){
19188             if (settings[size]) {
19189                 cfg.cls += ' col-' + size + '-' + settings[size];
19190             }
19191         });
19192         
19193         cfg.cn = combobox;
19194         
19195         return cfg;
19196         
19197     },
19198     
19199     initIOSView : function()
19200     {
19201         this.store.on('load', this.onIOSViewLoad, this);
19202         
19203         return;
19204     },
19205     
19206     onIOSViewLoad : function()
19207     {
19208         if(this.store.getCount() < 1){
19209             return;
19210         }
19211         
19212         this.clearIOSView();
19213         
19214         if(this.allowBlank) {
19215             
19216             var default_text = '-- SELECT --';
19217             
19218             if(this.placeholder.length){
19219                 default_text = this.placeholder;
19220             }
19221             
19222             if(this.emptyTitle.length){
19223                 default_text += ' - ' + this.emptyTitle + ' -';
19224             }
19225             
19226             var opt = this.inputEl().createChild({
19227                 tag: 'option',
19228                 value : 0,
19229                 html : default_text
19230             });
19231             
19232             var o = {};
19233             o[this.valueField] = 0;
19234             o[this.displayField] = default_text;
19235             
19236             this.ios_options.push({
19237                 data : o,
19238                 el : opt
19239             });
19240             
19241         }
19242         
19243         this.store.data.each(function(d, rowIndex){
19244             
19245             var html = '';
19246             
19247             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19248                 html = d.data[this.displayField];
19249             }
19250             
19251             var value = '';
19252             
19253             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19254                 value = d.data[this.valueField];
19255             }
19256             
19257             var option = {
19258                 tag: 'option',
19259                 value : value,
19260                 html : html
19261             };
19262             
19263             if(this.value == d.data[this.valueField]){
19264                 option['selected'] = true;
19265             }
19266             
19267             var opt = this.inputEl().createChild(option);
19268             
19269             this.ios_options.push({
19270                 data : d.data,
19271                 el : opt
19272             });
19273             
19274         }, this);
19275         
19276         this.inputEl().on('change', function(){
19277            this.fireEvent('select', this);
19278         }, this);
19279         
19280     },
19281     
19282     clearIOSView: function()
19283     {
19284         this.inputEl().dom.innerHTML = '';
19285         
19286         this.ios_options = [];
19287     },
19288     
19289     setIOSValue: function(v)
19290     {
19291         this.value = v;
19292         
19293         if(!this.ios_options){
19294             return;
19295         }
19296         
19297         Roo.each(this.ios_options, function(opts){
19298            
19299            opts.el.dom.removeAttribute('selected');
19300            
19301            if(opts.data[this.valueField] != v){
19302                return;
19303            }
19304            
19305            opts.el.dom.setAttribute('selected', true);
19306            
19307         }, this);
19308     }
19309
19310     /** 
19311     * @cfg {Boolean} grow 
19312     * @hide 
19313     */
19314     /** 
19315     * @cfg {Number} growMin 
19316     * @hide 
19317     */
19318     /** 
19319     * @cfg {Number} growMax 
19320     * @hide 
19321     */
19322     /**
19323      * @hide
19324      * @method autoSize
19325      */
19326 });
19327
19328 Roo.apply(Roo.bootstrap.ComboBox,  {
19329     
19330     header : {
19331         tag: 'div',
19332         cls: 'modal-header',
19333         cn: [
19334             {
19335                 tag: 'h4',
19336                 cls: 'modal-title'
19337             }
19338         ]
19339     },
19340     
19341     body : {
19342         tag: 'div',
19343         cls: 'modal-body',
19344         cn: [
19345             {
19346                 tag: 'ul',
19347                 cls: 'list-group'
19348             }
19349         ]
19350     },
19351     
19352     listItemRadio : {
19353         tag: 'li',
19354         cls: 'list-group-item',
19355         cn: [
19356             {
19357                 tag: 'span',
19358                 cls: 'roo-combobox-list-group-item-value'
19359             },
19360             {
19361                 tag: 'div',
19362                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19363                 cn: [
19364                     {
19365                         tag: 'input',
19366                         type: 'radio'
19367                     },
19368                     {
19369                         tag: 'label'
19370                     }
19371                 ]
19372             }
19373         ]
19374     },
19375     
19376     listItemCheckbox : {
19377         tag: 'li',
19378         cls: 'list-group-item',
19379         cn: [
19380             {
19381                 tag: 'span',
19382                 cls: 'roo-combobox-list-group-item-value'
19383             },
19384             {
19385                 tag: 'div',
19386                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19387                 cn: [
19388                     {
19389                         tag: 'input',
19390                         type: 'checkbox'
19391                     },
19392                     {
19393                         tag: 'label'
19394                     }
19395                 ]
19396             }
19397         ]
19398     },
19399     
19400     emptyResult : {
19401         tag: 'div',
19402         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19403     },
19404     
19405     footer : {
19406         tag: 'div',
19407         cls: 'modal-footer',
19408         cn: [
19409             {
19410                 tag: 'div',
19411                 cls: 'row',
19412                 cn: [
19413                     {
19414                         tag: 'div',
19415                         cls: 'col-xs-6 text-left',
19416                         cn: {
19417                             tag: 'button',
19418                             cls: 'btn btn-danger roo-touch-view-cancel',
19419                             html: 'Cancel'
19420                         }
19421                     },
19422                     {
19423                         tag: 'div',
19424                         cls: 'col-xs-6 text-right',
19425                         cn: {
19426                             tag: 'button',
19427                             cls: 'btn btn-success roo-touch-view-ok',
19428                             html: 'OK'
19429                         }
19430                     }
19431                 ]
19432             }
19433         ]
19434         
19435     }
19436 });
19437
19438 Roo.apply(Roo.bootstrap.ComboBox,  {
19439     
19440     touchViewTemplate : {
19441         tag: 'div',
19442         cls: 'modal fade roo-combobox-touch-view',
19443         cn: [
19444             {
19445                 tag: 'div',
19446                 cls: 'modal-dialog',
19447                 style : 'position:fixed', // we have to fix position....
19448                 cn: [
19449                     {
19450                         tag: 'div',
19451                         cls: 'modal-content',
19452                         cn: [
19453                             Roo.bootstrap.ComboBox.header,
19454                             Roo.bootstrap.ComboBox.body,
19455                             Roo.bootstrap.ComboBox.footer
19456                         ]
19457                     }
19458                 ]
19459             }
19460         ]
19461     }
19462 });/*
19463  * Based on:
19464  * Ext JS Library 1.1.1
19465  * Copyright(c) 2006-2007, Ext JS, LLC.
19466  *
19467  * Originally Released Under LGPL - original licence link has changed is not relivant.
19468  *
19469  * Fork - LGPL
19470  * <script type="text/javascript">
19471  */
19472
19473 /**
19474  * @class Roo.View
19475  * @extends Roo.util.Observable
19476  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19477  * This class also supports single and multi selection modes. <br>
19478  * Create a data model bound view:
19479  <pre><code>
19480  var store = new Roo.data.Store(...);
19481
19482  var view = new Roo.View({
19483     el : "my-element",
19484     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19485  
19486     singleSelect: true,
19487     selectedClass: "ydataview-selected",
19488     store: store
19489  });
19490
19491  // listen for node click?
19492  view.on("click", function(vw, index, node, e){
19493  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19494  });
19495
19496  // load XML data
19497  dataModel.load("foobar.xml");
19498  </code></pre>
19499  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19500  * <br><br>
19501  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19502  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19503  * 
19504  * Note: old style constructor is still suported (container, template, config)
19505  * 
19506  * @constructor
19507  * Create a new View
19508  * @param {Object} config The config object
19509  * 
19510  */
19511 Roo.View = function(config, depreciated_tpl, depreciated_config){
19512     
19513     this.parent = false;
19514     
19515     if (typeof(depreciated_tpl) == 'undefined') {
19516         // new way.. - universal constructor.
19517         Roo.apply(this, config);
19518         this.el  = Roo.get(this.el);
19519     } else {
19520         // old format..
19521         this.el  = Roo.get(config);
19522         this.tpl = depreciated_tpl;
19523         Roo.apply(this, depreciated_config);
19524     }
19525     this.wrapEl  = this.el.wrap().wrap();
19526     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19527     
19528     
19529     if(typeof(this.tpl) == "string"){
19530         this.tpl = new Roo.Template(this.tpl);
19531     } else {
19532         // support xtype ctors..
19533         this.tpl = new Roo.factory(this.tpl, Roo);
19534     }
19535     
19536     
19537     this.tpl.compile();
19538     
19539     /** @private */
19540     this.addEvents({
19541         /**
19542          * @event beforeclick
19543          * Fires before a click is processed. Returns false to cancel the default action.
19544          * @param {Roo.View} this
19545          * @param {Number} index The index of the target node
19546          * @param {HTMLElement} node The target node
19547          * @param {Roo.EventObject} e The raw event object
19548          */
19549             "beforeclick" : true,
19550         /**
19551          * @event click
19552          * Fires when a template node is clicked.
19553          * @param {Roo.View} this
19554          * @param {Number} index The index of the target node
19555          * @param {HTMLElement} node The target node
19556          * @param {Roo.EventObject} e The raw event object
19557          */
19558             "click" : true,
19559         /**
19560          * @event dblclick
19561          * Fires when a template node is double clicked.
19562          * @param {Roo.View} this
19563          * @param {Number} index The index of the target node
19564          * @param {HTMLElement} node The target node
19565          * @param {Roo.EventObject} e The raw event object
19566          */
19567             "dblclick" : true,
19568         /**
19569          * @event contextmenu
19570          * Fires when a template node is right clicked.
19571          * @param {Roo.View} this
19572          * @param {Number} index The index of the target node
19573          * @param {HTMLElement} node The target node
19574          * @param {Roo.EventObject} e The raw event object
19575          */
19576             "contextmenu" : true,
19577         /**
19578          * @event selectionchange
19579          * Fires when the selected nodes change.
19580          * @param {Roo.View} this
19581          * @param {Array} selections Array of the selected nodes
19582          */
19583             "selectionchange" : true,
19584     
19585         /**
19586          * @event beforeselect
19587          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19588          * @param {Roo.View} this
19589          * @param {HTMLElement} node The node to be selected
19590          * @param {Array} selections Array of currently selected nodes
19591          */
19592             "beforeselect" : true,
19593         /**
19594          * @event preparedata
19595          * Fires on every row to render, to allow you to change the data.
19596          * @param {Roo.View} this
19597          * @param {Object} data to be rendered (change this)
19598          */
19599           "preparedata" : true
19600           
19601           
19602         });
19603
19604
19605
19606     this.el.on({
19607         "click": this.onClick,
19608         "dblclick": this.onDblClick,
19609         "contextmenu": this.onContextMenu,
19610         scope:this
19611     });
19612
19613     this.selections = [];
19614     this.nodes = [];
19615     this.cmp = new Roo.CompositeElementLite([]);
19616     if(this.store){
19617         this.store = Roo.factory(this.store, Roo.data);
19618         this.setStore(this.store, true);
19619     }
19620     
19621     if ( this.footer && this.footer.xtype) {
19622            
19623          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19624         
19625         this.footer.dataSource = this.store;
19626         this.footer.container = fctr;
19627         this.footer = Roo.factory(this.footer, Roo);
19628         fctr.insertFirst(this.el);
19629         
19630         // this is a bit insane - as the paging toolbar seems to detach the el..
19631 //        dom.parentNode.parentNode.parentNode
19632          // they get detached?
19633     }
19634     
19635     
19636     Roo.View.superclass.constructor.call(this);
19637     
19638     
19639 };
19640
19641 Roo.extend(Roo.View, Roo.util.Observable, {
19642     
19643      /**
19644      * @cfg {Roo.data.Store} store Data store to load data from.
19645      */
19646     store : false,
19647     
19648     /**
19649      * @cfg {String|Roo.Element} el The container element.
19650      */
19651     el : '',
19652     
19653     /**
19654      * @cfg {String|Roo.Template} tpl The template used by this View 
19655      */
19656     tpl : false,
19657     /**
19658      * @cfg {String} dataName the named area of the template to use as the data area
19659      *                          Works with domtemplates roo-name="name"
19660      */
19661     dataName: false,
19662     /**
19663      * @cfg {String} selectedClass The css class to add to selected nodes
19664      */
19665     selectedClass : "x-view-selected",
19666      /**
19667      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19668      */
19669     emptyText : "",
19670     
19671     /**
19672      * @cfg {String} text to display on mask (default Loading)
19673      */
19674     mask : false,
19675     /**
19676      * @cfg {Boolean} multiSelect Allow multiple selection
19677      */
19678     multiSelect : false,
19679     /**
19680      * @cfg {Boolean} singleSelect Allow single selection
19681      */
19682     singleSelect:  false,
19683     
19684     /**
19685      * @cfg {Boolean} toggleSelect - selecting 
19686      */
19687     toggleSelect : false,
19688     
19689     /**
19690      * @cfg {Boolean} tickable - selecting 
19691      */
19692     tickable : false,
19693     
19694     /**
19695      * Returns the element this view is bound to.
19696      * @return {Roo.Element}
19697      */
19698     getEl : function(){
19699         return this.wrapEl;
19700     },
19701     
19702     
19703
19704     /**
19705      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19706      */
19707     refresh : function(){
19708         //Roo.log('refresh');
19709         var t = this.tpl;
19710         
19711         // if we are using something like 'domtemplate', then
19712         // the what gets used is:
19713         // t.applySubtemplate(NAME, data, wrapping data..)
19714         // the outer template then get' applied with
19715         //     the store 'extra data'
19716         // and the body get's added to the
19717         //      roo-name="data" node?
19718         //      <span class='roo-tpl-{name}'></span> ?????
19719         
19720         
19721         
19722         this.clearSelections();
19723         this.el.update("");
19724         var html = [];
19725         var records = this.store.getRange();
19726         if(records.length < 1) {
19727             
19728             // is this valid??  = should it render a template??
19729             
19730             this.el.update(this.emptyText);
19731             return;
19732         }
19733         var el = this.el;
19734         if (this.dataName) {
19735             this.el.update(t.apply(this.store.meta)); //????
19736             el = this.el.child('.roo-tpl-' + this.dataName);
19737         }
19738         
19739         for(var i = 0, len = records.length; i < len; i++){
19740             var data = this.prepareData(records[i].data, i, records[i]);
19741             this.fireEvent("preparedata", this, data, i, records[i]);
19742             
19743             var d = Roo.apply({}, data);
19744             
19745             if(this.tickable){
19746                 Roo.apply(d, {'roo-id' : Roo.id()});
19747                 
19748                 var _this = this;
19749             
19750                 Roo.each(this.parent.item, function(item){
19751                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19752                         return;
19753                     }
19754                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19755                 });
19756             }
19757             
19758             html[html.length] = Roo.util.Format.trim(
19759                 this.dataName ?
19760                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19761                     t.apply(d)
19762             );
19763         }
19764         
19765         
19766         
19767         el.update(html.join(""));
19768         this.nodes = el.dom.childNodes;
19769         this.updateIndexes(0);
19770     },
19771     
19772
19773     /**
19774      * Function to override to reformat the data that is sent to
19775      * the template for each node.
19776      * DEPRICATED - use the preparedata event handler.
19777      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19778      * a JSON object for an UpdateManager bound view).
19779      */
19780     prepareData : function(data, index, record)
19781     {
19782         this.fireEvent("preparedata", this, data, index, record);
19783         return data;
19784     },
19785
19786     onUpdate : function(ds, record){
19787         // Roo.log('on update');   
19788         this.clearSelections();
19789         var index = this.store.indexOf(record);
19790         var n = this.nodes[index];
19791         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19792         n.parentNode.removeChild(n);
19793         this.updateIndexes(index, index);
19794     },
19795
19796     
19797     
19798 // --------- FIXME     
19799     onAdd : function(ds, records, index)
19800     {
19801         //Roo.log(['on Add', ds, records, index] );        
19802         this.clearSelections();
19803         if(this.nodes.length == 0){
19804             this.refresh();
19805             return;
19806         }
19807         var n = this.nodes[index];
19808         for(var i = 0, len = records.length; i < len; i++){
19809             var d = this.prepareData(records[i].data, i, records[i]);
19810             if(n){
19811                 this.tpl.insertBefore(n, d);
19812             }else{
19813                 
19814                 this.tpl.append(this.el, d);
19815             }
19816         }
19817         this.updateIndexes(index);
19818     },
19819
19820     onRemove : function(ds, record, index){
19821        // Roo.log('onRemove');
19822         this.clearSelections();
19823         var el = this.dataName  ?
19824             this.el.child('.roo-tpl-' + this.dataName) :
19825             this.el; 
19826         
19827         el.dom.removeChild(this.nodes[index]);
19828         this.updateIndexes(index);
19829     },
19830
19831     /**
19832      * Refresh an individual node.
19833      * @param {Number} index
19834      */
19835     refreshNode : function(index){
19836         this.onUpdate(this.store, this.store.getAt(index));
19837     },
19838
19839     updateIndexes : function(startIndex, endIndex){
19840         var ns = this.nodes;
19841         startIndex = startIndex || 0;
19842         endIndex = endIndex || ns.length - 1;
19843         for(var i = startIndex; i <= endIndex; i++){
19844             ns[i].nodeIndex = i;
19845         }
19846     },
19847
19848     /**
19849      * Changes the data store this view uses and refresh the view.
19850      * @param {Store} store
19851      */
19852     setStore : function(store, initial){
19853         if(!initial && this.store){
19854             this.store.un("datachanged", this.refresh);
19855             this.store.un("add", this.onAdd);
19856             this.store.un("remove", this.onRemove);
19857             this.store.un("update", this.onUpdate);
19858             this.store.un("clear", this.refresh);
19859             this.store.un("beforeload", this.onBeforeLoad);
19860             this.store.un("load", this.onLoad);
19861             this.store.un("loadexception", this.onLoad);
19862         }
19863         if(store){
19864           
19865             store.on("datachanged", this.refresh, this);
19866             store.on("add", this.onAdd, this);
19867             store.on("remove", this.onRemove, this);
19868             store.on("update", this.onUpdate, this);
19869             store.on("clear", this.refresh, this);
19870             store.on("beforeload", this.onBeforeLoad, this);
19871             store.on("load", this.onLoad, this);
19872             store.on("loadexception", this.onLoad, this);
19873         }
19874         
19875         if(store){
19876             this.refresh();
19877         }
19878     },
19879     /**
19880      * onbeforeLoad - masks the loading area.
19881      *
19882      */
19883     onBeforeLoad : function(store,opts)
19884     {
19885          //Roo.log('onBeforeLoad');   
19886         if (!opts.add) {
19887             this.el.update("");
19888         }
19889         this.el.mask(this.mask ? this.mask : "Loading" ); 
19890     },
19891     onLoad : function ()
19892     {
19893         this.el.unmask();
19894     },
19895     
19896
19897     /**
19898      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19899      * @param {HTMLElement} node
19900      * @return {HTMLElement} The template node
19901      */
19902     findItemFromChild : function(node){
19903         var el = this.dataName  ?
19904             this.el.child('.roo-tpl-' + this.dataName,true) :
19905             this.el.dom; 
19906         
19907         if(!node || node.parentNode == el){
19908                     return node;
19909             }
19910             var p = node.parentNode;
19911             while(p && p != el){
19912             if(p.parentNode == el){
19913                 return p;
19914             }
19915             p = p.parentNode;
19916         }
19917             return null;
19918     },
19919
19920     /** @ignore */
19921     onClick : function(e){
19922         var item = this.findItemFromChild(e.getTarget());
19923         if(item){
19924             var index = this.indexOf(item);
19925             if(this.onItemClick(item, index, e) !== false){
19926                 this.fireEvent("click", this, index, item, e);
19927             }
19928         }else{
19929             this.clearSelections();
19930         }
19931     },
19932
19933     /** @ignore */
19934     onContextMenu : function(e){
19935         var item = this.findItemFromChild(e.getTarget());
19936         if(item){
19937             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19938         }
19939     },
19940
19941     /** @ignore */
19942     onDblClick : function(e){
19943         var item = this.findItemFromChild(e.getTarget());
19944         if(item){
19945             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19946         }
19947     },
19948
19949     onItemClick : function(item, index, e)
19950     {
19951         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19952             return false;
19953         }
19954         if (this.toggleSelect) {
19955             var m = this.isSelected(item) ? 'unselect' : 'select';
19956             //Roo.log(m);
19957             var _t = this;
19958             _t[m](item, true, false);
19959             return true;
19960         }
19961         if(this.multiSelect || this.singleSelect){
19962             if(this.multiSelect && e.shiftKey && this.lastSelection){
19963                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19964             }else{
19965                 this.select(item, this.multiSelect && e.ctrlKey);
19966                 this.lastSelection = item;
19967             }
19968             
19969             if(!this.tickable){
19970                 e.preventDefault();
19971             }
19972             
19973         }
19974         return true;
19975     },
19976
19977     /**
19978      * Get the number of selected nodes.
19979      * @return {Number}
19980      */
19981     getSelectionCount : function(){
19982         return this.selections.length;
19983     },
19984
19985     /**
19986      * Get the currently selected nodes.
19987      * @return {Array} An array of HTMLElements
19988      */
19989     getSelectedNodes : function(){
19990         return this.selections;
19991     },
19992
19993     /**
19994      * Get the indexes of the selected nodes.
19995      * @return {Array}
19996      */
19997     getSelectedIndexes : function(){
19998         var indexes = [], s = this.selections;
19999         for(var i = 0, len = s.length; i < len; i++){
20000             indexes.push(s[i].nodeIndex);
20001         }
20002         return indexes;
20003     },
20004
20005     /**
20006      * Clear all selections
20007      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20008      */
20009     clearSelections : function(suppressEvent){
20010         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20011             this.cmp.elements = this.selections;
20012             this.cmp.removeClass(this.selectedClass);
20013             this.selections = [];
20014             if(!suppressEvent){
20015                 this.fireEvent("selectionchange", this, this.selections);
20016             }
20017         }
20018     },
20019
20020     /**
20021      * Returns true if the passed node is selected
20022      * @param {HTMLElement/Number} node The node or node index
20023      * @return {Boolean}
20024      */
20025     isSelected : function(node){
20026         var s = this.selections;
20027         if(s.length < 1){
20028             return false;
20029         }
20030         node = this.getNode(node);
20031         return s.indexOf(node) !== -1;
20032     },
20033
20034     /**
20035      * Selects nodes.
20036      * @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
20037      * @param {Boolean} keepExisting (optional) true to keep existing selections
20038      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20039      */
20040     select : function(nodeInfo, keepExisting, suppressEvent){
20041         if(nodeInfo instanceof Array){
20042             if(!keepExisting){
20043                 this.clearSelections(true);
20044             }
20045             for(var i = 0, len = nodeInfo.length; i < len; i++){
20046                 this.select(nodeInfo[i], true, true);
20047             }
20048             return;
20049         } 
20050         var node = this.getNode(nodeInfo);
20051         if(!node || this.isSelected(node)){
20052             return; // already selected.
20053         }
20054         if(!keepExisting){
20055             this.clearSelections(true);
20056         }
20057         
20058         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20059             Roo.fly(node).addClass(this.selectedClass);
20060             this.selections.push(node);
20061             if(!suppressEvent){
20062                 this.fireEvent("selectionchange", this, this.selections);
20063             }
20064         }
20065         
20066         
20067     },
20068       /**
20069      * Unselects nodes.
20070      * @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
20071      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20072      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20073      */
20074     unselect : function(nodeInfo, keepExisting, suppressEvent)
20075     {
20076         if(nodeInfo instanceof Array){
20077             Roo.each(this.selections, function(s) {
20078                 this.unselect(s, nodeInfo);
20079             }, this);
20080             return;
20081         }
20082         var node = this.getNode(nodeInfo);
20083         if(!node || !this.isSelected(node)){
20084             //Roo.log("not selected");
20085             return; // not selected.
20086         }
20087         // fireevent???
20088         var ns = [];
20089         Roo.each(this.selections, function(s) {
20090             if (s == node ) {
20091                 Roo.fly(node).removeClass(this.selectedClass);
20092
20093                 return;
20094             }
20095             ns.push(s);
20096         },this);
20097         
20098         this.selections= ns;
20099         this.fireEvent("selectionchange", this, this.selections);
20100     },
20101
20102     /**
20103      * Gets a template node.
20104      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20105      * @return {HTMLElement} The node or null if it wasn't found
20106      */
20107     getNode : function(nodeInfo){
20108         if(typeof nodeInfo == "string"){
20109             return document.getElementById(nodeInfo);
20110         }else if(typeof nodeInfo == "number"){
20111             return this.nodes[nodeInfo];
20112         }
20113         return nodeInfo;
20114     },
20115
20116     /**
20117      * Gets a range template nodes.
20118      * @param {Number} startIndex
20119      * @param {Number} endIndex
20120      * @return {Array} An array of nodes
20121      */
20122     getNodes : function(start, end){
20123         var ns = this.nodes;
20124         start = start || 0;
20125         end = typeof end == "undefined" ? ns.length - 1 : end;
20126         var nodes = [];
20127         if(start <= end){
20128             for(var i = start; i <= end; i++){
20129                 nodes.push(ns[i]);
20130             }
20131         } else{
20132             for(var i = start; i >= end; i--){
20133                 nodes.push(ns[i]);
20134             }
20135         }
20136         return nodes;
20137     },
20138
20139     /**
20140      * Finds the index of the passed node
20141      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20142      * @return {Number} The index of the node or -1
20143      */
20144     indexOf : function(node){
20145         node = this.getNode(node);
20146         if(typeof node.nodeIndex == "number"){
20147             return node.nodeIndex;
20148         }
20149         var ns = this.nodes;
20150         for(var i = 0, len = ns.length; i < len; i++){
20151             if(ns[i] == node){
20152                 return i;
20153             }
20154         }
20155         return -1;
20156     }
20157 });
20158 /*
20159  * - LGPL
20160  *
20161  * based on jquery fullcalendar
20162  * 
20163  */
20164
20165 Roo.bootstrap = Roo.bootstrap || {};
20166 /**
20167  * @class Roo.bootstrap.Calendar
20168  * @extends Roo.bootstrap.Component
20169  * Bootstrap Calendar class
20170  * @cfg {Boolean} loadMask (true|false) default false
20171  * @cfg {Object} header generate the user specific header of the calendar, default false
20172
20173  * @constructor
20174  * Create a new Container
20175  * @param {Object} config The config object
20176  */
20177
20178
20179
20180 Roo.bootstrap.Calendar = function(config){
20181     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20182      this.addEvents({
20183         /**
20184              * @event select
20185              * Fires when a date is selected
20186              * @param {DatePicker} this
20187              * @param {Date} date The selected date
20188              */
20189         'select': true,
20190         /**
20191              * @event monthchange
20192              * Fires when the displayed month changes 
20193              * @param {DatePicker} this
20194              * @param {Date} date The selected month
20195              */
20196         'monthchange': true,
20197         /**
20198              * @event evententer
20199              * Fires when mouse over an event
20200              * @param {Calendar} this
20201              * @param {event} Event
20202              */
20203         'evententer': true,
20204         /**
20205              * @event eventleave
20206              * Fires when the mouse leaves an
20207              * @param {Calendar} this
20208              * @param {event}
20209              */
20210         'eventleave': true,
20211         /**
20212              * @event eventclick
20213              * Fires when the mouse click an
20214              * @param {Calendar} this
20215              * @param {event}
20216              */
20217         'eventclick': true
20218         
20219     });
20220
20221 };
20222
20223 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20224     
20225           /**
20226      * @cfg {Roo.data.Store} store
20227      * The data source for the calendar
20228      */
20229         store : false,
20230      /**
20231      * @cfg {Number} startDay
20232      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20233      */
20234     startDay : 0,
20235     
20236     loadMask : false,
20237     
20238     header : false,
20239       
20240     getAutoCreate : function(){
20241         
20242         
20243         var fc_button = function(name, corner, style, content ) {
20244             return Roo.apply({},{
20245                 tag : 'span',
20246                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20247                          (corner.length ?
20248                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20249                             ''
20250                         ),
20251                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20252                 unselectable: 'on'
20253             });
20254         };
20255         
20256         var header = {};
20257         
20258         if(!this.header){
20259             header = {
20260                 tag : 'table',
20261                 cls : 'fc-header',
20262                 style : 'width:100%',
20263                 cn : [
20264                     {
20265                         tag: 'tr',
20266                         cn : [
20267                             {
20268                                 tag : 'td',
20269                                 cls : 'fc-header-left',
20270                                 cn : [
20271                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20272                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20273                                     { tag: 'span', cls: 'fc-header-space' },
20274                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20275
20276
20277                                 ]
20278                             },
20279
20280                             {
20281                                 tag : 'td',
20282                                 cls : 'fc-header-center',
20283                                 cn : [
20284                                     {
20285                                         tag: 'span',
20286                                         cls: 'fc-header-title',
20287                                         cn : {
20288                                             tag: 'H2',
20289                                             html : 'month / year'
20290                                         }
20291                                     }
20292
20293                                 ]
20294                             },
20295                             {
20296                                 tag : 'td',
20297                                 cls : 'fc-header-right',
20298                                 cn : [
20299                               /*      fc_button('month', 'left', '', 'month' ),
20300                                     fc_button('week', '', '', 'week' ),
20301                                     fc_button('day', 'right', '', 'day' )
20302                                 */    
20303
20304                                 ]
20305                             }
20306
20307                         ]
20308                     }
20309                 ]
20310             };
20311         }
20312         
20313         header = this.header;
20314         
20315        
20316         var cal_heads = function() {
20317             var ret = [];
20318             // fixme - handle this.
20319             
20320             for (var i =0; i < Date.dayNames.length; i++) {
20321                 var d = Date.dayNames[i];
20322                 ret.push({
20323                     tag: 'th',
20324                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20325                     html : d.substring(0,3)
20326                 });
20327                 
20328             }
20329             ret[0].cls += ' fc-first';
20330             ret[6].cls += ' fc-last';
20331             return ret;
20332         };
20333         var cal_cell = function(n) {
20334             return  {
20335                 tag: 'td',
20336                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20337                 cn : [
20338                     {
20339                         cn : [
20340                             {
20341                                 cls: 'fc-day-number',
20342                                 html: 'D'
20343                             },
20344                             {
20345                                 cls: 'fc-day-content',
20346                              
20347                                 cn : [
20348                                      {
20349                                         style: 'position: relative;' // height: 17px;
20350                                     }
20351                                 ]
20352                             }
20353                             
20354                             
20355                         ]
20356                     }
20357                 ]
20358                 
20359             }
20360         };
20361         var cal_rows = function() {
20362             
20363             var ret = [];
20364             for (var r = 0; r < 6; r++) {
20365                 var row= {
20366                     tag : 'tr',
20367                     cls : 'fc-week',
20368                     cn : []
20369                 };
20370                 
20371                 for (var i =0; i < Date.dayNames.length; i++) {
20372                     var d = Date.dayNames[i];
20373                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20374
20375                 }
20376                 row.cn[0].cls+=' fc-first';
20377                 row.cn[0].cn[0].style = 'min-height:90px';
20378                 row.cn[6].cls+=' fc-last';
20379                 ret.push(row);
20380                 
20381             }
20382             ret[0].cls += ' fc-first';
20383             ret[4].cls += ' fc-prev-last';
20384             ret[5].cls += ' fc-last';
20385             return ret;
20386             
20387         };
20388         
20389         var cal_table = {
20390             tag: 'table',
20391             cls: 'fc-border-separate',
20392             style : 'width:100%',
20393             cellspacing  : 0,
20394             cn : [
20395                 { 
20396                     tag: 'thead',
20397                     cn : [
20398                         { 
20399                             tag: 'tr',
20400                             cls : 'fc-first fc-last',
20401                             cn : cal_heads()
20402                         }
20403                     ]
20404                 },
20405                 { 
20406                     tag: 'tbody',
20407                     cn : cal_rows()
20408                 }
20409                   
20410             ]
20411         };
20412          
20413          var cfg = {
20414             cls : 'fc fc-ltr',
20415             cn : [
20416                 header,
20417                 {
20418                     cls : 'fc-content',
20419                     style : "position: relative;",
20420                     cn : [
20421                         {
20422                             cls : 'fc-view fc-view-month fc-grid',
20423                             style : 'position: relative',
20424                             unselectable : 'on',
20425                             cn : [
20426                                 {
20427                                     cls : 'fc-event-container',
20428                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20429                                 },
20430                                 cal_table
20431                             ]
20432                         }
20433                     ]
20434     
20435                 }
20436            ] 
20437             
20438         };
20439         
20440          
20441         
20442         return cfg;
20443     },
20444     
20445     
20446     initEvents : function()
20447     {
20448         if(!this.store){
20449             throw "can not find store for calendar";
20450         }
20451         
20452         var mark = {
20453             tag: "div",
20454             cls:"x-dlg-mask",
20455             style: "text-align:center",
20456             cn: [
20457                 {
20458                     tag: "div",
20459                     style: "background-color:white;width:50%;margin:250 auto",
20460                     cn: [
20461                         {
20462                             tag: "img",
20463                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20464                         },
20465                         {
20466                             tag: "span",
20467                             html: "Loading"
20468                         }
20469                         
20470                     ]
20471                 }
20472             ]
20473         };
20474         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20475         
20476         var size = this.el.select('.fc-content', true).first().getSize();
20477         this.maskEl.setSize(size.width, size.height);
20478         this.maskEl.enableDisplayMode("block");
20479         if(!this.loadMask){
20480             this.maskEl.hide();
20481         }
20482         
20483         this.store = Roo.factory(this.store, Roo.data);
20484         this.store.on('load', this.onLoad, this);
20485         this.store.on('beforeload', this.onBeforeLoad, this);
20486         
20487         this.resize();
20488         
20489         this.cells = this.el.select('.fc-day',true);
20490         //Roo.log(this.cells);
20491         this.textNodes = this.el.query('.fc-day-number');
20492         this.cells.addClassOnOver('fc-state-hover');
20493         
20494         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20495         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20496         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20497         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20498         
20499         this.on('monthchange', this.onMonthChange, this);
20500         
20501         this.update(new Date().clearTime());
20502     },
20503     
20504     resize : function() {
20505         var sz  = this.el.getSize();
20506         
20507         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20508         this.el.select('.fc-day-content div',true).setHeight(34);
20509     },
20510     
20511     
20512     // private
20513     showPrevMonth : function(e){
20514         this.update(this.activeDate.add("mo", -1));
20515     },
20516     showToday : function(e){
20517         this.update(new Date().clearTime());
20518     },
20519     // private
20520     showNextMonth : function(e){
20521         this.update(this.activeDate.add("mo", 1));
20522     },
20523
20524     // private
20525     showPrevYear : function(){
20526         this.update(this.activeDate.add("y", -1));
20527     },
20528
20529     // private
20530     showNextYear : function(){
20531         this.update(this.activeDate.add("y", 1));
20532     },
20533
20534     
20535    // private
20536     update : function(date)
20537     {
20538         var vd = this.activeDate;
20539         this.activeDate = date;
20540 //        if(vd && this.el){
20541 //            var t = date.getTime();
20542 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20543 //                Roo.log('using add remove');
20544 //                
20545 //                this.fireEvent('monthchange', this, date);
20546 //                
20547 //                this.cells.removeClass("fc-state-highlight");
20548 //                this.cells.each(function(c){
20549 //                   if(c.dateValue == t){
20550 //                       c.addClass("fc-state-highlight");
20551 //                       setTimeout(function(){
20552 //                            try{c.dom.firstChild.focus();}catch(e){}
20553 //                       }, 50);
20554 //                       return false;
20555 //                   }
20556 //                   return true;
20557 //                });
20558 //                return;
20559 //            }
20560 //        }
20561         
20562         var days = date.getDaysInMonth();
20563         
20564         var firstOfMonth = date.getFirstDateOfMonth();
20565         var startingPos = firstOfMonth.getDay()-this.startDay;
20566         
20567         if(startingPos < this.startDay){
20568             startingPos += 7;
20569         }
20570         
20571         var pm = date.add(Date.MONTH, -1);
20572         var prevStart = pm.getDaysInMonth()-startingPos;
20573 //        
20574         this.cells = this.el.select('.fc-day',true);
20575         this.textNodes = this.el.query('.fc-day-number');
20576         this.cells.addClassOnOver('fc-state-hover');
20577         
20578         var cells = this.cells.elements;
20579         var textEls = this.textNodes;
20580         
20581         Roo.each(cells, function(cell){
20582             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20583         });
20584         
20585         days += startingPos;
20586
20587         // convert everything to numbers so it's fast
20588         var day = 86400000;
20589         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20590         //Roo.log(d);
20591         //Roo.log(pm);
20592         //Roo.log(prevStart);
20593         
20594         var today = new Date().clearTime().getTime();
20595         var sel = date.clearTime().getTime();
20596         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20597         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20598         var ddMatch = this.disabledDatesRE;
20599         var ddText = this.disabledDatesText;
20600         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20601         var ddaysText = this.disabledDaysText;
20602         var format = this.format;
20603         
20604         var setCellClass = function(cal, cell){
20605             cell.row = 0;
20606             cell.events = [];
20607             cell.more = [];
20608             //Roo.log('set Cell Class');
20609             cell.title = "";
20610             var t = d.getTime();
20611             
20612             //Roo.log(d);
20613             
20614             cell.dateValue = t;
20615             if(t == today){
20616                 cell.className += " fc-today";
20617                 cell.className += " fc-state-highlight";
20618                 cell.title = cal.todayText;
20619             }
20620             if(t == sel){
20621                 // disable highlight in other month..
20622                 //cell.className += " fc-state-highlight";
20623                 
20624             }
20625             // disabling
20626             if(t < min) {
20627                 cell.className = " fc-state-disabled";
20628                 cell.title = cal.minText;
20629                 return;
20630             }
20631             if(t > max) {
20632                 cell.className = " fc-state-disabled";
20633                 cell.title = cal.maxText;
20634                 return;
20635             }
20636             if(ddays){
20637                 if(ddays.indexOf(d.getDay()) != -1){
20638                     cell.title = ddaysText;
20639                     cell.className = " fc-state-disabled";
20640                 }
20641             }
20642             if(ddMatch && format){
20643                 var fvalue = d.dateFormat(format);
20644                 if(ddMatch.test(fvalue)){
20645                     cell.title = ddText.replace("%0", fvalue);
20646                     cell.className = " fc-state-disabled";
20647                 }
20648             }
20649             
20650             if (!cell.initialClassName) {
20651                 cell.initialClassName = cell.dom.className;
20652             }
20653             
20654             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20655         };
20656
20657         var i = 0;
20658         
20659         for(; i < startingPos; i++) {
20660             textEls[i].innerHTML = (++prevStart);
20661             d.setDate(d.getDate()+1);
20662             
20663             cells[i].className = "fc-past fc-other-month";
20664             setCellClass(this, cells[i]);
20665         }
20666         
20667         var intDay = 0;
20668         
20669         for(; i < days; i++){
20670             intDay = i - startingPos + 1;
20671             textEls[i].innerHTML = (intDay);
20672             d.setDate(d.getDate()+1);
20673             
20674             cells[i].className = ''; // "x-date-active";
20675             setCellClass(this, cells[i]);
20676         }
20677         var extraDays = 0;
20678         
20679         for(; i < 42; i++) {
20680             textEls[i].innerHTML = (++extraDays);
20681             d.setDate(d.getDate()+1);
20682             
20683             cells[i].className = "fc-future fc-other-month";
20684             setCellClass(this, cells[i]);
20685         }
20686         
20687         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20688         
20689         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20690         
20691         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20692         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20693         
20694         if(totalRows != 6){
20695             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20696             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20697         }
20698         
20699         this.fireEvent('monthchange', this, date);
20700         
20701         
20702         /*
20703         if(!this.internalRender){
20704             var main = this.el.dom.firstChild;
20705             var w = main.offsetWidth;
20706             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20707             Roo.fly(main).setWidth(w);
20708             this.internalRender = true;
20709             // opera does not respect the auto grow header center column
20710             // then, after it gets a width opera refuses to recalculate
20711             // without a second pass
20712             if(Roo.isOpera && !this.secondPass){
20713                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20714                 this.secondPass = true;
20715                 this.update.defer(10, this, [date]);
20716             }
20717         }
20718         */
20719         
20720     },
20721     
20722     findCell : function(dt) {
20723         dt = dt.clearTime().getTime();
20724         var ret = false;
20725         this.cells.each(function(c){
20726             //Roo.log("check " +c.dateValue + '?=' + dt);
20727             if(c.dateValue == dt){
20728                 ret = c;
20729                 return false;
20730             }
20731             return true;
20732         });
20733         
20734         return ret;
20735     },
20736     
20737     findCells : function(ev) {
20738         var s = ev.start.clone().clearTime().getTime();
20739        // Roo.log(s);
20740         var e= ev.end.clone().clearTime().getTime();
20741        // Roo.log(e);
20742         var ret = [];
20743         this.cells.each(function(c){
20744              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20745             
20746             if(c.dateValue > e){
20747                 return ;
20748             }
20749             if(c.dateValue < s){
20750                 return ;
20751             }
20752             ret.push(c);
20753         });
20754         
20755         return ret;    
20756     },
20757     
20758 //    findBestRow: function(cells)
20759 //    {
20760 //        var ret = 0;
20761 //        
20762 //        for (var i =0 ; i < cells.length;i++) {
20763 //            ret  = Math.max(cells[i].rows || 0,ret);
20764 //        }
20765 //        return ret;
20766 //        
20767 //    },
20768     
20769     
20770     addItem : function(ev)
20771     {
20772         // look for vertical location slot in
20773         var cells = this.findCells(ev);
20774         
20775 //        ev.row = this.findBestRow(cells);
20776         
20777         // work out the location.
20778         
20779         var crow = false;
20780         var rows = [];
20781         for(var i =0; i < cells.length; i++) {
20782             
20783             cells[i].row = cells[0].row;
20784             
20785             if(i == 0){
20786                 cells[i].row = cells[i].row + 1;
20787             }
20788             
20789             if (!crow) {
20790                 crow = {
20791                     start : cells[i],
20792                     end :  cells[i]
20793                 };
20794                 continue;
20795             }
20796             if (crow.start.getY() == cells[i].getY()) {
20797                 // on same row.
20798                 crow.end = cells[i];
20799                 continue;
20800             }
20801             // different row.
20802             rows.push(crow);
20803             crow = {
20804                 start: cells[i],
20805                 end : cells[i]
20806             };
20807             
20808         }
20809         
20810         rows.push(crow);
20811         ev.els = [];
20812         ev.rows = rows;
20813         ev.cells = cells;
20814         
20815         cells[0].events.push(ev);
20816         
20817         this.calevents.push(ev);
20818     },
20819     
20820     clearEvents: function() {
20821         
20822         if(!this.calevents){
20823             return;
20824         }
20825         
20826         Roo.each(this.cells.elements, function(c){
20827             c.row = 0;
20828             c.events = [];
20829             c.more = [];
20830         });
20831         
20832         Roo.each(this.calevents, function(e) {
20833             Roo.each(e.els, function(el) {
20834                 el.un('mouseenter' ,this.onEventEnter, this);
20835                 el.un('mouseleave' ,this.onEventLeave, this);
20836                 el.remove();
20837             },this);
20838         },this);
20839         
20840         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20841             e.remove();
20842         });
20843         
20844     },
20845     
20846     renderEvents: function()
20847     {   
20848         var _this = this;
20849         
20850         this.cells.each(function(c) {
20851             
20852             if(c.row < 5){
20853                 return;
20854             }
20855             
20856             var ev = c.events;
20857             
20858             var r = 4;
20859             if(c.row != c.events.length){
20860                 r = 4 - (4 - (c.row - c.events.length));
20861             }
20862             
20863             c.events = ev.slice(0, r);
20864             c.more = ev.slice(r);
20865             
20866             if(c.more.length && c.more.length == 1){
20867                 c.events.push(c.more.pop());
20868             }
20869             
20870             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20871             
20872         });
20873             
20874         this.cells.each(function(c) {
20875             
20876             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20877             
20878             
20879             for (var e = 0; e < c.events.length; e++){
20880                 var ev = c.events[e];
20881                 var rows = ev.rows;
20882                 
20883                 for(var i = 0; i < rows.length; i++) {
20884                 
20885                     // how many rows should it span..
20886
20887                     var  cfg = {
20888                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20889                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20890
20891                         unselectable : "on",
20892                         cn : [
20893                             {
20894                                 cls: 'fc-event-inner',
20895                                 cn : [
20896     //                                {
20897     //                                  tag:'span',
20898     //                                  cls: 'fc-event-time',
20899     //                                  html : cells.length > 1 ? '' : ev.time
20900     //                                },
20901                                     {
20902                                       tag:'span',
20903                                       cls: 'fc-event-title',
20904                                       html : String.format('{0}', ev.title)
20905                                     }
20906
20907
20908                                 ]
20909                             },
20910                             {
20911                                 cls: 'ui-resizable-handle ui-resizable-e',
20912                                 html : '&nbsp;&nbsp;&nbsp'
20913                             }
20914
20915                         ]
20916                     };
20917
20918                     if (i == 0) {
20919                         cfg.cls += ' fc-event-start';
20920                     }
20921                     if ((i+1) == rows.length) {
20922                         cfg.cls += ' fc-event-end';
20923                     }
20924
20925                     var ctr = _this.el.select('.fc-event-container',true).first();
20926                     var cg = ctr.createChild(cfg);
20927
20928                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20929                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20930
20931                     var r = (c.more.length) ? 1 : 0;
20932                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20933                     cg.setWidth(ebox.right - sbox.x -2);
20934
20935                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20936                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20937                     cg.on('click', _this.onEventClick, _this, ev);
20938
20939                     ev.els.push(cg);
20940                     
20941                 }
20942                 
20943             }
20944             
20945             
20946             if(c.more.length){
20947                 var  cfg = {
20948                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20949                     style : 'position: absolute',
20950                     unselectable : "on",
20951                     cn : [
20952                         {
20953                             cls: 'fc-event-inner',
20954                             cn : [
20955                                 {
20956                                   tag:'span',
20957                                   cls: 'fc-event-title',
20958                                   html : 'More'
20959                                 }
20960
20961
20962                             ]
20963                         },
20964                         {
20965                             cls: 'ui-resizable-handle ui-resizable-e',
20966                             html : '&nbsp;&nbsp;&nbsp'
20967                         }
20968
20969                     ]
20970                 };
20971
20972                 var ctr = _this.el.select('.fc-event-container',true).first();
20973                 var cg = ctr.createChild(cfg);
20974
20975                 var sbox = c.select('.fc-day-content',true).first().getBox();
20976                 var ebox = c.select('.fc-day-content',true).first().getBox();
20977                 //Roo.log(cg);
20978                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20979                 cg.setWidth(ebox.right - sbox.x -2);
20980
20981                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20982                 
20983             }
20984             
20985         });
20986         
20987         
20988         
20989     },
20990     
20991     onEventEnter: function (e, el,event,d) {
20992         this.fireEvent('evententer', this, el, event);
20993     },
20994     
20995     onEventLeave: function (e, el,event,d) {
20996         this.fireEvent('eventleave', this, el, event);
20997     },
20998     
20999     onEventClick: function (e, el,event,d) {
21000         this.fireEvent('eventclick', this, el, event);
21001     },
21002     
21003     onMonthChange: function () {
21004         this.store.load();
21005     },
21006     
21007     onMoreEventClick: function(e, el, more)
21008     {
21009         var _this = this;
21010         
21011         this.calpopover.placement = 'right';
21012         this.calpopover.setTitle('More');
21013         
21014         this.calpopover.setContent('');
21015         
21016         var ctr = this.calpopover.el.select('.popover-content', true).first();
21017         
21018         Roo.each(more, function(m){
21019             var cfg = {
21020                 cls : 'fc-event-hori fc-event-draggable',
21021                 html : m.title
21022             };
21023             var cg = ctr.createChild(cfg);
21024             
21025             cg.on('click', _this.onEventClick, _this, m);
21026         });
21027         
21028         this.calpopover.show(el);
21029         
21030         
21031     },
21032     
21033     onLoad: function () 
21034     {   
21035         this.calevents = [];
21036         var cal = this;
21037         
21038         if(this.store.getCount() > 0){
21039             this.store.data.each(function(d){
21040                cal.addItem({
21041                     id : d.data.id,
21042                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21043                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21044                     time : d.data.start_time,
21045                     title : d.data.title,
21046                     description : d.data.description,
21047                     venue : d.data.venue
21048                 });
21049             });
21050         }
21051         
21052         this.renderEvents();
21053         
21054         if(this.calevents.length && this.loadMask){
21055             this.maskEl.hide();
21056         }
21057     },
21058     
21059     onBeforeLoad: function()
21060     {
21061         this.clearEvents();
21062         if(this.loadMask){
21063             this.maskEl.show();
21064         }
21065     }
21066 });
21067
21068  
21069  /*
21070  * - LGPL
21071  *
21072  * element
21073  * 
21074  */
21075
21076 /**
21077  * @class Roo.bootstrap.Popover
21078  * @extends Roo.bootstrap.Component
21079  * @builder-top
21080  * @children Roo.bootstrap.Component
21081  * Bootstrap Popover class
21082  * @cfg {String} html contents of the popover   (or false to use children..)
21083  * @cfg {String} title of popover (or false to hide)
21084  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21085  * @cfg {String} trigger click || hover (or false to trigger manually)
21086  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21087  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21088  *      - if false and it has a 'parent' then it will be automatically added to that element
21089  *      - if string - Roo.get  will be called 
21090  * @cfg {Number} delay - delay before showing
21091  
21092  * @constructor
21093  * Create a new Popover
21094  * @param {Object} config The config object
21095  */
21096
21097 Roo.bootstrap.Popover = function(config){
21098     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21099     
21100     this.addEvents({
21101         // raw events
21102          /**
21103          * @event show
21104          * After the popover show
21105          * 
21106          * @param {Roo.bootstrap.Popover} this
21107          */
21108         "show" : true,
21109         /**
21110          * @event hide
21111          * After the popover hide
21112          * 
21113          * @param {Roo.bootstrap.Popover} this
21114          */
21115         "hide" : true
21116     });
21117 };
21118
21119 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21120     
21121     title: false,
21122     html: false,
21123     
21124     placement : 'right',
21125     trigger : 'hover', // hover
21126     modal : false,
21127     delay : 0,
21128     
21129     over: false,
21130     
21131     can_build_overlaid : false,
21132     
21133     maskEl : false, // the mask element
21134     headerEl : false,
21135     contentEl : false,
21136     alignEl : false, // when show is called with an element - this get's stored.
21137     
21138     getChildContainer : function()
21139     {
21140         return this.contentEl;
21141         
21142     },
21143     getPopoverHeader : function()
21144     {
21145         this.title = true; // flag not to hide it..
21146         this.headerEl.addClass('p-0');
21147         return this.headerEl
21148     },
21149     
21150     
21151     getAutoCreate : function(){
21152          
21153         var cfg = {
21154            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21155            style: 'display:block',
21156            cn : [
21157                 {
21158                     cls : 'arrow'
21159                 },
21160                 {
21161                     cls : 'popover-inner ',
21162                     cn : [
21163                         {
21164                             tag: 'h3',
21165                             cls: 'popover-title popover-header',
21166                             html : this.title === false ? '' : this.title
21167                         },
21168                         {
21169                             cls : 'popover-content popover-body '  + (this.cls || ''),
21170                             html : this.html || ''
21171                         }
21172                     ]
21173                     
21174                 }
21175            ]
21176         };
21177         
21178         return cfg;
21179     },
21180     /**
21181      * @param {string} the title
21182      */
21183     setTitle: function(str)
21184     {
21185         this.title = str;
21186         if (this.el) {
21187             this.headerEl.dom.innerHTML = str;
21188         }
21189         
21190     },
21191     /**
21192      * @param {string} the body content
21193      */
21194     setContent: function(str)
21195     {
21196         this.html = str;
21197         if (this.contentEl) {
21198             this.contentEl.dom.innerHTML = str;
21199         }
21200         
21201     },
21202     // as it get's added to the bottom of the page.
21203     onRender : function(ct, position)
21204     {
21205         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21206         
21207         
21208         
21209         if(!this.el){
21210             var cfg = Roo.apply({},  this.getAutoCreate());
21211             cfg.id = Roo.id();
21212             
21213             if (this.cls) {
21214                 cfg.cls += ' ' + this.cls;
21215             }
21216             if (this.style) {
21217                 cfg.style = this.style;
21218             }
21219             //Roo.log("adding to ");
21220             this.el = Roo.get(document.body).createChild(cfg, position);
21221 //            Roo.log(this.el);
21222         }
21223         
21224         this.contentEl = this.el.select('.popover-content',true).first();
21225         this.headerEl =  this.el.select('.popover-title',true).first();
21226         
21227         var nitems = [];
21228         if(typeof(this.items) != 'undefined'){
21229             var items = this.items;
21230             delete this.items;
21231
21232             for(var i =0;i < items.length;i++) {
21233                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21234             }
21235         }
21236
21237         this.items = nitems;
21238         
21239         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21240         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21241         
21242         
21243         
21244         this.initEvents();
21245     },
21246     
21247     resizeMask : function()
21248     {
21249         this.maskEl.setSize(
21250             Roo.lib.Dom.getViewWidth(true),
21251             Roo.lib.Dom.getViewHeight(true)
21252         );
21253     },
21254     
21255     initEvents : function()
21256     {
21257         
21258         if (!this.modal) { 
21259             Roo.bootstrap.Popover.register(this);
21260         }
21261          
21262         this.arrowEl = this.el.select('.arrow',true).first();
21263         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21264         this.el.enableDisplayMode('block');
21265         this.el.hide();
21266  
21267         
21268         if (this.over === false && !this.parent()) {
21269             return; 
21270         }
21271         if (this.triggers === false) {
21272             return;
21273         }
21274          
21275         // support parent
21276         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21277         var triggers = this.trigger ? this.trigger.split(' ') : [];
21278         Roo.each(triggers, function(trigger) {
21279         
21280             if (trigger == 'click') {
21281                 on_el.on('click', this.toggle, this);
21282             } else if (trigger != 'manual') {
21283                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21284                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21285       
21286                 on_el.on(eventIn  ,this.enter, this);
21287                 on_el.on(eventOut, this.leave, this);
21288             }
21289         }, this);
21290     },
21291     
21292     
21293     // private
21294     timeout : null,
21295     hoverState : null,
21296     
21297     toggle : function () {
21298         this.hoverState == 'in' ? this.leave() : this.enter();
21299     },
21300     
21301     enter : function () {
21302         
21303         clearTimeout(this.timeout);
21304     
21305         this.hoverState = 'in';
21306     
21307         if (!this.delay || !this.delay.show) {
21308             this.show();
21309             return;
21310         }
21311         var _t = this;
21312         this.timeout = setTimeout(function () {
21313             if (_t.hoverState == 'in') {
21314                 _t.show();
21315             }
21316         }, this.delay.show)
21317     },
21318     
21319     leave : function() {
21320         clearTimeout(this.timeout);
21321     
21322         this.hoverState = 'out';
21323     
21324         if (!this.delay || !this.delay.hide) {
21325             this.hide();
21326             return;
21327         }
21328         var _t = this;
21329         this.timeout = setTimeout(function () {
21330             if (_t.hoverState == 'out') {
21331                 _t.hide();
21332             }
21333         }, this.delay.hide)
21334     },
21335     /**
21336      * Show the popover
21337      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21338      * @param {string} (left|right|top|bottom) position
21339      */
21340     show : function (on_el, placement)
21341     {
21342         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21343         on_el = on_el || false; // default to false
21344          
21345         if (!on_el) {
21346             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21347                 on_el = this.parent().el;
21348             } else if (this.over) {
21349                 on_el = Roo.get(this.over);
21350             }
21351             
21352         }
21353         
21354         this.alignEl = Roo.get( on_el );
21355
21356         if (!this.el) {
21357             this.render(document.body);
21358         }
21359         
21360         
21361          
21362         
21363         if (this.title === false) {
21364             this.headerEl.hide();
21365         }
21366         
21367        
21368         this.el.show();
21369         this.el.dom.style.display = 'block';
21370          
21371  
21372         if (this.alignEl) {
21373             this.updatePosition(this.placement, true);
21374              
21375         } else {
21376             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21377             var es = this.el.getSize();
21378             var x = Roo.lib.Dom.getViewWidth()/2;
21379             var y = Roo.lib.Dom.getViewHeight()/2;
21380             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21381             
21382         }
21383
21384         
21385         //var arrow = this.el.select('.arrow',true).first();
21386         //arrow.set(align[2], 
21387         
21388         this.el.addClass('in');
21389         
21390          
21391         
21392         this.hoverState = 'in';
21393         
21394         if (this.modal) {
21395             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21396             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21397             this.maskEl.dom.style.display = 'block';
21398             this.maskEl.addClass('show');
21399         }
21400         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21401  
21402         this.fireEvent('show', this);
21403         
21404     },
21405     /**
21406      * fire this manually after loading a grid in the table for example
21407      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21408      * @param {Boolean} try and move it if we cant get right position.
21409      */
21410     updatePosition : function(placement, try_move)
21411     {
21412         // allow for calling with no parameters
21413         placement = placement   ? placement :  this.placement;
21414         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21415         
21416         this.el.removeClass([
21417             'fade','top','bottom', 'left', 'right','in',
21418             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21419         ]);
21420         this.el.addClass(placement + ' bs-popover-' + placement);
21421         
21422         if (!this.alignEl ) {
21423             return false;
21424         }
21425         
21426         switch (placement) {
21427             case 'right':
21428                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21429                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21430                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21431                     //normal display... or moved up/down.
21432                     this.el.setXY(offset);
21433                     var xy = this.alignEl.getAnchorXY('tr', false);
21434                     xy[0]+=2;xy[1]+=5;
21435                     this.arrowEl.setXY(xy);
21436                     return true;
21437                 }
21438                 // continue through...
21439                 return this.updatePosition('left', false);
21440                 
21441             
21442             case 'left':
21443                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21444                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21445                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21446                     //normal display... or moved up/down.
21447                     this.el.setXY(offset);
21448                     var xy = this.alignEl.getAnchorXY('tl', false);
21449                     xy[0]-=10;xy[1]+=5; // << fix me
21450                     this.arrowEl.setXY(xy);
21451                     return true;
21452                 }
21453                 // call self...
21454                 return this.updatePosition('right', false);
21455             
21456             case 'top':
21457                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21458                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21459                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21460                     //normal display... or moved up/down.
21461                     this.el.setXY(offset);
21462                     var xy = this.alignEl.getAnchorXY('t', false);
21463                     xy[1]-=10; // << fix me
21464                     this.arrowEl.setXY(xy);
21465                     return true;
21466                 }
21467                 // fall through
21468                return this.updatePosition('bottom', false);
21469             
21470             case 'bottom':
21471                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21472                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21473                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21474                     //normal display... or moved up/down.
21475                     this.el.setXY(offset);
21476                     var xy = this.alignEl.getAnchorXY('b', false);
21477                      xy[1]+=2; // << fix me
21478                     this.arrowEl.setXY(xy);
21479                     return true;
21480                 }
21481                 // fall through
21482                 return this.updatePosition('top', false);
21483                 
21484             
21485         }
21486         
21487         
21488         return false;
21489     },
21490     
21491     hide : function()
21492     {
21493         this.el.setXY([0,0]);
21494         this.el.removeClass('in');
21495         this.el.hide();
21496         this.hoverState = null;
21497         this.maskEl.hide(); // always..
21498         this.fireEvent('hide', this);
21499     }
21500     
21501 });
21502
21503
21504 Roo.apply(Roo.bootstrap.Popover, {
21505
21506     alignment : {
21507         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21508         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21509         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21510         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21511     },
21512     
21513     zIndex : 20001,
21514
21515     clickHander : false,
21516     
21517     
21518
21519     onMouseDown : function(e)
21520     {
21521         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21522             /// what is nothing is showing..
21523             this.hideAll();
21524         }
21525          
21526     },
21527     
21528     
21529     popups : [],
21530     
21531     register : function(popup)
21532     {
21533         if (!Roo.bootstrap.Popover.clickHandler) {
21534             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21535         }
21536         // hide other popups.
21537         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21538         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21539         this.hideAll(); //<< why?
21540         //this.popups.push(popup);
21541     },
21542     hideAll : function()
21543     {
21544         this.popups.forEach(function(p) {
21545             p.hide();
21546         });
21547     },
21548     onShow : function() {
21549         Roo.bootstrap.Popover.popups.push(this);
21550     },
21551     onHide : function() {
21552         Roo.bootstrap.Popover.popups.remove(this);
21553     } 
21554
21555 });/*
21556  * - LGPL
21557  *
21558  * Card header - holder for the card header elements.
21559  * 
21560  */
21561
21562 /**
21563  * @class Roo.bootstrap.PopoverNav
21564  * @extends Roo.bootstrap.NavGroup
21565  * Bootstrap Popover header navigation class
21566  * @constructor
21567  * Create a new Popover Header Navigation 
21568  * @param {Object} config The config object
21569  */
21570
21571 Roo.bootstrap.PopoverNav = function(config){
21572     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21573 };
21574
21575 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21576     
21577     
21578     container_method : 'getPopoverHeader' 
21579     
21580      
21581     
21582     
21583    
21584 });
21585
21586  
21587
21588  /*
21589  * - LGPL
21590  *
21591  * Progress
21592  * 
21593  */
21594
21595 /**
21596  * @class Roo.bootstrap.Progress
21597  * @extends Roo.bootstrap.Component
21598  * Bootstrap Progress class
21599  * @cfg {Boolean} striped striped of the progress bar
21600  * @cfg {Boolean} active animated of the progress bar
21601  * 
21602  * 
21603  * @constructor
21604  * Create a new Progress
21605  * @param {Object} config The config object
21606  */
21607
21608 Roo.bootstrap.Progress = function(config){
21609     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21610 };
21611
21612 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21613     
21614     striped : false,
21615     active: false,
21616     
21617     getAutoCreate : function(){
21618         var cfg = {
21619             tag: 'div',
21620             cls: 'progress'
21621         };
21622         
21623         
21624         if(this.striped){
21625             cfg.cls += ' progress-striped';
21626         }
21627       
21628         if(this.active){
21629             cfg.cls += ' active';
21630         }
21631         
21632         
21633         return cfg;
21634     }
21635    
21636 });
21637
21638  
21639
21640  /*
21641  * - LGPL
21642  *
21643  * ProgressBar
21644  * 
21645  */
21646
21647 /**
21648  * @class Roo.bootstrap.ProgressBar
21649  * @extends Roo.bootstrap.Component
21650  * Bootstrap ProgressBar class
21651  * @cfg {Number} aria_valuenow aria-value now
21652  * @cfg {Number} aria_valuemin aria-value min
21653  * @cfg {Number} aria_valuemax aria-value max
21654  * @cfg {String} label label for the progress bar
21655  * @cfg {String} panel (success | info | warning | danger )
21656  * @cfg {String} role role of the progress bar
21657  * @cfg {String} sr_only text
21658  * 
21659  * 
21660  * @constructor
21661  * Create a new ProgressBar
21662  * @param {Object} config The config object
21663  */
21664
21665 Roo.bootstrap.ProgressBar = function(config){
21666     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21667 };
21668
21669 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21670     
21671     aria_valuenow : 0,
21672     aria_valuemin : 0,
21673     aria_valuemax : 100,
21674     label : false,
21675     panel : false,
21676     role : false,
21677     sr_only: false,
21678     
21679     getAutoCreate : function()
21680     {
21681         
21682         var cfg = {
21683             tag: 'div',
21684             cls: 'progress-bar',
21685             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21686         };
21687         
21688         if(this.sr_only){
21689             cfg.cn = {
21690                 tag: 'span',
21691                 cls: 'sr-only',
21692                 html: this.sr_only
21693             }
21694         }
21695         
21696         if(this.role){
21697             cfg.role = this.role;
21698         }
21699         
21700         if(this.aria_valuenow){
21701             cfg['aria-valuenow'] = this.aria_valuenow;
21702         }
21703         
21704         if(this.aria_valuemin){
21705             cfg['aria-valuemin'] = this.aria_valuemin;
21706         }
21707         
21708         if(this.aria_valuemax){
21709             cfg['aria-valuemax'] = this.aria_valuemax;
21710         }
21711         
21712         if(this.label && !this.sr_only){
21713             cfg.html = this.label;
21714         }
21715         
21716         if(this.panel){
21717             cfg.cls += ' progress-bar-' + this.panel;
21718         }
21719         
21720         return cfg;
21721     },
21722     
21723     update : function(aria_valuenow)
21724     {
21725         this.aria_valuenow = aria_valuenow;
21726         
21727         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21728     }
21729    
21730 });
21731
21732  
21733
21734  /*
21735  * - LGPL
21736  *
21737  * column
21738  * 
21739  */
21740
21741 /**
21742  * @class Roo.bootstrap.TabGroup
21743  * @extends Roo.bootstrap.Column
21744  * Bootstrap Column class
21745  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21746  * @cfg {Boolean} carousel true to make the group behave like a carousel
21747  * @cfg {Boolean} bullets show bullets for the panels
21748  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21749  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21750  * @cfg {Boolean} showarrow (true|false) show arrow default true
21751  * 
21752  * @constructor
21753  * Create a new TabGroup
21754  * @param {Object} config The config object
21755  */
21756
21757 Roo.bootstrap.TabGroup = function(config){
21758     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21759     if (!this.navId) {
21760         this.navId = Roo.id();
21761     }
21762     this.tabs = [];
21763     Roo.bootstrap.TabGroup.register(this);
21764     
21765 };
21766
21767 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21768     
21769     carousel : false,
21770     transition : false,
21771     bullets : 0,
21772     timer : 0,
21773     autoslide : false,
21774     slideFn : false,
21775     slideOnTouch : false,
21776     showarrow : true,
21777     
21778     getAutoCreate : function()
21779     {
21780         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21781         
21782         cfg.cls += ' tab-content';
21783         
21784         if (this.carousel) {
21785             cfg.cls += ' carousel slide';
21786             
21787             cfg.cn = [{
21788                cls : 'carousel-inner',
21789                cn : []
21790             }];
21791         
21792             if(this.bullets  && !Roo.isTouch){
21793                 
21794                 var bullets = {
21795                     cls : 'carousel-bullets',
21796                     cn : []
21797                 };
21798                
21799                 if(this.bullets_cls){
21800                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21801                 }
21802                 
21803                 bullets.cn.push({
21804                     cls : 'clear'
21805                 });
21806                 
21807                 cfg.cn[0].cn.push(bullets);
21808             }
21809             
21810             if(this.showarrow){
21811                 cfg.cn[0].cn.push({
21812                     tag : 'div',
21813                     class : 'carousel-arrow',
21814                     cn : [
21815                         {
21816                             tag : 'div',
21817                             class : 'carousel-prev',
21818                             cn : [
21819                                 {
21820                                     tag : 'i',
21821                                     class : 'fa fa-chevron-left'
21822                                 }
21823                             ]
21824                         },
21825                         {
21826                             tag : 'div',
21827                             class : 'carousel-next',
21828                             cn : [
21829                                 {
21830                                     tag : 'i',
21831                                     class : 'fa fa-chevron-right'
21832                                 }
21833                             ]
21834                         }
21835                     ]
21836                 });
21837             }
21838             
21839         }
21840         
21841         return cfg;
21842     },
21843     
21844     initEvents:  function()
21845     {
21846 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21847 //            this.el.on("touchstart", this.onTouchStart, this);
21848 //        }
21849         
21850         if(this.autoslide){
21851             var _this = this;
21852             
21853             this.slideFn = window.setInterval(function() {
21854                 _this.showPanelNext();
21855             }, this.timer);
21856         }
21857         
21858         if(this.showarrow){
21859             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21860             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21861         }
21862         
21863         
21864     },
21865     
21866 //    onTouchStart : function(e, el, o)
21867 //    {
21868 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21869 //            return;
21870 //        }
21871 //        
21872 //        this.showPanelNext();
21873 //    },
21874     
21875     
21876     getChildContainer : function()
21877     {
21878         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21879     },
21880     
21881     /**
21882     * register a Navigation item
21883     * @param {Roo.bootstrap.NavItem} the navitem to add
21884     */
21885     register : function(item)
21886     {
21887         this.tabs.push( item);
21888         item.navId = this.navId; // not really needed..
21889         this.addBullet();
21890     
21891     },
21892     
21893     getActivePanel : function()
21894     {
21895         var r = false;
21896         Roo.each(this.tabs, function(t) {
21897             if (t.active) {
21898                 r = t;
21899                 return false;
21900             }
21901             return null;
21902         });
21903         return r;
21904         
21905     },
21906     getPanelByName : function(n)
21907     {
21908         var r = false;
21909         Roo.each(this.tabs, function(t) {
21910             if (t.tabId == n) {
21911                 r = t;
21912                 return false;
21913             }
21914             return null;
21915         });
21916         return r;
21917     },
21918     indexOfPanel : function(p)
21919     {
21920         var r = false;
21921         Roo.each(this.tabs, function(t,i) {
21922             if (t.tabId == p.tabId) {
21923                 r = i;
21924                 return false;
21925             }
21926             return null;
21927         });
21928         return r;
21929     },
21930     /**
21931      * show a specific panel
21932      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21933      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21934      */
21935     showPanel : function (pan)
21936     {
21937         if(this.transition || typeof(pan) == 'undefined'){
21938             Roo.log("waiting for the transitionend");
21939             return false;
21940         }
21941         
21942         if (typeof(pan) == 'number') {
21943             pan = this.tabs[pan];
21944         }
21945         
21946         if (typeof(pan) == 'string') {
21947             pan = this.getPanelByName(pan);
21948         }
21949         
21950         var cur = this.getActivePanel();
21951         
21952         if(!pan || !cur){
21953             Roo.log('pan or acitve pan is undefined');
21954             return false;
21955         }
21956         
21957         if (pan.tabId == this.getActivePanel().tabId) {
21958             return true;
21959         }
21960         
21961         if (false === cur.fireEvent('beforedeactivate')) {
21962             return false;
21963         }
21964         
21965         if(this.bullets > 0 && !Roo.isTouch){
21966             this.setActiveBullet(this.indexOfPanel(pan));
21967         }
21968         
21969         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21970             
21971             //class="carousel-item carousel-item-next carousel-item-left"
21972             
21973             this.transition = true;
21974             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21975             var lr = dir == 'next' ? 'left' : 'right';
21976             pan.el.addClass(dir); // or prev
21977             pan.el.addClass('carousel-item-' + dir); // or prev
21978             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21979             cur.el.addClass(lr); // or right
21980             pan.el.addClass(lr);
21981             cur.el.addClass('carousel-item-' +lr); // or right
21982             pan.el.addClass('carousel-item-' +lr);
21983             
21984             
21985             var _this = this;
21986             cur.el.on('transitionend', function() {
21987                 Roo.log("trans end?");
21988                 
21989                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21990                 pan.setActive(true);
21991                 
21992                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21993                 cur.setActive(false);
21994                 
21995                 _this.transition = false;
21996                 
21997             }, this, { single:  true } );
21998             
21999             return true;
22000         }
22001         
22002         cur.setActive(false);
22003         pan.setActive(true);
22004         
22005         return true;
22006         
22007     },
22008     showPanelNext : function()
22009     {
22010         var i = this.indexOfPanel(this.getActivePanel());
22011         
22012         if (i >= this.tabs.length - 1 && !this.autoslide) {
22013             return;
22014         }
22015         
22016         if (i >= this.tabs.length - 1 && this.autoslide) {
22017             i = -1;
22018         }
22019         
22020         this.showPanel(this.tabs[i+1]);
22021     },
22022     
22023     showPanelPrev : function()
22024     {
22025         var i = this.indexOfPanel(this.getActivePanel());
22026         
22027         if (i  < 1 && !this.autoslide) {
22028             return;
22029         }
22030         
22031         if (i < 1 && this.autoslide) {
22032             i = this.tabs.length;
22033         }
22034         
22035         this.showPanel(this.tabs[i-1]);
22036     },
22037     
22038     
22039     addBullet: function()
22040     {
22041         if(!this.bullets || Roo.isTouch){
22042             return;
22043         }
22044         var ctr = this.el.select('.carousel-bullets',true).first();
22045         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22046         var bullet = ctr.createChild({
22047             cls : 'bullet bullet-' + i
22048         },ctr.dom.lastChild);
22049         
22050         
22051         var _this = this;
22052         
22053         bullet.on('click', (function(e, el, o, ii, t){
22054
22055             e.preventDefault();
22056
22057             this.showPanel(ii);
22058
22059             if(this.autoslide && this.slideFn){
22060                 clearInterval(this.slideFn);
22061                 this.slideFn = window.setInterval(function() {
22062                     _this.showPanelNext();
22063                 }, this.timer);
22064             }
22065
22066         }).createDelegate(this, [i, bullet], true));
22067                 
22068         
22069     },
22070      
22071     setActiveBullet : function(i)
22072     {
22073         if(Roo.isTouch){
22074             return;
22075         }
22076         
22077         Roo.each(this.el.select('.bullet', true).elements, function(el){
22078             el.removeClass('selected');
22079         });
22080
22081         var bullet = this.el.select('.bullet-' + i, true).first();
22082         
22083         if(!bullet){
22084             return;
22085         }
22086         
22087         bullet.addClass('selected');
22088     }
22089     
22090     
22091   
22092 });
22093
22094  
22095
22096  
22097  
22098 Roo.apply(Roo.bootstrap.TabGroup, {
22099     
22100     groups: {},
22101      /**
22102     * register a Navigation Group
22103     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22104     */
22105     register : function(navgrp)
22106     {
22107         this.groups[navgrp.navId] = navgrp;
22108         
22109     },
22110     /**
22111     * fetch a Navigation Group based on the navigation ID
22112     * if one does not exist , it will get created.
22113     * @param {string} the navgroup to add
22114     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22115     */
22116     get: function(navId) {
22117         if (typeof(this.groups[navId]) == 'undefined') {
22118             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22119         }
22120         return this.groups[navId] ;
22121     }
22122     
22123     
22124     
22125 });
22126
22127  /*
22128  * - LGPL
22129  *
22130  * TabPanel
22131  * 
22132  */
22133
22134 /**
22135  * @class Roo.bootstrap.TabPanel
22136  * @extends Roo.bootstrap.Component
22137  * @children Roo.bootstrap.Component
22138  * Bootstrap TabPanel class
22139  * @cfg {Boolean} active panel active
22140  * @cfg {String} html panel content
22141  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22142  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22143  * @cfg {String} href click to link..
22144  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22145  * 
22146  * 
22147  * @constructor
22148  * Create a new TabPanel
22149  * @param {Object} config The config object
22150  */
22151
22152 Roo.bootstrap.TabPanel = function(config){
22153     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22154     this.addEvents({
22155         /**
22156              * @event changed
22157              * Fires when the active status changes
22158              * @param {Roo.bootstrap.TabPanel} this
22159              * @param {Boolean} state the new state
22160             
22161          */
22162         'changed': true,
22163         /**
22164              * @event beforedeactivate
22165              * Fires before a tab is de-activated - can be used to do validation on a form.
22166              * @param {Roo.bootstrap.TabPanel} this
22167              * @return {Boolean} false if there is an error
22168             
22169          */
22170         'beforedeactivate': true
22171      });
22172     
22173     this.tabId = this.tabId || Roo.id();
22174   
22175 };
22176
22177 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22178     
22179     active: false,
22180     html: false,
22181     tabId: false,
22182     navId : false,
22183     href : '',
22184     touchSlide : false,
22185     getAutoCreate : function(){
22186         
22187         
22188         var cfg = {
22189             tag: 'div',
22190             // item is needed for carousel - not sure if it has any effect otherwise
22191             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22192             html: this.html || ''
22193         };
22194         
22195         if(this.active){
22196             cfg.cls += ' active';
22197         }
22198         
22199         if(this.tabId){
22200             cfg.tabId = this.tabId;
22201         }
22202         
22203         
22204         
22205         return cfg;
22206     },
22207     
22208     initEvents:  function()
22209     {
22210         var p = this.parent();
22211         
22212         this.navId = this.navId || p.navId;
22213         
22214         if (typeof(this.navId) != 'undefined') {
22215             // not really needed.. but just in case.. parent should be a NavGroup.
22216             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22217             
22218             tg.register(this);
22219             
22220             var i = tg.tabs.length - 1;
22221             
22222             if(this.active && tg.bullets > 0 && i < tg.bullets){
22223                 tg.setActiveBullet(i);
22224             }
22225         }
22226         
22227         this.el.on('click', this.onClick, this);
22228         
22229         if(Roo.isTouch && this.touchSlide){
22230             this.el.on("touchstart", this.onTouchStart, this);
22231             this.el.on("touchmove", this.onTouchMove, this);
22232             this.el.on("touchend", this.onTouchEnd, this);
22233         }
22234         
22235     },
22236     
22237     onRender : function(ct, position)
22238     {
22239         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22240     },
22241     
22242     setActive : function(state)
22243     {
22244         Roo.log("panel - set active " + this.tabId + "=" + state);
22245         
22246         this.active = state;
22247         if (!state) {
22248             this.el.removeClass('active');
22249             
22250         } else  if (!this.el.hasClass('active')) {
22251             this.el.addClass('active');
22252         }
22253         
22254         this.fireEvent('changed', this, state);
22255     },
22256     
22257     onClick : function(e)
22258     {
22259         e.preventDefault();
22260         
22261         if(!this.href.length){
22262             return;
22263         }
22264         
22265         window.location.href = this.href;
22266     },
22267     
22268     startX : 0,
22269     startY : 0,
22270     endX : 0,
22271     endY : 0,
22272     swiping : false,
22273     
22274     onTouchStart : function(e)
22275     {
22276         this.swiping = false;
22277         
22278         this.startX = e.browserEvent.touches[0].clientX;
22279         this.startY = e.browserEvent.touches[0].clientY;
22280     },
22281     
22282     onTouchMove : function(e)
22283     {
22284         this.swiping = true;
22285         
22286         this.endX = e.browserEvent.touches[0].clientX;
22287         this.endY = e.browserEvent.touches[0].clientY;
22288     },
22289     
22290     onTouchEnd : function(e)
22291     {
22292         if(!this.swiping){
22293             this.onClick(e);
22294             return;
22295         }
22296         
22297         var tabGroup = this.parent();
22298         
22299         if(this.endX > this.startX){ // swiping right
22300             tabGroup.showPanelPrev();
22301             return;
22302         }
22303         
22304         if(this.startX > this.endX){ // swiping left
22305             tabGroup.showPanelNext();
22306             return;
22307         }
22308     }
22309     
22310     
22311 });
22312  
22313
22314  
22315
22316  /*
22317  * - LGPL
22318  *
22319  * DateField
22320  * 
22321  */
22322
22323 /**
22324  * @class Roo.bootstrap.DateField
22325  * @extends Roo.bootstrap.Input
22326  * Bootstrap DateField class
22327  * @cfg {Number} weekStart default 0
22328  * @cfg {String} viewMode default empty, (months|years)
22329  * @cfg {String} minViewMode default empty, (months|years)
22330  * @cfg {Number} startDate default -Infinity
22331  * @cfg {Number} endDate default Infinity
22332  * @cfg {Boolean} todayHighlight default false
22333  * @cfg {Boolean} todayBtn default false
22334  * @cfg {Boolean} calendarWeeks default false
22335  * @cfg {Object} daysOfWeekDisabled default empty
22336  * @cfg {Boolean} singleMode default false (true | false)
22337  * 
22338  * @cfg {Boolean} keyboardNavigation default true
22339  * @cfg {String} language default en
22340  * 
22341  * @constructor
22342  * Create a new DateField
22343  * @param {Object} config The config object
22344  */
22345
22346 Roo.bootstrap.DateField = function(config){
22347     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22348      this.addEvents({
22349             /**
22350              * @event show
22351              * Fires when this field show.
22352              * @param {Roo.bootstrap.DateField} this
22353              * @param {Mixed} date The date value
22354              */
22355             show : true,
22356             /**
22357              * @event show
22358              * Fires when this field hide.
22359              * @param {Roo.bootstrap.DateField} this
22360              * @param {Mixed} date The date value
22361              */
22362             hide : true,
22363             /**
22364              * @event select
22365              * Fires when select a date.
22366              * @param {Roo.bootstrap.DateField} this
22367              * @param {Mixed} date The date value
22368              */
22369             select : true,
22370             /**
22371              * @event beforeselect
22372              * Fires when before select a date.
22373              * @param {Roo.bootstrap.DateField} this
22374              * @param {Mixed} date The date value
22375              */
22376             beforeselect : true
22377         });
22378 };
22379
22380 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22381     
22382     /**
22383      * @cfg {String} format
22384      * The default date format string which can be overriden for localization support.  The format must be
22385      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22386      */
22387     format : "m/d/y",
22388     /**
22389      * @cfg {String} altFormats
22390      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22391      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22392      */
22393     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22394     
22395     weekStart : 0,
22396     
22397     viewMode : '',
22398     
22399     minViewMode : '',
22400     
22401     todayHighlight : false,
22402     
22403     todayBtn: false,
22404     
22405     language: 'en',
22406     
22407     keyboardNavigation: true,
22408     
22409     calendarWeeks: false,
22410     
22411     startDate: -Infinity,
22412     
22413     endDate: Infinity,
22414     
22415     daysOfWeekDisabled: [],
22416     
22417     _events: [],
22418     
22419     singleMode : false,
22420     
22421     UTCDate: function()
22422     {
22423         return new Date(Date.UTC.apply(Date, arguments));
22424     },
22425     
22426     UTCToday: function()
22427     {
22428         var today = new Date();
22429         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22430     },
22431     
22432     getDate: function() {
22433             var d = this.getUTCDate();
22434             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22435     },
22436     
22437     getUTCDate: function() {
22438             return this.date;
22439     },
22440     
22441     setDate: function(d) {
22442             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22443     },
22444     
22445     setUTCDate: function(d) {
22446             this.date = d;
22447             this.setValue(this.formatDate(this.date));
22448     },
22449         
22450     onRender: function(ct, position)
22451     {
22452         
22453         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22454         
22455         this.language = this.language || 'en';
22456         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22457         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22458         
22459         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22460         this.format = this.format || 'm/d/y';
22461         this.isInline = false;
22462         this.isInput = true;
22463         this.component = this.el.select('.add-on', true).first() || false;
22464         this.component = (this.component && this.component.length === 0) ? false : this.component;
22465         this.hasInput = this.component && this.inputEl().length;
22466         
22467         if (typeof(this.minViewMode === 'string')) {
22468             switch (this.minViewMode) {
22469                 case 'months':
22470                     this.minViewMode = 1;
22471                     break;
22472                 case 'years':
22473                     this.minViewMode = 2;
22474                     break;
22475                 default:
22476                     this.minViewMode = 0;
22477                     break;
22478             }
22479         }
22480         
22481         if (typeof(this.viewMode === 'string')) {
22482             switch (this.viewMode) {
22483                 case 'months':
22484                     this.viewMode = 1;
22485                     break;
22486                 case 'years':
22487                     this.viewMode = 2;
22488                     break;
22489                 default:
22490                     this.viewMode = 0;
22491                     break;
22492             }
22493         }
22494                 
22495         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22496         
22497 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22498         
22499         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22500         
22501         this.picker().on('mousedown', this.onMousedown, this);
22502         this.picker().on('click', this.onClick, this);
22503         
22504         this.picker().addClass('datepicker-dropdown');
22505         
22506         this.startViewMode = this.viewMode;
22507         
22508         if(this.singleMode){
22509             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22510                 v.setVisibilityMode(Roo.Element.DISPLAY);
22511                 v.hide();
22512             });
22513             
22514             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22515                 v.setStyle('width', '189px');
22516             });
22517         }
22518         
22519         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22520             if(!this.calendarWeeks){
22521                 v.remove();
22522                 return;
22523             }
22524             
22525             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22526             v.attr('colspan', function(i, val){
22527                 return parseInt(val) + 1;
22528             });
22529         });
22530                         
22531         
22532         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22533         
22534         this.setStartDate(this.startDate);
22535         this.setEndDate(this.endDate);
22536         
22537         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22538         
22539         this.fillDow();
22540         this.fillMonths();
22541         this.update();
22542         this.showMode();
22543         
22544         if(this.isInline) {
22545             this.showPopup();
22546         }
22547     },
22548     
22549     picker : function()
22550     {
22551         return this.pickerEl;
22552 //        return this.el.select('.datepicker', true).first();
22553     },
22554     
22555     fillDow: function()
22556     {
22557         var dowCnt = this.weekStart;
22558         
22559         var dow = {
22560             tag: 'tr',
22561             cn: [
22562                 
22563             ]
22564         };
22565         
22566         if(this.calendarWeeks){
22567             dow.cn.push({
22568                 tag: 'th',
22569                 cls: 'cw',
22570                 html: '&nbsp;'
22571             })
22572         }
22573         
22574         while (dowCnt < this.weekStart + 7) {
22575             dow.cn.push({
22576                 tag: 'th',
22577                 cls: 'dow',
22578                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22579             });
22580         }
22581         
22582         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22583     },
22584     
22585     fillMonths: function()
22586     {    
22587         var i = 0;
22588         var months = this.picker().select('>.datepicker-months td', true).first();
22589         
22590         months.dom.innerHTML = '';
22591         
22592         while (i < 12) {
22593             var month = {
22594                 tag: 'span',
22595                 cls: 'month',
22596                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22597             };
22598             
22599             months.createChild(month);
22600         }
22601         
22602     },
22603     
22604     update: function()
22605     {
22606         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;
22607         
22608         if (this.date < this.startDate) {
22609             this.viewDate = new Date(this.startDate);
22610         } else if (this.date > this.endDate) {
22611             this.viewDate = new Date(this.endDate);
22612         } else {
22613             this.viewDate = new Date(this.date);
22614         }
22615         
22616         this.fill();
22617     },
22618     
22619     fill: function() 
22620     {
22621         var d = new Date(this.viewDate),
22622                 year = d.getUTCFullYear(),
22623                 month = d.getUTCMonth(),
22624                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22625                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22626                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22627                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22628                 currentDate = this.date && this.date.valueOf(),
22629                 today = this.UTCToday();
22630         
22631         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22632         
22633 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22634         
22635 //        this.picker.select('>tfoot th.today').
22636 //                                              .text(dates[this.language].today)
22637 //                                              .toggle(this.todayBtn !== false);
22638     
22639         this.updateNavArrows();
22640         this.fillMonths();
22641                                                 
22642         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22643         
22644         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22645          
22646         prevMonth.setUTCDate(day);
22647         
22648         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22649         
22650         var nextMonth = new Date(prevMonth);
22651         
22652         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22653         
22654         nextMonth = nextMonth.valueOf();
22655         
22656         var fillMonths = false;
22657         
22658         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22659         
22660         while(prevMonth.valueOf() <= nextMonth) {
22661             var clsName = '';
22662             
22663             if (prevMonth.getUTCDay() === this.weekStart) {
22664                 if(fillMonths){
22665                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22666                 }
22667                     
22668                 fillMonths = {
22669                     tag: 'tr',
22670                     cn: []
22671                 };
22672                 
22673                 if(this.calendarWeeks){
22674                     // ISO 8601: First week contains first thursday.
22675                     // ISO also states week starts on Monday, but we can be more abstract here.
22676                     var
22677                     // Start of current week: based on weekstart/current date
22678                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22679                     // Thursday of this week
22680                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22681                     // First Thursday of year, year from thursday
22682                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22683                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22684                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22685                     
22686                     fillMonths.cn.push({
22687                         tag: 'td',
22688                         cls: 'cw',
22689                         html: calWeek
22690                     });
22691                 }
22692             }
22693             
22694             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22695                 clsName += ' old';
22696             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22697                 clsName += ' new';
22698             }
22699             if (this.todayHighlight &&
22700                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22701                 prevMonth.getUTCMonth() == today.getMonth() &&
22702                 prevMonth.getUTCDate() == today.getDate()) {
22703                 clsName += ' today';
22704             }
22705             
22706             if (currentDate && prevMonth.valueOf() === currentDate) {
22707                 clsName += ' active';
22708             }
22709             
22710             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22711                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22712                     clsName += ' disabled';
22713             }
22714             
22715             fillMonths.cn.push({
22716                 tag: 'td',
22717                 cls: 'day ' + clsName,
22718                 html: prevMonth.getDate()
22719             });
22720             
22721             prevMonth.setDate(prevMonth.getDate()+1);
22722         }
22723           
22724         var currentYear = this.date && this.date.getUTCFullYear();
22725         var currentMonth = this.date && this.date.getUTCMonth();
22726         
22727         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22728         
22729         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22730             v.removeClass('active');
22731             
22732             if(currentYear === year && k === currentMonth){
22733                 v.addClass('active');
22734             }
22735             
22736             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22737                 v.addClass('disabled');
22738             }
22739             
22740         });
22741         
22742         
22743         year = parseInt(year/10, 10) * 10;
22744         
22745         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22746         
22747         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22748         
22749         year -= 1;
22750         for (var i = -1; i < 11; i++) {
22751             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22752                 tag: 'span',
22753                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22754                 html: year
22755             });
22756             
22757             year += 1;
22758         }
22759     },
22760     
22761     showMode: function(dir) 
22762     {
22763         if (dir) {
22764             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22765         }
22766         
22767         Roo.each(this.picker().select('>div',true).elements, function(v){
22768             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22769             v.hide();
22770         });
22771         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22772     },
22773     
22774     place: function()
22775     {
22776         if(this.isInline) {
22777             return;
22778         }
22779         
22780         this.picker().removeClass(['bottom', 'top']);
22781         
22782         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22783             /*
22784              * place to the top of element!
22785              *
22786              */
22787             
22788             this.picker().addClass('top');
22789             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22790             
22791             return;
22792         }
22793         
22794         this.picker().addClass('bottom');
22795         
22796         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22797     },
22798     
22799     parseDate : function(value)
22800     {
22801         if(!value || value instanceof Date){
22802             return value;
22803         }
22804         var v = Date.parseDate(value, this.format);
22805         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22806             v = Date.parseDate(value, 'Y-m-d');
22807         }
22808         if(!v && this.altFormats){
22809             if(!this.altFormatsArray){
22810                 this.altFormatsArray = this.altFormats.split("|");
22811             }
22812             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22813                 v = Date.parseDate(value, this.altFormatsArray[i]);
22814             }
22815         }
22816         return v;
22817     },
22818     
22819     formatDate : function(date, fmt)
22820     {   
22821         return (!date || !(date instanceof Date)) ?
22822         date : date.dateFormat(fmt || this.format);
22823     },
22824     
22825     onFocus : function()
22826     {
22827         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22828         this.showPopup();
22829     },
22830     
22831     onBlur : function()
22832     {
22833         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22834         
22835         var d = this.inputEl().getValue();
22836         
22837         this.setValue(d);
22838                 
22839         this.hidePopup();
22840     },
22841     
22842     showPopup : function()
22843     {
22844         this.picker().show();
22845         this.update();
22846         this.place();
22847         
22848         this.fireEvent('showpopup', this, this.date);
22849     },
22850     
22851     hidePopup : function()
22852     {
22853         if(this.isInline) {
22854             return;
22855         }
22856         this.picker().hide();
22857         this.viewMode = this.startViewMode;
22858         this.showMode();
22859         
22860         this.fireEvent('hidepopup', this, this.date);
22861         
22862     },
22863     
22864     onMousedown: function(e)
22865     {
22866         e.stopPropagation();
22867         e.preventDefault();
22868     },
22869     
22870     keyup: function(e)
22871     {
22872         Roo.bootstrap.DateField.superclass.keyup.call(this);
22873         this.update();
22874     },
22875
22876     setValue: function(v)
22877     {
22878         if(this.fireEvent('beforeselect', this, v) !== false){
22879             var d = new Date(this.parseDate(v) ).clearTime();
22880         
22881             if(isNaN(d.getTime())){
22882                 this.date = this.viewDate = '';
22883                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22884                 return;
22885             }
22886
22887             v = this.formatDate(d);
22888
22889             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22890
22891             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22892
22893             this.update();
22894
22895             this.fireEvent('select', this, this.date);
22896         }
22897     },
22898     
22899     getValue: function()
22900     {
22901         return this.formatDate(this.date);
22902     },
22903     
22904     fireKey: function(e)
22905     {
22906         if (!this.picker().isVisible()){
22907             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22908                 this.showPopup();
22909             }
22910             return;
22911         }
22912         
22913         var dateChanged = false,
22914         dir, day, month,
22915         newDate, newViewDate;
22916         
22917         switch(e.keyCode){
22918             case 27: // escape
22919                 this.hidePopup();
22920                 e.preventDefault();
22921                 break;
22922             case 37: // left
22923             case 39: // right
22924                 if (!this.keyboardNavigation) {
22925                     break;
22926                 }
22927                 dir = e.keyCode == 37 ? -1 : 1;
22928                 
22929                 if (e.ctrlKey){
22930                     newDate = this.moveYear(this.date, dir);
22931                     newViewDate = this.moveYear(this.viewDate, dir);
22932                 } else if (e.shiftKey){
22933                     newDate = this.moveMonth(this.date, dir);
22934                     newViewDate = this.moveMonth(this.viewDate, dir);
22935                 } else {
22936                     newDate = new Date(this.date);
22937                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22938                     newViewDate = new Date(this.viewDate);
22939                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22940                 }
22941                 if (this.dateWithinRange(newDate)){
22942                     this.date = newDate;
22943                     this.viewDate = newViewDate;
22944                     this.setValue(this.formatDate(this.date));
22945 //                    this.update();
22946                     e.preventDefault();
22947                     dateChanged = true;
22948                 }
22949                 break;
22950             case 38: // up
22951             case 40: // down
22952                 if (!this.keyboardNavigation) {
22953                     break;
22954                 }
22955                 dir = e.keyCode == 38 ? -1 : 1;
22956                 if (e.ctrlKey){
22957                     newDate = this.moveYear(this.date, dir);
22958                     newViewDate = this.moveYear(this.viewDate, dir);
22959                 } else if (e.shiftKey){
22960                     newDate = this.moveMonth(this.date, dir);
22961                     newViewDate = this.moveMonth(this.viewDate, dir);
22962                 } else {
22963                     newDate = new Date(this.date);
22964                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22965                     newViewDate = new Date(this.viewDate);
22966                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22967                 }
22968                 if (this.dateWithinRange(newDate)){
22969                     this.date = newDate;
22970                     this.viewDate = newViewDate;
22971                     this.setValue(this.formatDate(this.date));
22972 //                    this.update();
22973                     e.preventDefault();
22974                     dateChanged = true;
22975                 }
22976                 break;
22977             case 13: // enter
22978                 this.setValue(this.formatDate(this.date));
22979                 this.hidePopup();
22980                 e.preventDefault();
22981                 break;
22982             case 9: // tab
22983                 this.setValue(this.formatDate(this.date));
22984                 this.hidePopup();
22985                 break;
22986             case 16: // shift
22987             case 17: // ctrl
22988             case 18: // alt
22989                 break;
22990             default :
22991                 this.hidePopup();
22992                 
22993         }
22994     },
22995     
22996     
22997     onClick: function(e) 
22998     {
22999         e.stopPropagation();
23000         e.preventDefault();
23001         
23002         var target = e.getTarget();
23003         
23004         if(target.nodeName.toLowerCase() === 'i'){
23005             target = Roo.get(target).dom.parentNode;
23006         }
23007         
23008         var nodeName = target.nodeName;
23009         var className = target.className;
23010         var html = target.innerHTML;
23011         //Roo.log(nodeName);
23012         
23013         switch(nodeName.toLowerCase()) {
23014             case 'th':
23015                 switch(className) {
23016                     case 'switch':
23017                         this.showMode(1);
23018                         break;
23019                     case 'prev':
23020                     case 'next':
23021                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23022                         switch(this.viewMode){
23023                                 case 0:
23024                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23025                                         break;
23026                                 case 1:
23027                                 case 2:
23028                                         this.viewDate = this.moveYear(this.viewDate, dir);
23029                                         break;
23030                         }
23031                         this.fill();
23032                         break;
23033                     case 'today':
23034                         var date = new Date();
23035                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23036 //                        this.fill()
23037                         this.setValue(this.formatDate(this.date));
23038                         
23039                         this.hidePopup();
23040                         break;
23041                 }
23042                 break;
23043             case 'span':
23044                 if (className.indexOf('disabled') < 0) {
23045                 if (!this.viewDate) {
23046                     this.viewDate = new Date();
23047                 }
23048                 this.viewDate.setUTCDate(1);
23049                     if (className.indexOf('month') > -1) {
23050                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23051                     } else {
23052                         var year = parseInt(html, 10) || 0;
23053                         this.viewDate.setUTCFullYear(year);
23054                         
23055                     }
23056                     
23057                     if(this.singleMode){
23058                         this.setValue(this.formatDate(this.viewDate));
23059                         this.hidePopup();
23060                         return;
23061                     }
23062                     
23063                     this.showMode(-1);
23064                     this.fill();
23065                 }
23066                 break;
23067                 
23068             case 'td':
23069                 //Roo.log(className);
23070                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23071                     var day = parseInt(html, 10) || 1;
23072                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23073                         month = (this.viewDate || new Date()).getUTCMonth();
23074
23075                     if (className.indexOf('old') > -1) {
23076                         if(month === 0 ){
23077                             month = 11;
23078                             year -= 1;
23079                         }else{
23080                             month -= 1;
23081                         }
23082                     } else if (className.indexOf('new') > -1) {
23083                         if (month == 11) {
23084                             month = 0;
23085                             year += 1;
23086                         } else {
23087                             month += 1;
23088                         }
23089                     }
23090                     //Roo.log([year,month,day]);
23091                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23092                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23093 //                    this.fill();
23094                     //Roo.log(this.formatDate(this.date));
23095                     this.setValue(this.formatDate(this.date));
23096                     this.hidePopup();
23097                 }
23098                 break;
23099         }
23100     },
23101     
23102     setStartDate: function(startDate)
23103     {
23104         this.startDate = startDate || -Infinity;
23105         if (this.startDate !== -Infinity) {
23106             this.startDate = this.parseDate(this.startDate);
23107         }
23108         this.update();
23109         this.updateNavArrows();
23110     },
23111
23112     setEndDate: function(endDate)
23113     {
23114         this.endDate = endDate || Infinity;
23115         if (this.endDate !== Infinity) {
23116             this.endDate = this.parseDate(this.endDate);
23117         }
23118         this.update();
23119         this.updateNavArrows();
23120     },
23121     
23122     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23123     {
23124         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23125         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23126             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23127         }
23128         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23129             return parseInt(d, 10);
23130         });
23131         this.update();
23132         this.updateNavArrows();
23133     },
23134     
23135     updateNavArrows: function() 
23136     {
23137         if(this.singleMode){
23138             return;
23139         }
23140         
23141         var d = new Date(this.viewDate),
23142         year = d.getUTCFullYear(),
23143         month = d.getUTCMonth();
23144         
23145         Roo.each(this.picker().select('.prev', true).elements, function(v){
23146             v.show();
23147             switch (this.viewMode) {
23148                 case 0:
23149
23150                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23151                         v.hide();
23152                     }
23153                     break;
23154                 case 1:
23155                 case 2:
23156                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23157                         v.hide();
23158                     }
23159                     break;
23160             }
23161         });
23162         
23163         Roo.each(this.picker().select('.next', true).elements, function(v){
23164             v.show();
23165             switch (this.viewMode) {
23166                 case 0:
23167
23168                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23169                         v.hide();
23170                     }
23171                     break;
23172                 case 1:
23173                 case 2:
23174                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23175                         v.hide();
23176                     }
23177                     break;
23178             }
23179         })
23180     },
23181     
23182     moveMonth: function(date, dir)
23183     {
23184         if (!dir) {
23185             return date;
23186         }
23187         var new_date = new Date(date.valueOf()),
23188         day = new_date.getUTCDate(),
23189         month = new_date.getUTCMonth(),
23190         mag = Math.abs(dir),
23191         new_month, test;
23192         dir = dir > 0 ? 1 : -1;
23193         if (mag == 1){
23194             test = dir == -1
23195             // If going back one month, make sure month is not current month
23196             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23197             ? function(){
23198                 return new_date.getUTCMonth() == month;
23199             }
23200             // If going forward one month, make sure month is as expected
23201             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23202             : function(){
23203                 return new_date.getUTCMonth() != new_month;
23204             };
23205             new_month = month + dir;
23206             new_date.setUTCMonth(new_month);
23207             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23208             if (new_month < 0 || new_month > 11) {
23209                 new_month = (new_month + 12) % 12;
23210             }
23211         } else {
23212             // For magnitudes >1, move one month at a time...
23213             for (var i=0; i<mag; i++) {
23214                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23215                 new_date = this.moveMonth(new_date, dir);
23216             }
23217             // ...then reset the day, keeping it in the new month
23218             new_month = new_date.getUTCMonth();
23219             new_date.setUTCDate(day);
23220             test = function(){
23221                 return new_month != new_date.getUTCMonth();
23222             };
23223         }
23224         // Common date-resetting loop -- if date is beyond end of month, make it
23225         // end of month
23226         while (test()){
23227             new_date.setUTCDate(--day);
23228             new_date.setUTCMonth(new_month);
23229         }
23230         return new_date;
23231     },
23232
23233     moveYear: function(date, dir)
23234     {
23235         return this.moveMonth(date, dir*12);
23236     },
23237
23238     dateWithinRange: function(date)
23239     {
23240         return date >= this.startDate && date <= this.endDate;
23241     },
23242
23243     
23244     remove: function() 
23245     {
23246         this.picker().remove();
23247     },
23248     
23249     validateValue : function(value)
23250     {
23251         if(this.getVisibilityEl().hasClass('hidden')){
23252             return true;
23253         }
23254         
23255         if(value.length < 1)  {
23256             if(this.allowBlank){
23257                 return true;
23258             }
23259             return false;
23260         }
23261         
23262         if(value.length < this.minLength){
23263             return false;
23264         }
23265         if(value.length > this.maxLength){
23266             return false;
23267         }
23268         if(this.vtype){
23269             var vt = Roo.form.VTypes;
23270             if(!vt[this.vtype](value, this)){
23271                 return false;
23272             }
23273         }
23274         if(typeof this.validator == "function"){
23275             var msg = this.validator(value);
23276             if(msg !== true){
23277                 return false;
23278             }
23279         }
23280         
23281         if(this.regex && !this.regex.test(value)){
23282             return false;
23283         }
23284         
23285         if(typeof(this.parseDate(value)) == 'undefined'){
23286             return false;
23287         }
23288         
23289         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23290             return false;
23291         }      
23292         
23293         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23294             return false;
23295         } 
23296         
23297         
23298         return true;
23299     },
23300     
23301     reset : function()
23302     {
23303         this.date = this.viewDate = '';
23304         
23305         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23306     }
23307    
23308 });
23309
23310 Roo.apply(Roo.bootstrap.DateField,  {
23311     
23312     head : {
23313         tag: 'thead',
23314         cn: [
23315         {
23316             tag: 'tr',
23317             cn: [
23318             {
23319                 tag: 'th',
23320                 cls: 'prev',
23321                 html: '<i class="fa fa-arrow-left"/>'
23322             },
23323             {
23324                 tag: 'th',
23325                 cls: 'switch',
23326                 colspan: '5'
23327             },
23328             {
23329                 tag: 'th',
23330                 cls: 'next',
23331                 html: '<i class="fa fa-arrow-right"/>'
23332             }
23333
23334             ]
23335         }
23336         ]
23337     },
23338     
23339     content : {
23340         tag: 'tbody',
23341         cn: [
23342         {
23343             tag: 'tr',
23344             cn: [
23345             {
23346                 tag: 'td',
23347                 colspan: '7'
23348             }
23349             ]
23350         }
23351         ]
23352     },
23353     
23354     footer : {
23355         tag: 'tfoot',
23356         cn: [
23357         {
23358             tag: 'tr',
23359             cn: [
23360             {
23361                 tag: 'th',
23362                 colspan: '7',
23363                 cls: 'today'
23364             }
23365                     
23366             ]
23367         }
23368         ]
23369     },
23370     
23371     dates:{
23372         en: {
23373             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23374             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23375             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23376             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23377             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23378             today: "Today"
23379         }
23380     },
23381     
23382     modes: [
23383     {
23384         clsName: 'days',
23385         navFnc: 'Month',
23386         navStep: 1
23387     },
23388     {
23389         clsName: 'months',
23390         navFnc: 'FullYear',
23391         navStep: 1
23392     },
23393     {
23394         clsName: 'years',
23395         navFnc: 'FullYear',
23396         navStep: 10
23397     }]
23398 });
23399
23400 Roo.apply(Roo.bootstrap.DateField,  {
23401   
23402     template : {
23403         tag: 'div',
23404         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23405         cn: [
23406         {
23407             tag: 'div',
23408             cls: 'datepicker-days',
23409             cn: [
23410             {
23411                 tag: 'table',
23412                 cls: 'table-condensed',
23413                 cn:[
23414                 Roo.bootstrap.DateField.head,
23415                 {
23416                     tag: 'tbody'
23417                 },
23418                 Roo.bootstrap.DateField.footer
23419                 ]
23420             }
23421             ]
23422         },
23423         {
23424             tag: 'div',
23425             cls: 'datepicker-months',
23426             cn: [
23427             {
23428                 tag: 'table',
23429                 cls: 'table-condensed',
23430                 cn:[
23431                 Roo.bootstrap.DateField.head,
23432                 Roo.bootstrap.DateField.content,
23433                 Roo.bootstrap.DateField.footer
23434                 ]
23435             }
23436             ]
23437         },
23438         {
23439             tag: 'div',
23440             cls: 'datepicker-years',
23441             cn: [
23442             {
23443                 tag: 'table',
23444                 cls: 'table-condensed',
23445                 cn:[
23446                 Roo.bootstrap.DateField.head,
23447                 Roo.bootstrap.DateField.content,
23448                 Roo.bootstrap.DateField.footer
23449                 ]
23450             }
23451             ]
23452         }
23453         ]
23454     }
23455 });
23456
23457  
23458
23459  /*
23460  * - LGPL
23461  *
23462  * TimeField
23463  * 
23464  */
23465
23466 /**
23467  * @class Roo.bootstrap.TimeField
23468  * @extends Roo.bootstrap.Input
23469  * Bootstrap DateField class
23470  * 
23471  * 
23472  * @constructor
23473  * Create a new TimeField
23474  * @param {Object} config The config object
23475  */
23476
23477 Roo.bootstrap.TimeField = function(config){
23478     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23479     this.addEvents({
23480             /**
23481              * @event show
23482              * Fires when this field show.
23483              * @param {Roo.bootstrap.DateField} thisthis
23484              * @param {Mixed} date The date value
23485              */
23486             show : true,
23487             /**
23488              * @event show
23489              * Fires when this field hide.
23490              * @param {Roo.bootstrap.DateField} this
23491              * @param {Mixed} date The date value
23492              */
23493             hide : true,
23494             /**
23495              * @event select
23496              * Fires when select a date.
23497              * @param {Roo.bootstrap.DateField} this
23498              * @param {Mixed} date The date value
23499              */
23500             select : true
23501         });
23502 };
23503
23504 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23505     
23506     /**
23507      * @cfg {String} format
23508      * The default time format string which can be overriden for localization support.  The format must be
23509      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23510      */
23511     format : "H:i",
23512
23513     getAutoCreate : function()
23514     {
23515         this.after = '<i class="fa far fa-clock"></i>';
23516         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23517         
23518          
23519     },
23520     onRender: function(ct, position)
23521     {
23522         
23523         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23524                 
23525         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23526         
23527         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23528         
23529         this.pop = this.picker().select('>.datepicker-time',true).first();
23530         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23531         
23532         this.picker().on('mousedown', this.onMousedown, this);
23533         this.picker().on('click', this.onClick, this);
23534         
23535         this.picker().addClass('datepicker-dropdown');
23536     
23537         this.fillTime();
23538         this.update();
23539             
23540         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23541         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23542         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23543         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23544         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23545         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23546
23547     },
23548     
23549     fireKey: function(e){
23550         if (!this.picker().isVisible()){
23551             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23552                 this.show();
23553             }
23554             return;
23555         }
23556
23557         e.preventDefault();
23558         
23559         switch(e.keyCode){
23560             case 27: // escape
23561                 this.hide();
23562                 break;
23563             case 37: // left
23564             case 39: // right
23565                 this.onTogglePeriod();
23566                 break;
23567             case 38: // up
23568                 this.onIncrementMinutes();
23569                 break;
23570             case 40: // down
23571                 this.onDecrementMinutes();
23572                 break;
23573             case 13: // enter
23574             case 9: // tab
23575                 this.setTime();
23576                 break;
23577         }
23578     },
23579     
23580     onClick: function(e) {
23581         e.stopPropagation();
23582         e.preventDefault();
23583     },
23584     
23585     picker : function()
23586     {
23587         return this.pickerEl;
23588     },
23589     
23590     fillTime: function()
23591     {    
23592         var time = this.pop.select('tbody', true).first();
23593         
23594         time.dom.innerHTML = '';
23595         
23596         time.createChild({
23597             tag: 'tr',
23598             cn: [
23599                 {
23600                     tag: 'td',
23601                     cn: [
23602                         {
23603                             tag: 'a',
23604                             href: '#',
23605                             cls: 'btn',
23606                             cn: [
23607                                 {
23608                                     tag: 'i',
23609                                     cls: 'hours-up fa fas fa-chevron-up'
23610                                 }
23611                             ]
23612                         } 
23613                     ]
23614                 },
23615                 {
23616                     tag: 'td',
23617                     cls: 'separator'
23618                 },
23619                 {
23620                     tag: 'td',
23621                     cn: [
23622                         {
23623                             tag: 'a',
23624                             href: '#',
23625                             cls: 'btn',
23626                             cn: [
23627                                 {
23628                                     tag: 'i',
23629                                     cls: 'minutes-up fa fas fa-chevron-up'
23630                                 }
23631                             ]
23632                         }
23633                     ]
23634                 },
23635                 {
23636                     tag: 'td',
23637                     cls: 'separator'
23638                 }
23639             ]
23640         });
23641         
23642         time.createChild({
23643             tag: 'tr',
23644             cn: [
23645                 {
23646                     tag: 'td',
23647                     cn: [
23648                         {
23649                             tag: 'span',
23650                             cls: 'timepicker-hour',
23651                             html: '00'
23652                         }  
23653                     ]
23654                 },
23655                 {
23656                     tag: 'td',
23657                     cls: 'separator',
23658                     html: ':'
23659                 },
23660                 {
23661                     tag: 'td',
23662                     cn: [
23663                         {
23664                             tag: 'span',
23665                             cls: 'timepicker-minute',
23666                             html: '00'
23667                         }  
23668                     ]
23669                 },
23670                 {
23671                     tag: 'td',
23672                     cls: 'separator'
23673                 },
23674                 {
23675                     tag: 'td',
23676                     cn: [
23677                         {
23678                             tag: 'button',
23679                             type: 'button',
23680                             cls: 'btn btn-primary period',
23681                             html: 'AM'
23682                             
23683                         }
23684                     ]
23685                 }
23686             ]
23687         });
23688         
23689         time.createChild({
23690             tag: 'tr',
23691             cn: [
23692                 {
23693                     tag: 'td',
23694                     cn: [
23695                         {
23696                             tag: 'a',
23697                             href: '#',
23698                             cls: 'btn',
23699                             cn: [
23700                                 {
23701                                     tag: 'span',
23702                                     cls: 'hours-down fa fas fa-chevron-down'
23703                                 }
23704                             ]
23705                         }
23706                     ]
23707                 },
23708                 {
23709                     tag: 'td',
23710                     cls: 'separator'
23711                 },
23712                 {
23713                     tag: 'td',
23714                     cn: [
23715                         {
23716                             tag: 'a',
23717                             href: '#',
23718                             cls: 'btn',
23719                             cn: [
23720                                 {
23721                                     tag: 'span',
23722                                     cls: 'minutes-down fa fas fa-chevron-down'
23723                                 }
23724                             ]
23725                         }
23726                     ]
23727                 },
23728                 {
23729                     tag: 'td',
23730                     cls: 'separator'
23731                 }
23732             ]
23733         });
23734         
23735     },
23736     
23737     update: function()
23738     {
23739         
23740         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23741         
23742         this.fill();
23743     },
23744     
23745     fill: function() 
23746     {
23747         var hours = this.time.getHours();
23748         var minutes = this.time.getMinutes();
23749         var period = 'AM';
23750         
23751         if(hours > 11){
23752             period = 'PM';
23753         }
23754         
23755         if(hours == 0){
23756             hours = 12;
23757         }
23758         
23759         
23760         if(hours > 12){
23761             hours = hours - 12;
23762         }
23763         
23764         if(hours < 10){
23765             hours = '0' + hours;
23766         }
23767         
23768         if(minutes < 10){
23769             minutes = '0' + minutes;
23770         }
23771         
23772         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23773         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23774         this.pop.select('button', true).first().dom.innerHTML = period;
23775         
23776     },
23777     
23778     place: function()
23779     {   
23780         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23781         
23782         var cls = ['bottom'];
23783         
23784         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23785             cls.pop();
23786             cls.push('top');
23787         }
23788         
23789         cls.push('right');
23790         
23791         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23792             cls.pop();
23793             cls.push('left');
23794         }
23795         //this.picker().setXY(20000,20000);
23796         this.picker().addClass(cls.join('-'));
23797         
23798         var _this = this;
23799         
23800         Roo.each(cls, function(c){
23801             if(c == 'bottom'){
23802                 (function() {
23803                  //  
23804                 }).defer(200);
23805                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23806                 //_this.picker().setTop(_this.inputEl().getHeight());
23807                 return;
23808             }
23809             if(c == 'top'){
23810                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23811                 
23812                 //_this.picker().setTop(0 - _this.picker().getHeight());
23813                 return;
23814             }
23815             /*
23816             if(c == 'left'){
23817                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23818                 return;
23819             }
23820             if(c == 'right'){
23821                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23822                 return;
23823             }
23824             */
23825         });
23826         
23827     },
23828   
23829     onFocus : function()
23830     {
23831         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23832         this.show();
23833     },
23834     
23835     onBlur : function()
23836     {
23837         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23838         this.hide();
23839     },
23840     
23841     show : function()
23842     {
23843         this.picker().show();
23844         this.pop.show();
23845         this.update();
23846         this.place();
23847         
23848         this.fireEvent('show', this, this.date);
23849     },
23850     
23851     hide : function()
23852     {
23853         this.picker().hide();
23854         this.pop.hide();
23855         
23856         this.fireEvent('hide', this, this.date);
23857     },
23858     
23859     setTime : function()
23860     {
23861         this.hide();
23862         this.setValue(this.time.format(this.format));
23863         
23864         this.fireEvent('select', this, this.date);
23865         
23866         
23867     },
23868     
23869     onMousedown: function(e){
23870         e.stopPropagation();
23871         e.preventDefault();
23872     },
23873     
23874     onIncrementHours: function()
23875     {
23876         Roo.log('onIncrementHours');
23877         this.time = this.time.add(Date.HOUR, 1);
23878         this.update();
23879         
23880     },
23881     
23882     onDecrementHours: function()
23883     {
23884         Roo.log('onDecrementHours');
23885         this.time = this.time.add(Date.HOUR, -1);
23886         this.update();
23887     },
23888     
23889     onIncrementMinutes: function()
23890     {
23891         Roo.log('onIncrementMinutes');
23892         this.time = this.time.add(Date.MINUTE, 1);
23893         this.update();
23894     },
23895     
23896     onDecrementMinutes: function()
23897     {
23898         Roo.log('onDecrementMinutes');
23899         this.time = this.time.add(Date.MINUTE, -1);
23900         this.update();
23901     },
23902     
23903     onTogglePeriod: function()
23904     {
23905         Roo.log('onTogglePeriod');
23906         this.time = this.time.add(Date.HOUR, 12);
23907         this.update();
23908     }
23909     
23910    
23911 });
23912  
23913
23914 Roo.apply(Roo.bootstrap.TimeField,  {
23915   
23916     template : {
23917         tag: 'div',
23918         cls: 'datepicker dropdown-menu',
23919         cn: [
23920             {
23921                 tag: 'div',
23922                 cls: 'datepicker-time',
23923                 cn: [
23924                 {
23925                     tag: 'table',
23926                     cls: 'table-condensed',
23927                     cn:[
23928                         {
23929                             tag: 'tbody',
23930                             cn: [
23931                                 {
23932                                     tag: 'tr',
23933                                     cn: [
23934                                     {
23935                                         tag: 'td',
23936                                         colspan: '7'
23937                                     }
23938                                     ]
23939                                 }
23940                             ]
23941                         },
23942                         {
23943                             tag: 'tfoot',
23944                             cn: [
23945                                 {
23946                                     tag: 'tr',
23947                                     cn: [
23948                                     {
23949                                         tag: 'th',
23950                                         colspan: '7',
23951                                         cls: '',
23952                                         cn: [
23953                                             {
23954                                                 tag: 'button',
23955                                                 cls: 'btn btn-info ok',
23956                                                 html: 'OK'
23957                                             }
23958                                         ]
23959                                     }
23960                     
23961                                     ]
23962                                 }
23963                             ]
23964                         }
23965                     ]
23966                 }
23967                 ]
23968             }
23969         ]
23970     }
23971 });
23972
23973  
23974
23975  /*
23976  * - LGPL
23977  *
23978  * MonthField
23979  * 
23980  */
23981
23982 /**
23983  * @class Roo.bootstrap.MonthField
23984  * @extends Roo.bootstrap.Input
23985  * Bootstrap MonthField class
23986  * 
23987  * @cfg {String} language default en
23988  * 
23989  * @constructor
23990  * Create a new MonthField
23991  * @param {Object} config The config object
23992  */
23993
23994 Roo.bootstrap.MonthField = function(config){
23995     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23996     
23997     this.addEvents({
23998         /**
23999          * @event show
24000          * Fires when this field show.
24001          * @param {Roo.bootstrap.MonthField} this
24002          * @param {Mixed} date The date value
24003          */
24004         show : true,
24005         /**
24006          * @event show
24007          * Fires when this field hide.
24008          * @param {Roo.bootstrap.MonthField} this
24009          * @param {Mixed} date The date value
24010          */
24011         hide : true,
24012         /**
24013          * @event select
24014          * Fires when select a date.
24015          * @param {Roo.bootstrap.MonthField} this
24016          * @param {String} oldvalue The old value
24017          * @param {String} newvalue The new value
24018          */
24019         select : true
24020     });
24021 };
24022
24023 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24024     
24025     onRender: function(ct, position)
24026     {
24027         
24028         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24029         
24030         this.language = this.language || 'en';
24031         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24032         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24033         
24034         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24035         this.isInline = false;
24036         this.isInput = true;
24037         this.component = this.el.select('.add-on', true).first() || false;
24038         this.component = (this.component && this.component.length === 0) ? false : this.component;
24039         this.hasInput = this.component && this.inputEL().length;
24040         
24041         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24042         
24043         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24044         
24045         this.picker().on('mousedown', this.onMousedown, this);
24046         this.picker().on('click', this.onClick, this);
24047         
24048         this.picker().addClass('datepicker-dropdown');
24049         
24050         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24051             v.setStyle('width', '189px');
24052         });
24053         
24054         this.fillMonths();
24055         
24056         this.update();
24057         
24058         if(this.isInline) {
24059             this.show();
24060         }
24061         
24062     },
24063     
24064     setValue: function(v, suppressEvent)
24065     {   
24066         var o = this.getValue();
24067         
24068         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24069         
24070         this.update();
24071
24072         if(suppressEvent !== true){
24073             this.fireEvent('select', this, o, v);
24074         }
24075         
24076     },
24077     
24078     getValue: function()
24079     {
24080         return this.value;
24081     },
24082     
24083     onClick: function(e) 
24084     {
24085         e.stopPropagation();
24086         e.preventDefault();
24087         
24088         var target = e.getTarget();
24089         
24090         if(target.nodeName.toLowerCase() === 'i'){
24091             target = Roo.get(target).dom.parentNode;
24092         }
24093         
24094         var nodeName = target.nodeName;
24095         var className = target.className;
24096         var html = target.innerHTML;
24097         
24098         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24099             return;
24100         }
24101         
24102         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24103         
24104         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24105         
24106         this.hide();
24107                         
24108     },
24109     
24110     picker : function()
24111     {
24112         return this.pickerEl;
24113     },
24114     
24115     fillMonths: function()
24116     {    
24117         var i = 0;
24118         var months = this.picker().select('>.datepicker-months td', true).first();
24119         
24120         months.dom.innerHTML = '';
24121         
24122         while (i < 12) {
24123             var month = {
24124                 tag: 'span',
24125                 cls: 'month',
24126                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24127             };
24128             
24129             months.createChild(month);
24130         }
24131         
24132     },
24133     
24134     update: function()
24135     {
24136         var _this = this;
24137         
24138         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24139             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24140         }
24141         
24142         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24143             e.removeClass('active');
24144             
24145             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24146                 e.addClass('active');
24147             }
24148         })
24149     },
24150     
24151     place: function()
24152     {
24153         if(this.isInline) {
24154             return;
24155         }
24156         
24157         this.picker().removeClass(['bottom', 'top']);
24158         
24159         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24160             /*
24161              * place to the top of element!
24162              *
24163              */
24164             
24165             this.picker().addClass('top');
24166             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24167             
24168             return;
24169         }
24170         
24171         this.picker().addClass('bottom');
24172         
24173         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24174     },
24175     
24176     onFocus : function()
24177     {
24178         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24179         this.show();
24180     },
24181     
24182     onBlur : function()
24183     {
24184         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24185         
24186         var d = this.inputEl().getValue();
24187         
24188         this.setValue(d);
24189                 
24190         this.hide();
24191     },
24192     
24193     show : function()
24194     {
24195         this.picker().show();
24196         this.picker().select('>.datepicker-months', true).first().show();
24197         this.update();
24198         this.place();
24199         
24200         this.fireEvent('show', this, this.date);
24201     },
24202     
24203     hide : function()
24204     {
24205         if(this.isInline) {
24206             return;
24207         }
24208         this.picker().hide();
24209         this.fireEvent('hide', this, this.date);
24210         
24211     },
24212     
24213     onMousedown: function(e)
24214     {
24215         e.stopPropagation();
24216         e.preventDefault();
24217     },
24218     
24219     keyup: function(e)
24220     {
24221         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24222         this.update();
24223     },
24224
24225     fireKey: function(e)
24226     {
24227         if (!this.picker().isVisible()){
24228             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24229                 this.show();
24230             }
24231             return;
24232         }
24233         
24234         var dir;
24235         
24236         switch(e.keyCode){
24237             case 27: // escape
24238                 this.hide();
24239                 e.preventDefault();
24240                 break;
24241             case 37: // left
24242             case 39: // right
24243                 dir = e.keyCode == 37 ? -1 : 1;
24244                 
24245                 this.vIndex = this.vIndex + dir;
24246                 
24247                 if(this.vIndex < 0){
24248                     this.vIndex = 0;
24249                 }
24250                 
24251                 if(this.vIndex > 11){
24252                     this.vIndex = 11;
24253                 }
24254                 
24255                 if(isNaN(this.vIndex)){
24256                     this.vIndex = 0;
24257                 }
24258                 
24259                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24260                 
24261                 break;
24262             case 38: // up
24263             case 40: // down
24264                 
24265                 dir = e.keyCode == 38 ? -1 : 1;
24266                 
24267                 this.vIndex = this.vIndex + dir * 4;
24268                 
24269                 if(this.vIndex < 0){
24270                     this.vIndex = 0;
24271                 }
24272                 
24273                 if(this.vIndex > 11){
24274                     this.vIndex = 11;
24275                 }
24276                 
24277                 if(isNaN(this.vIndex)){
24278                     this.vIndex = 0;
24279                 }
24280                 
24281                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24282                 break;
24283                 
24284             case 13: // enter
24285                 
24286                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24287                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24288                 }
24289                 
24290                 this.hide();
24291                 e.preventDefault();
24292                 break;
24293             case 9: // tab
24294                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24295                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24296                 }
24297                 this.hide();
24298                 break;
24299             case 16: // shift
24300             case 17: // ctrl
24301             case 18: // alt
24302                 break;
24303             default :
24304                 this.hide();
24305                 
24306         }
24307     },
24308     
24309     remove: function() 
24310     {
24311         this.picker().remove();
24312     }
24313    
24314 });
24315
24316 Roo.apply(Roo.bootstrap.MonthField,  {
24317     
24318     content : {
24319         tag: 'tbody',
24320         cn: [
24321         {
24322             tag: 'tr',
24323             cn: [
24324             {
24325                 tag: 'td',
24326                 colspan: '7'
24327             }
24328             ]
24329         }
24330         ]
24331     },
24332     
24333     dates:{
24334         en: {
24335             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24336             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24337         }
24338     }
24339 });
24340
24341 Roo.apply(Roo.bootstrap.MonthField,  {
24342   
24343     template : {
24344         tag: 'div',
24345         cls: 'datepicker dropdown-menu roo-dynamic',
24346         cn: [
24347             {
24348                 tag: 'div',
24349                 cls: 'datepicker-months',
24350                 cn: [
24351                 {
24352                     tag: 'table',
24353                     cls: 'table-condensed',
24354                     cn:[
24355                         Roo.bootstrap.DateField.content
24356                     ]
24357                 }
24358                 ]
24359             }
24360         ]
24361     }
24362 });
24363
24364  
24365
24366  
24367  /*
24368  * - LGPL
24369  *
24370  * CheckBox
24371  * 
24372  */
24373
24374 /**
24375  * @class Roo.bootstrap.CheckBox
24376  * @extends Roo.bootstrap.Input
24377  * Bootstrap CheckBox class
24378  * 
24379  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24380  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24381  * @cfg {String} boxLabel The text that appears beside the checkbox
24382  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24383  * @cfg {Boolean} checked initnal the element
24384  * @cfg {Boolean} inline inline the element (default false)
24385  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24386  * @cfg {String} tooltip label tooltip
24387  * 
24388  * @constructor
24389  * Create a new CheckBox
24390  * @param {Object} config The config object
24391  */
24392
24393 Roo.bootstrap.CheckBox = function(config){
24394     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24395    
24396     this.addEvents({
24397         /**
24398         * @event check
24399         * Fires when the element is checked or unchecked.
24400         * @param {Roo.bootstrap.CheckBox} this This input
24401         * @param {Boolean} checked The new checked value
24402         */
24403        check : true,
24404        /**
24405         * @event click
24406         * Fires when the element is click.
24407         * @param {Roo.bootstrap.CheckBox} this This input
24408         */
24409        click : true
24410     });
24411     
24412 };
24413
24414 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24415   
24416     inputType: 'checkbox',
24417     inputValue: 1,
24418     valueOff: 0,
24419     boxLabel: false,
24420     checked: false,
24421     weight : false,
24422     inline: false,
24423     tooltip : '',
24424     
24425     // checkbox success does not make any sense really.. 
24426     invalidClass : "",
24427     validClass : "",
24428     
24429     
24430     getAutoCreate : function()
24431     {
24432         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24433         
24434         var id = Roo.id();
24435         
24436         var cfg = {};
24437         
24438         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24439         
24440         if(this.inline){
24441             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24442         }
24443         
24444         var input =  {
24445             tag: 'input',
24446             id : id,
24447             type : this.inputType,
24448             value : this.inputValue,
24449             cls : 'roo-' + this.inputType, //'form-box',
24450             placeholder : this.placeholder || ''
24451             
24452         };
24453         
24454         if(this.inputType != 'radio'){
24455             var hidden =  {
24456                 tag: 'input',
24457                 type : 'hidden',
24458                 cls : 'roo-hidden-value',
24459                 value : this.checked ? this.inputValue : this.valueOff
24460             };
24461         }
24462         
24463             
24464         if (this.weight) { // Validity check?
24465             cfg.cls += " " + this.inputType + "-" + this.weight;
24466         }
24467         
24468         if (this.disabled) {
24469             input.disabled=true;
24470         }
24471         
24472         if(this.checked){
24473             input.checked = this.checked;
24474         }
24475         
24476         if (this.name) {
24477             
24478             input.name = this.name;
24479             
24480             if(this.inputType != 'radio'){
24481                 hidden.name = this.name;
24482                 input.name = '_hidden_' + this.name;
24483             }
24484         }
24485         
24486         if (this.size) {
24487             input.cls += ' input-' + this.size;
24488         }
24489         
24490         var settings=this;
24491         
24492         ['xs','sm','md','lg'].map(function(size){
24493             if (settings[size]) {
24494                 cfg.cls += ' col-' + size + '-' + settings[size];
24495             }
24496         });
24497         
24498         var inputblock = input;
24499          
24500         if (this.before || this.after) {
24501             
24502             inputblock = {
24503                 cls : 'input-group',
24504                 cn :  [] 
24505             };
24506             
24507             if (this.before) {
24508                 inputblock.cn.push({
24509                     tag :'span',
24510                     cls : 'input-group-addon',
24511                     html : this.before
24512                 });
24513             }
24514             
24515             inputblock.cn.push(input);
24516             
24517             if(this.inputType != 'radio'){
24518                 inputblock.cn.push(hidden);
24519             }
24520             
24521             if (this.after) {
24522                 inputblock.cn.push({
24523                     tag :'span',
24524                     cls : 'input-group-addon',
24525                     html : this.after
24526                 });
24527             }
24528             
24529         }
24530         var boxLabelCfg = false;
24531         
24532         if(this.boxLabel){
24533            
24534             boxLabelCfg = {
24535                 tag: 'label',
24536                 //'for': id, // box label is handled by onclick - so no for...
24537                 cls: 'box-label',
24538                 html: this.boxLabel
24539             };
24540             if(this.tooltip){
24541                 boxLabelCfg.tooltip = this.tooltip;
24542             }
24543              
24544         }
24545         
24546         
24547         if (align ==='left' && this.fieldLabel.length) {
24548 //                Roo.log("left and has label");
24549             cfg.cn = [
24550                 {
24551                     tag: 'label',
24552                     'for' :  id,
24553                     cls : 'control-label',
24554                     html : this.fieldLabel
24555                 },
24556                 {
24557                     cls : "", 
24558                     cn: [
24559                         inputblock
24560                     ]
24561                 }
24562             ];
24563             
24564             if (boxLabelCfg) {
24565                 cfg.cn[1].cn.push(boxLabelCfg);
24566             }
24567             
24568             if(this.labelWidth > 12){
24569                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24570             }
24571             
24572             if(this.labelWidth < 13 && this.labelmd == 0){
24573                 this.labelmd = this.labelWidth;
24574             }
24575             
24576             if(this.labellg > 0){
24577                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24578                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24579             }
24580             
24581             if(this.labelmd > 0){
24582                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24583                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24584             }
24585             
24586             if(this.labelsm > 0){
24587                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24588                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24589             }
24590             
24591             if(this.labelxs > 0){
24592                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24593                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24594             }
24595             
24596         } else if ( this.fieldLabel.length) {
24597 //                Roo.log(" label");
24598                 cfg.cn = [
24599                    
24600                     {
24601                         tag: this.boxLabel ? 'span' : 'label',
24602                         'for': id,
24603                         cls: 'control-label box-input-label',
24604                         //cls : 'input-group-addon',
24605                         html : this.fieldLabel
24606                     },
24607                     
24608                     inputblock
24609                     
24610                 ];
24611                 if (boxLabelCfg) {
24612                     cfg.cn.push(boxLabelCfg);
24613                 }
24614
24615         } else {
24616             
24617 //                Roo.log(" no label && no align");
24618                 cfg.cn = [  inputblock ] ;
24619                 if (boxLabelCfg) {
24620                     cfg.cn.push(boxLabelCfg);
24621                 }
24622
24623                 
24624         }
24625         
24626        
24627         
24628         if(this.inputType != 'radio'){
24629             cfg.cn.push(hidden);
24630         }
24631         
24632         return cfg;
24633         
24634     },
24635     
24636     /**
24637      * return the real input element.
24638      */
24639     inputEl: function ()
24640     {
24641         return this.el.select('input.roo-' + this.inputType,true).first();
24642     },
24643     hiddenEl: function ()
24644     {
24645         return this.el.select('input.roo-hidden-value',true).first();
24646     },
24647     
24648     labelEl: function()
24649     {
24650         return this.el.select('label.control-label',true).first();
24651     },
24652     /* depricated... */
24653     
24654     label: function()
24655     {
24656         return this.labelEl();
24657     },
24658     
24659     boxLabelEl: function()
24660     {
24661         return this.el.select('label.box-label',true).first();
24662     },
24663     
24664     initEvents : function()
24665     {
24666 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24667         
24668         this.inputEl().on('click', this.onClick,  this);
24669         
24670         if (this.boxLabel) { 
24671             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24672         }
24673         
24674         this.startValue = this.getValue();
24675         
24676         if(this.groupId){
24677             Roo.bootstrap.CheckBox.register(this);
24678         }
24679     },
24680     
24681     onClick : function(e)
24682     {   
24683         if(this.fireEvent('click', this, e) !== false){
24684             this.setChecked(!this.checked);
24685         }
24686         
24687     },
24688     
24689     setChecked : function(state,suppressEvent)
24690     {
24691         this.startValue = this.getValue();
24692
24693         if(this.inputType == 'radio'){
24694             
24695             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24696                 e.dom.checked = false;
24697             });
24698             
24699             this.inputEl().dom.checked = true;
24700             
24701             this.inputEl().dom.value = this.inputValue;
24702             
24703             if(suppressEvent !== true){
24704                 this.fireEvent('check', this, true);
24705             }
24706             
24707             this.validate();
24708             
24709             return;
24710         }
24711         
24712         this.checked = state;
24713         
24714         this.inputEl().dom.checked = state;
24715         
24716         
24717         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24718         
24719         if(suppressEvent !== true){
24720             this.fireEvent('check', this, state);
24721         }
24722         
24723         this.validate();
24724     },
24725     
24726     getValue : function()
24727     {
24728         if(this.inputType == 'radio'){
24729             return this.getGroupValue();
24730         }
24731         
24732         return this.hiddenEl().dom.value;
24733         
24734     },
24735     
24736     getGroupValue : function()
24737     {
24738         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24739             return '';
24740         }
24741         
24742         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24743     },
24744     
24745     setValue : function(v,suppressEvent)
24746     {
24747         if(this.inputType == 'radio'){
24748             this.setGroupValue(v, suppressEvent);
24749             return;
24750         }
24751         
24752         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24753         
24754         this.validate();
24755     },
24756     
24757     setGroupValue : function(v, suppressEvent)
24758     {
24759         this.startValue = this.getValue();
24760         
24761         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24762             e.dom.checked = false;
24763             
24764             if(e.dom.value == v){
24765                 e.dom.checked = true;
24766             }
24767         });
24768         
24769         if(suppressEvent !== true){
24770             this.fireEvent('check', this, true);
24771         }
24772
24773         this.validate();
24774         
24775         return;
24776     },
24777     
24778     validate : function()
24779     {
24780         if(this.getVisibilityEl().hasClass('hidden')){
24781             return true;
24782         }
24783         
24784         if(
24785                 this.disabled || 
24786                 (this.inputType == 'radio' && this.validateRadio()) ||
24787                 (this.inputType == 'checkbox' && this.validateCheckbox())
24788         ){
24789             this.markValid();
24790             return true;
24791         }
24792         
24793         this.markInvalid();
24794         return false;
24795     },
24796     
24797     validateRadio : function()
24798     {
24799         if(this.getVisibilityEl().hasClass('hidden')){
24800             return true;
24801         }
24802         
24803         if(this.allowBlank){
24804             return true;
24805         }
24806         
24807         var valid = false;
24808         
24809         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24810             if(!e.dom.checked){
24811                 return;
24812             }
24813             
24814             valid = true;
24815             
24816             return false;
24817         });
24818         
24819         return valid;
24820     },
24821     
24822     validateCheckbox : function()
24823     {
24824         if(!this.groupId){
24825             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24826             //return (this.getValue() == this.inputValue) ? true : false;
24827         }
24828         
24829         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24830         
24831         if(!group){
24832             return false;
24833         }
24834         
24835         var r = false;
24836         
24837         for(var i in group){
24838             if(group[i].el.isVisible(true)){
24839                 r = false;
24840                 break;
24841             }
24842             
24843             r = true;
24844         }
24845         
24846         for(var i in group){
24847             if(r){
24848                 break;
24849             }
24850             
24851             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24852         }
24853         
24854         return r;
24855     },
24856     
24857     /**
24858      * Mark this field as valid
24859      */
24860     markValid : function()
24861     {
24862         var _this = this;
24863         
24864         this.fireEvent('valid', this);
24865         
24866         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24867         
24868         if(this.groupId){
24869             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24870         }
24871         
24872         if(label){
24873             label.markValid();
24874         }
24875
24876         if(this.inputType == 'radio'){
24877             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24878                 var fg = e.findParent('.form-group', false, true);
24879                 if (Roo.bootstrap.version == 3) {
24880                     fg.removeClass([_this.invalidClass, _this.validClass]);
24881                     fg.addClass(_this.validClass);
24882                 } else {
24883                     fg.removeClass(['is-valid', 'is-invalid']);
24884                     fg.addClass('is-valid');
24885                 }
24886             });
24887             
24888             return;
24889         }
24890
24891         if(!this.groupId){
24892             var fg = this.el.findParent('.form-group', false, true);
24893             if (Roo.bootstrap.version == 3) {
24894                 fg.removeClass([this.invalidClass, this.validClass]);
24895                 fg.addClass(this.validClass);
24896             } else {
24897                 fg.removeClass(['is-valid', 'is-invalid']);
24898                 fg.addClass('is-valid');
24899             }
24900             return;
24901         }
24902         
24903         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24904         
24905         if(!group){
24906             return;
24907         }
24908         
24909         for(var i in group){
24910             var fg = group[i].el.findParent('.form-group', false, true);
24911             if (Roo.bootstrap.version == 3) {
24912                 fg.removeClass([this.invalidClass, this.validClass]);
24913                 fg.addClass(this.validClass);
24914             } else {
24915                 fg.removeClass(['is-valid', 'is-invalid']);
24916                 fg.addClass('is-valid');
24917             }
24918         }
24919     },
24920     
24921      /**
24922      * Mark this field as invalid
24923      * @param {String} msg The validation message
24924      */
24925     markInvalid : function(msg)
24926     {
24927         if(this.allowBlank){
24928             return;
24929         }
24930         
24931         var _this = this;
24932         
24933         this.fireEvent('invalid', this, msg);
24934         
24935         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24936         
24937         if(this.groupId){
24938             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24939         }
24940         
24941         if(label){
24942             label.markInvalid();
24943         }
24944             
24945         if(this.inputType == 'radio'){
24946             
24947             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24948                 var fg = e.findParent('.form-group', false, true);
24949                 if (Roo.bootstrap.version == 3) {
24950                     fg.removeClass([_this.invalidClass, _this.validClass]);
24951                     fg.addClass(_this.invalidClass);
24952                 } else {
24953                     fg.removeClass(['is-invalid', 'is-valid']);
24954                     fg.addClass('is-invalid');
24955                 }
24956             });
24957             
24958             return;
24959         }
24960         
24961         if(!this.groupId){
24962             var fg = this.el.findParent('.form-group', false, true);
24963             if (Roo.bootstrap.version == 3) {
24964                 fg.removeClass([_this.invalidClass, _this.validClass]);
24965                 fg.addClass(_this.invalidClass);
24966             } else {
24967                 fg.removeClass(['is-invalid', 'is-valid']);
24968                 fg.addClass('is-invalid');
24969             }
24970             return;
24971         }
24972         
24973         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24974         
24975         if(!group){
24976             return;
24977         }
24978         
24979         for(var i in group){
24980             var fg = group[i].el.findParent('.form-group', false, true);
24981             if (Roo.bootstrap.version == 3) {
24982                 fg.removeClass([_this.invalidClass, _this.validClass]);
24983                 fg.addClass(_this.invalidClass);
24984             } else {
24985                 fg.removeClass(['is-invalid', 'is-valid']);
24986                 fg.addClass('is-invalid');
24987             }
24988         }
24989         
24990     },
24991     
24992     clearInvalid : function()
24993     {
24994         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24995         
24996         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24997         
24998         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24999         
25000         if (label && label.iconEl) {
25001             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25002             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25003         }
25004     },
25005     
25006     disable : function()
25007     {
25008         if(this.inputType != 'radio'){
25009             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25010             return;
25011         }
25012         
25013         var _this = this;
25014         
25015         if(this.rendered){
25016             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25017                 _this.getActionEl().addClass(this.disabledClass);
25018                 e.dom.disabled = true;
25019             });
25020         }
25021         
25022         this.disabled = true;
25023         this.fireEvent("disable", this);
25024         return this;
25025     },
25026
25027     enable : function()
25028     {
25029         if(this.inputType != 'radio'){
25030             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25031             return;
25032         }
25033         
25034         var _this = this;
25035         
25036         if(this.rendered){
25037             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25038                 _this.getActionEl().removeClass(this.disabledClass);
25039                 e.dom.disabled = false;
25040             });
25041         }
25042         
25043         this.disabled = false;
25044         this.fireEvent("enable", this);
25045         return this;
25046     },
25047     
25048     setBoxLabel : function(v)
25049     {
25050         this.boxLabel = v;
25051         
25052         if(this.rendered){
25053             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25054         }
25055     }
25056
25057 });
25058
25059 Roo.apply(Roo.bootstrap.CheckBox, {
25060     
25061     groups: {},
25062     
25063      /**
25064     * register a CheckBox Group
25065     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25066     */
25067     register : function(checkbox)
25068     {
25069         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25070             this.groups[checkbox.groupId] = {};
25071         }
25072         
25073         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25074             return;
25075         }
25076         
25077         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25078         
25079     },
25080     /**
25081     * fetch a CheckBox Group based on the group ID
25082     * @param {string} the group ID
25083     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25084     */
25085     get: function(groupId) {
25086         if (typeof(this.groups[groupId]) == 'undefined') {
25087             return false;
25088         }
25089         
25090         return this.groups[groupId] ;
25091     }
25092     
25093     
25094 });
25095 /*
25096  * - LGPL
25097  *
25098  * RadioItem
25099  * 
25100  */
25101
25102 /**
25103  * @class Roo.bootstrap.Radio
25104  * @extends Roo.bootstrap.Component
25105  * Bootstrap Radio class
25106  * @cfg {String} boxLabel - the label associated
25107  * @cfg {String} value - the value of radio
25108  * 
25109  * @constructor
25110  * Create a new Radio
25111  * @param {Object} config The config object
25112  */
25113 Roo.bootstrap.Radio = function(config){
25114     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25115     
25116 };
25117
25118 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25119     
25120     boxLabel : '',
25121     
25122     value : '',
25123     
25124     getAutoCreate : function()
25125     {
25126         var cfg = {
25127             tag : 'div',
25128             cls : 'form-group radio',
25129             cn : [
25130                 {
25131                     tag : 'label',
25132                     cls : 'box-label',
25133                     html : this.boxLabel
25134                 }
25135             ]
25136         };
25137         
25138         return cfg;
25139     },
25140     
25141     initEvents : function() 
25142     {
25143         this.parent().register(this);
25144         
25145         this.el.on('click', this.onClick, this);
25146         
25147     },
25148     
25149     onClick : function(e)
25150     {
25151         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25152             this.setChecked(true);
25153         }
25154     },
25155     
25156     setChecked : function(state, suppressEvent)
25157     {
25158         this.parent().setValue(this.value, suppressEvent);
25159         
25160     },
25161     
25162     setBoxLabel : function(v)
25163     {
25164         this.boxLabel = v;
25165         
25166         if(this.rendered){
25167             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25168         }
25169     }
25170     
25171 });
25172  
25173
25174  /*
25175  * - LGPL
25176  *
25177  * Input
25178  * 
25179  */
25180
25181 /**
25182  * @class Roo.bootstrap.SecurePass
25183  * @extends Roo.bootstrap.Input
25184  * Bootstrap SecurePass class
25185  *
25186  * 
25187  * @constructor
25188  * Create a new SecurePass
25189  * @param {Object} config The config object
25190  */
25191  
25192 Roo.bootstrap.SecurePass = function (config) {
25193     // these go here, so the translation tool can replace them..
25194     this.errors = {
25195         PwdEmpty: "Please type a password, and then retype it to confirm.",
25196         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25197         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25198         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25199         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25200         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25201         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25202         TooWeak: "Your password is Too Weak."
25203     },
25204     this.meterLabel = "Password strength:";
25205     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25206     this.meterClass = [
25207         "roo-password-meter-tooweak", 
25208         "roo-password-meter-weak", 
25209         "roo-password-meter-medium", 
25210         "roo-password-meter-strong", 
25211         "roo-password-meter-grey"
25212     ];
25213     
25214     this.errors = {};
25215     
25216     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25217 }
25218
25219 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25220     /**
25221      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25222      * {
25223      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25224      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25225      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25226      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25227      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25228      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25229      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25230      * })
25231      */
25232     // private
25233     
25234     meterWidth: 300,
25235     errorMsg :'',    
25236     errors: false,
25237     imageRoot: '/',
25238     /**
25239      * @cfg {String/Object} Label for the strength meter (defaults to
25240      * 'Password strength:')
25241      */
25242     // private
25243     meterLabel: '',
25244     /**
25245      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25246      * ['Weak', 'Medium', 'Strong'])
25247      */
25248     // private    
25249     pwdStrengths: false,    
25250     // private
25251     strength: 0,
25252     // private
25253     _lastPwd: null,
25254     // private
25255     kCapitalLetter: 0,
25256     kSmallLetter: 1,
25257     kDigit: 2,
25258     kPunctuation: 3,
25259     
25260     insecure: false,
25261     // private
25262     initEvents: function ()
25263     {
25264         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25265
25266         if (this.el.is('input[type=password]') && Roo.isSafari) {
25267             this.el.on('keydown', this.SafariOnKeyDown, this);
25268         }
25269
25270         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25271     },
25272     // private
25273     onRender: function (ct, position)
25274     {
25275         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25276         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25277         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25278
25279         this.trigger.createChild({
25280                    cn: [
25281                     {
25282                     //id: 'PwdMeter',
25283                     tag: 'div',
25284                     cls: 'roo-password-meter-grey col-xs-12',
25285                     style: {
25286                         //width: 0,
25287                         //width: this.meterWidth + 'px'                                                
25288                         }
25289                     },
25290                     {                            
25291                          cls: 'roo-password-meter-text'                          
25292                     }
25293                 ]            
25294         });
25295
25296          
25297         if (this.hideTrigger) {
25298             this.trigger.setDisplayed(false);
25299         }
25300         this.setSize(this.width || '', this.height || '');
25301     },
25302     // private
25303     onDestroy: function ()
25304     {
25305         if (this.trigger) {
25306             this.trigger.removeAllListeners();
25307             this.trigger.remove();
25308         }
25309         if (this.wrap) {
25310             this.wrap.remove();
25311         }
25312         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25313     },
25314     // private
25315     checkStrength: function ()
25316     {
25317         var pwd = this.inputEl().getValue();
25318         if (pwd == this._lastPwd) {
25319             return;
25320         }
25321
25322         var strength;
25323         if (this.ClientSideStrongPassword(pwd)) {
25324             strength = 3;
25325         } else if (this.ClientSideMediumPassword(pwd)) {
25326             strength = 2;
25327         } else if (this.ClientSideWeakPassword(pwd)) {
25328             strength = 1;
25329         } else {
25330             strength = 0;
25331         }
25332         
25333         Roo.log('strength1: ' + strength);
25334         
25335         //var pm = this.trigger.child('div/div/div').dom;
25336         var pm = this.trigger.child('div/div');
25337         pm.removeClass(this.meterClass);
25338         pm.addClass(this.meterClass[strength]);
25339                 
25340         
25341         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25342                 
25343         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25344         
25345         this._lastPwd = pwd;
25346     },
25347     reset: function ()
25348     {
25349         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25350         
25351         this._lastPwd = '';
25352         
25353         var pm = this.trigger.child('div/div');
25354         pm.removeClass(this.meterClass);
25355         pm.addClass('roo-password-meter-grey');        
25356         
25357         
25358         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25359         
25360         pt.innerHTML = '';
25361         this.inputEl().dom.type='password';
25362     },
25363     // private
25364     validateValue: function (value)
25365     {
25366         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25367             return false;
25368         }
25369         if (value.length == 0) {
25370             if (this.allowBlank) {
25371                 this.clearInvalid();
25372                 return true;
25373             }
25374
25375             this.markInvalid(this.errors.PwdEmpty);
25376             this.errorMsg = this.errors.PwdEmpty;
25377             return false;
25378         }
25379         
25380         if(this.insecure){
25381             return true;
25382         }
25383         
25384         if (!value.match(/[\x21-\x7e]+/)) {
25385             this.markInvalid(this.errors.PwdBadChar);
25386             this.errorMsg = this.errors.PwdBadChar;
25387             return false;
25388         }
25389         if (value.length < 6) {
25390             this.markInvalid(this.errors.PwdShort);
25391             this.errorMsg = this.errors.PwdShort;
25392             return false;
25393         }
25394         if (value.length > 16) {
25395             this.markInvalid(this.errors.PwdLong);
25396             this.errorMsg = this.errors.PwdLong;
25397             return false;
25398         }
25399         var strength;
25400         if (this.ClientSideStrongPassword(value)) {
25401             strength = 3;
25402         } else if (this.ClientSideMediumPassword(value)) {
25403             strength = 2;
25404         } else if (this.ClientSideWeakPassword(value)) {
25405             strength = 1;
25406         } else {
25407             strength = 0;
25408         }
25409
25410         
25411         if (strength < 2) {
25412             //this.markInvalid(this.errors.TooWeak);
25413             this.errorMsg = this.errors.TooWeak;
25414             //return false;
25415         }
25416         
25417         
25418         console.log('strength2: ' + strength);
25419         
25420         //var pm = this.trigger.child('div/div/div').dom;
25421         
25422         var pm = this.trigger.child('div/div');
25423         pm.removeClass(this.meterClass);
25424         pm.addClass(this.meterClass[strength]);
25425                 
25426         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25427                 
25428         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25429         
25430         this.errorMsg = ''; 
25431         return true;
25432     },
25433     // private
25434     CharacterSetChecks: function (type)
25435     {
25436         this.type = type;
25437         this.fResult = false;
25438     },
25439     // private
25440     isctype: function (character, type)
25441     {
25442         switch (type) {  
25443             case this.kCapitalLetter:
25444                 if (character >= 'A' && character <= 'Z') {
25445                     return true;
25446                 }
25447                 break;
25448             
25449             case this.kSmallLetter:
25450                 if (character >= 'a' && character <= 'z') {
25451                     return true;
25452                 }
25453                 break;
25454             
25455             case this.kDigit:
25456                 if (character >= '0' && character <= '9') {
25457                     return true;
25458                 }
25459                 break;
25460             
25461             case this.kPunctuation:
25462                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25463                     return true;
25464                 }
25465                 break;
25466             
25467             default:
25468                 return false;
25469         }
25470
25471     },
25472     // private
25473     IsLongEnough: function (pwd, size)
25474     {
25475         return !(pwd == null || isNaN(size) || pwd.length < size);
25476     },
25477     // private
25478     SpansEnoughCharacterSets: function (word, nb)
25479     {
25480         if (!this.IsLongEnough(word, nb))
25481         {
25482             return false;
25483         }
25484
25485         var characterSetChecks = new Array(
25486             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25487             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25488         );
25489         
25490         for (var index = 0; index < word.length; ++index) {
25491             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25492                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25493                     characterSetChecks[nCharSet].fResult = true;
25494                     break;
25495                 }
25496             }
25497         }
25498
25499         var nCharSets = 0;
25500         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25501             if (characterSetChecks[nCharSet].fResult) {
25502                 ++nCharSets;
25503             }
25504         }
25505
25506         if (nCharSets < nb) {
25507             return false;
25508         }
25509         return true;
25510     },
25511     // private
25512     ClientSideStrongPassword: function (pwd)
25513     {
25514         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25515     },
25516     // private
25517     ClientSideMediumPassword: function (pwd)
25518     {
25519         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25520     },
25521     // private
25522     ClientSideWeakPassword: function (pwd)
25523     {
25524         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25525     }
25526           
25527 })//<script type="text/javascript">
25528
25529 /*
25530  * Based  Ext JS Library 1.1.1
25531  * Copyright(c) 2006-2007, Ext JS, LLC.
25532  * LGPL
25533  *
25534  */
25535  
25536 /**
25537  * @class Roo.HtmlEditorCore
25538  * @extends Roo.Component
25539  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25540  *
25541  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25542  */
25543
25544 Roo.HtmlEditorCore = function(config){
25545     
25546     
25547     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25548     
25549     
25550     this.addEvents({
25551         /**
25552          * @event initialize
25553          * Fires when the editor is fully initialized (including the iframe)
25554          * @param {Roo.HtmlEditorCore} this
25555          */
25556         initialize: true,
25557         /**
25558          * @event activate
25559          * Fires when the editor is first receives the focus. Any insertion must wait
25560          * until after this event.
25561          * @param {Roo.HtmlEditorCore} this
25562          */
25563         activate: true,
25564          /**
25565          * @event beforesync
25566          * Fires before the textarea is updated with content from the editor iframe. Return false
25567          * to cancel the sync.
25568          * @param {Roo.HtmlEditorCore} this
25569          * @param {String} html
25570          */
25571         beforesync: true,
25572          /**
25573          * @event beforepush
25574          * Fires before the iframe editor is updated with content from the textarea. Return false
25575          * to cancel the push.
25576          * @param {Roo.HtmlEditorCore} this
25577          * @param {String} html
25578          */
25579         beforepush: true,
25580          /**
25581          * @event sync
25582          * Fires when the textarea is updated with content from the editor iframe.
25583          * @param {Roo.HtmlEditorCore} this
25584          * @param {String} html
25585          */
25586         sync: true,
25587          /**
25588          * @event push
25589          * Fires when the iframe editor is updated with content from the textarea.
25590          * @param {Roo.HtmlEditorCore} this
25591          * @param {String} html
25592          */
25593         push: true,
25594         
25595         /**
25596          * @event editorevent
25597          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25598          * @param {Roo.HtmlEditorCore} this
25599          */
25600         editorevent: true
25601         
25602     });
25603     
25604     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25605     
25606     // defaults : white / black...
25607     this.applyBlacklists();
25608     
25609     
25610     
25611 };
25612
25613
25614 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25615
25616
25617      /**
25618      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25619      */
25620     
25621     owner : false,
25622     
25623      /**
25624      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25625      *                        Roo.resizable.
25626      */
25627     resizable : false,
25628      /**
25629      * @cfg {Number} height (in pixels)
25630      */   
25631     height: 300,
25632    /**
25633      * @cfg {Number} width (in pixels)
25634      */   
25635     width: 500,
25636     
25637     /**
25638      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25639      * 
25640      */
25641     stylesheets: false,
25642     
25643     /**
25644      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25645      */
25646     allowComments: false,
25647     // id of frame..
25648     frameId: false,
25649     
25650     // private properties
25651     validationEvent : false,
25652     deferHeight: true,
25653     initialized : false,
25654     activated : false,
25655     sourceEditMode : false,
25656     onFocus : Roo.emptyFn,
25657     iframePad:3,
25658     hideMode:'offsets',
25659     
25660     clearUp: true,
25661     
25662     // blacklist + whitelisted elements..
25663     black: false,
25664     white: false,
25665      
25666     bodyCls : '',
25667
25668     /**
25669      * Protected method that will not generally be called directly. It
25670      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25671      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25672      */
25673     getDocMarkup : function(){
25674         // body styles..
25675         var st = '';
25676         
25677         // inherit styels from page...?? 
25678         if (this.stylesheets === false) {
25679             
25680             Roo.get(document.head).select('style').each(function(node) {
25681                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25682             });
25683             
25684             Roo.get(document.head).select('link').each(function(node) { 
25685                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25686             });
25687             
25688         } else if (!this.stylesheets.length) {
25689                 // simple..
25690                 st = '<style type="text/css">' +
25691                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25692                    '</style>';
25693         } else {
25694             for (var i in this.stylesheets) { 
25695                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25696             }
25697             
25698         }
25699         
25700         st +=  '<style type="text/css">' +
25701             'IMG { cursor: pointer } ' +
25702         '</style>';
25703
25704         var cls = 'roo-htmleditor-body';
25705         
25706         if(this.bodyCls.length){
25707             cls += ' ' + this.bodyCls;
25708         }
25709         
25710         return '<html><head>' + st  +
25711             //<style type="text/css">' +
25712             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25713             //'</style>' +
25714             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25715     },
25716
25717     // private
25718     onRender : function(ct, position)
25719     {
25720         var _t = this;
25721         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25722         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25723         
25724         
25725         this.el.dom.style.border = '0 none';
25726         this.el.dom.setAttribute('tabIndex', -1);
25727         this.el.addClass('x-hidden hide');
25728         
25729         
25730         
25731         if(Roo.isIE){ // fix IE 1px bogus margin
25732             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25733         }
25734        
25735         
25736         this.frameId = Roo.id();
25737         
25738          
25739         
25740         var iframe = this.owner.wrap.createChild({
25741             tag: 'iframe',
25742             cls: 'form-control', // bootstrap..
25743             id: this.frameId,
25744             name: this.frameId,
25745             frameBorder : 'no',
25746             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25747         }, this.el
25748         );
25749         
25750         
25751         this.iframe = iframe.dom;
25752
25753          this.assignDocWin();
25754         
25755         this.doc.designMode = 'on';
25756        
25757         this.doc.open();
25758         this.doc.write(this.getDocMarkup());
25759         this.doc.close();
25760
25761         
25762         var task = { // must defer to wait for browser to be ready
25763             run : function(){
25764                 //console.log("run task?" + this.doc.readyState);
25765                 this.assignDocWin();
25766                 if(this.doc.body || this.doc.readyState == 'complete'){
25767                     try {
25768                         this.doc.designMode="on";
25769                     } catch (e) {
25770                         return;
25771                     }
25772                     Roo.TaskMgr.stop(task);
25773                     this.initEditor.defer(10, this);
25774                 }
25775             },
25776             interval : 10,
25777             duration: 10000,
25778             scope: this
25779         };
25780         Roo.TaskMgr.start(task);
25781
25782     },
25783
25784     // private
25785     onResize : function(w, h)
25786     {
25787          Roo.log('resize: ' +w + ',' + h );
25788         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25789         if(!this.iframe){
25790             return;
25791         }
25792         if(typeof w == 'number'){
25793             
25794             this.iframe.style.width = w + 'px';
25795         }
25796         if(typeof h == 'number'){
25797             
25798             this.iframe.style.height = h + 'px';
25799             if(this.doc){
25800                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25801             }
25802         }
25803         
25804     },
25805
25806     /**
25807      * Toggles the editor between standard and source edit mode.
25808      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25809      */
25810     toggleSourceEdit : function(sourceEditMode){
25811         
25812         this.sourceEditMode = sourceEditMode === true;
25813         
25814         if(this.sourceEditMode){
25815  
25816             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25817             
25818         }else{
25819             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25820             //this.iframe.className = '';
25821             this.deferFocus();
25822         }
25823         //this.setSize(this.owner.wrap.getSize());
25824         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25825     },
25826
25827     
25828   
25829
25830     /**
25831      * Protected method that will not generally be called directly. If you need/want
25832      * custom HTML cleanup, this is the method you should override.
25833      * @param {String} html The HTML to be cleaned
25834      * return {String} The cleaned HTML
25835      */
25836     cleanHtml : function(html){
25837         html = String(html);
25838         if(html.length > 5){
25839             if(Roo.isSafari){ // strip safari nonsense
25840                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25841             }
25842         }
25843         if(html == '&nbsp;'){
25844             html = '';
25845         }
25846         return html;
25847     },
25848
25849     /**
25850      * HTML Editor -> Textarea
25851      * Protected method that will not generally be called directly. Syncs the contents
25852      * of the editor iframe with the textarea.
25853      */
25854     syncValue : function(){
25855         if(this.initialized){
25856             var bd = (this.doc.body || this.doc.documentElement);
25857             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25858             var html = bd.innerHTML;
25859             if(Roo.isSafari){
25860                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25861                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25862                 if(m && m[1]){
25863                     html = '<div style="'+m[0]+'">' + html + '</div>';
25864                 }
25865             }
25866             html = this.cleanHtml(html);
25867             // fix up the special chars.. normaly like back quotes in word...
25868             // however we do not want to do this with chinese..
25869             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25870                 
25871                 var cc = match.charCodeAt();
25872
25873                 // Get the character value, handling surrogate pairs
25874                 if (match.length == 2) {
25875                     // It's a surrogate pair, calculate the Unicode code point
25876                     var high = match.charCodeAt(0) - 0xD800;
25877                     var low  = match.charCodeAt(1) - 0xDC00;
25878                     cc = (high * 0x400) + low + 0x10000;
25879                 }  else if (
25880                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25881                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25882                     (cc >= 0xf900 && cc < 0xfb00 )
25883                 ) {
25884                         return match;
25885                 }  
25886          
25887                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25888                 return "&#" + cc + ";";
25889                 
25890                 
25891             });
25892             
25893             
25894              
25895             if(this.owner.fireEvent('beforesync', this, html) !== false){
25896                 this.el.dom.value = html;
25897                 this.owner.fireEvent('sync', this, html);
25898             }
25899         }
25900     },
25901
25902     /**
25903      * Protected method that will not generally be called directly. Pushes the value of the textarea
25904      * into the iframe editor.
25905      */
25906     pushValue : function(){
25907         if(this.initialized){
25908             var v = this.el.dom.value.trim();
25909             
25910 //            if(v.length < 1){
25911 //                v = '&#160;';
25912 //            }
25913             
25914             if(this.owner.fireEvent('beforepush', this, v) !== false){
25915                 var d = (this.doc.body || this.doc.documentElement);
25916                 d.innerHTML = v;
25917                 this.cleanUpPaste();
25918                 this.el.dom.value = d.innerHTML;
25919                 this.owner.fireEvent('push', this, v);
25920             }
25921         }
25922     },
25923
25924     // private
25925     deferFocus : function(){
25926         this.focus.defer(10, this);
25927     },
25928
25929     // doc'ed in Field
25930     focus : function(){
25931         if(this.win && !this.sourceEditMode){
25932             this.win.focus();
25933         }else{
25934             this.el.focus();
25935         }
25936     },
25937     
25938     assignDocWin: function()
25939     {
25940         var iframe = this.iframe;
25941         
25942          if(Roo.isIE){
25943             this.doc = iframe.contentWindow.document;
25944             this.win = iframe.contentWindow;
25945         } else {
25946 //            if (!Roo.get(this.frameId)) {
25947 //                return;
25948 //            }
25949 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25950 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25951             
25952             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25953                 return;
25954             }
25955             
25956             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25957             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25958         }
25959     },
25960     
25961     // private
25962     initEditor : function(){
25963         //console.log("INIT EDITOR");
25964         this.assignDocWin();
25965         
25966         
25967         
25968         this.doc.designMode="on";
25969         this.doc.open();
25970         this.doc.write(this.getDocMarkup());
25971         this.doc.close();
25972         
25973         var dbody = (this.doc.body || this.doc.documentElement);
25974         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25975         // this copies styles from the containing element into thsi one..
25976         // not sure why we need all of this..
25977         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25978         
25979         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25980         //ss['background-attachment'] = 'fixed'; // w3c
25981         dbody.bgProperties = 'fixed'; // ie
25982         //Roo.DomHelper.applyStyles(dbody, ss);
25983         Roo.EventManager.on(this.doc, {
25984             //'mousedown': this.onEditorEvent,
25985             'mouseup': this.onEditorEvent,
25986             'dblclick': this.onEditorEvent,
25987             'click': this.onEditorEvent,
25988             'keyup': this.onEditorEvent,
25989             buffer:100,
25990             scope: this
25991         });
25992         if(Roo.isGecko){
25993             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25994         }
25995         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25996             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25997         }
25998         this.initialized = true;
25999
26000         this.owner.fireEvent('initialize', this);
26001         this.pushValue();
26002     },
26003
26004     // private
26005     onDestroy : function(){
26006         
26007         
26008         
26009         if(this.rendered){
26010             
26011             //for (var i =0; i < this.toolbars.length;i++) {
26012             //    // fixme - ask toolbars for heights?
26013             //    this.toolbars[i].onDestroy();
26014            // }
26015             
26016             //this.wrap.dom.innerHTML = '';
26017             //this.wrap.remove();
26018         }
26019     },
26020
26021     // private
26022     onFirstFocus : function(){
26023         
26024         this.assignDocWin();
26025         
26026         
26027         this.activated = true;
26028          
26029     
26030         if(Roo.isGecko){ // prevent silly gecko errors
26031             this.win.focus();
26032             var s = this.win.getSelection();
26033             if(!s.focusNode || s.focusNode.nodeType != 3){
26034                 var r = s.getRangeAt(0);
26035                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26036                 r.collapse(true);
26037                 this.deferFocus();
26038             }
26039             try{
26040                 this.execCmd('useCSS', true);
26041                 this.execCmd('styleWithCSS', false);
26042             }catch(e){}
26043         }
26044         this.owner.fireEvent('activate', this);
26045     },
26046
26047     // private
26048     adjustFont: function(btn){
26049         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26050         //if(Roo.isSafari){ // safari
26051         //    adjust *= 2;
26052        // }
26053         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26054         if(Roo.isSafari){ // safari
26055             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26056             v =  (v < 10) ? 10 : v;
26057             v =  (v > 48) ? 48 : v;
26058             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26059             
26060         }
26061         
26062         
26063         v = Math.max(1, v+adjust);
26064         
26065         this.execCmd('FontSize', v  );
26066     },
26067
26068     onEditorEvent : function(e)
26069     {
26070         this.owner.fireEvent('editorevent', this, e);
26071       //  this.updateToolbar();
26072         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26073     },
26074
26075     insertTag : function(tg)
26076     {
26077         // could be a bit smarter... -> wrap the current selected tRoo..
26078         if (tg.toLowerCase() == 'span' ||
26079             tg.toLowerCase() == 'code' ||
26080             tg.toLowerCase() == 'sup' ||
26081             tg.toLowerCase() == 'sub' 
26082             ) {
26083             
26084             range = this.createRange(this.getSelection());
26085             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26086             wrappingNode.appendChild(range.extractContents());
26087             range.insertNode(wrappingNode);
26088
26089             return;
26090             
26091             
26092             
26093         }
26094         this.execCmd("formatblock",   tg);
26095         
26096     },
26097     
26098     insertText : function(txt)
26099     {
26100         
26101         
26102         var range = this.createRange();
26103         range.deleteContents();
26104                //alert(Sender.getAttribute('label'));
26105                
26106         range.insertNode(this.doc.createTextNode(txt));
26107     } ,
26108     
26109      
26110
26111     /**
26112      * Executes a Midas editor command on the editor document and performs necessary focus and
26113      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26114      * @param {String} cmd The Midas command
26115      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26116      */
26117     relayCmd : function(cmd, value){
26118         this.win.focus();
26119         this.execCmd(cmd, value);
26120         this.owner.fireEvent('editorevent', this);
26121         //this.updateToolbar();
26122         this.owner.deferFocus();
26123     },
26124
26125     /**
26126      * Executes a Midas editor command directly on the editor document.
26127      * For visual commands, you should use {@link #relayCmd} instead.
26128      * <b>This should only be called after the editor is initialized.</b>
26129      * @param {String} cmd The Midas command
26130      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26131      */
26132     execCmd : function(cmd, value){
26133         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26134         this.syncValue();
26135     },
26136  
26137  
26138    
26139     /**
26140      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26141      * to insert tRoo.
26142      * @param {String} text | dom node.. 
26143      */
26144     insertAtCursor : function(text)
26145     {
26146         
26147         if(!this.activated){
26148             return;
26149         }
26150         /*
26151         if(Roo.isIE){
26152             this.win.focus();
26153             var r = this.doc.selection.createRange();
26154             if(r){
26155                 r.collapse(true);
26156                 r.pasteHTML(text);
26157                 this.syncValue();
26158                 this.deferFocus();
26159             
26160             }
26161             return;
26162         }
26163         */
26164         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26165             this.win.focus();
26166             
26167             
26168             // from jquery ui (MIT licenced)
26169             var range, node;
26170             var win = this.win;
26171             
26172             if (win.getSelection && win.getSelection().getRangeAt) {
26173                 range = win.getSelection().getRangeAt(0);
26174                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26175                 range.insertNode(node);
26176             } else if (win.document.selection && win.document.selection.createRange) {
26177                 // no firefox support
26178                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26179                 win.document.selection.createRange().pasteHTML(txt);
26180             } else {
26181                 // no firefox support
26182                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26183                 this.execCmd('InsertHTML', txt);
26184             } 
26185             
26186             this.syncValue();
26187             
26188             this.deferFocus();
26189         }
26190     },
26191  // private
26192     mozKeyPress : function(e){
26193         if(e.ctrlKey){
26194             var c = e.getCharCode(), cmd;
26195           
26196             if(c > 0){
26197                 c = String.fromCharCode(c).toLowerCase();
26198                 switch(c){
26199                     case 'b':
26200                         cmd = 'bold';
26201                         break;
26202                     case 'i':
26203                         cmd = 'italic';
26204                         break;
26205                     
26206                     case 'u':
26207                         cmd = 'underline';
26208                         break;
26209                     
26210                     case 'v':
26211                         this.cleanUpPaste.defer(100, this);
26212                         return;
26213                         
26214                 }
26215                 if(cmd){
26216                     this.win.focus();
26217                     this.execCmd(cmd);
26218                     this.deferFocus();
26219                     e.preventDefault();
26220                 }
26221                 
26222             }
26223         }
26224     },
26225
26226     // private
26227     fixKeys : function(){ // load time branching for fastest keydown performance
26228         if(Roo.isIE){
26229             return function(e){
26230                 var k = e.getKey(), r;
26231                 if(k == e.TAB){
26232                     e.stopEvent();
26233                     r = this.doc.selection.createRange();
26234                     if(r){
26235                         r.collapse(true);
26236                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26237                         this.deferFocus();
26238                     }
26239                     return;
26240                 }
26241                 
26242                 if(k == e.ENTER){
26243                     r = this.doc.selection.createRange();
26244                     if(r){
26245                         var target = r.parentElement();
26246                         if(!target || target.tagName.toLowerCase() != 'li'){
26247                             e.stopEvent();
26248                             r.pasteHTML('<br />');
26249                             r.collapse(false);
26250                             r.select();
26251                         }
26252                     }
26253                 }
26254                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26255                     this.cleanUpPaste.defer(100, this);
26256                     return;
26257                 }
26258                 
26259                 
26260             };
26261         }else if(Roo.isOpera){
26262             return function(e){
26263                 var k = e.getKey();
26264                 if(k == e.TAB){
26265                     e.stopEvent();
26266                     this.win.focus();
26267                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26268                     this.deferFocus();
26269                 }
26270                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26271                     this.cleanUpPaste.defer(100, this);
26272                     return;
26273                 }
26274                 
26275             };
26276         }else if(Roo.isSafari){
26277             return function(e){
26278                 var k = e.getKey();
26279                 
26280                 if(k == e.TAB){
26281                     e.stopEvent();
26282                     this.execCmd('InsertText','\t');
26283                     this.deferFocus();
26284                     return;
26285                 }
26286                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26287                     this.cleanUpPaste.defer(100, this);
26288                     return;
26289                 }
26290                 
26291              };
26292         }
26293     }(),
26294     
26295     getAllAncestors: function()
26296     {
26297         var p = this.getSelectedNode();
26298         var a = [];
26299         if (!p) {
26300             a.push(p); // push blank onto stack..
26301             p = this.getParentElement();
26302         }
26303         
26304         
26305         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26306             a.push(p);
26307             p = p.parentNode;
26308         }
26309         a.push(this.doc.body);
26310         return a;
26311     },
26312     lastSel : false,
26313     lastSelNode : false,
26314     
26315     
26316     getSelection : function() 
26317     {
26318         this.assignDocWin();
26319         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26320     },
26321     
26322     getSelectedNode: function() 
26323     {
26324         // this may only work on Gecko!!!
26325         
26326         // should we cache this!!!!
26327         
26328         
26329         
26330          
26331         var range = this.createRange(this.getSelection()).cloneRange();
26332         
26333         if (Roo.isIE) {
26334             var parent = range.parentElement();
26335             while (true) {
26336                 var testRange = range.duplicate();
26337                 testRange.moveToElementText(parent);
26338                 if (testRange.inRange(range)) {
26339                     break;
26340                 }
26341                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26342                     break;
26343                 }
26344                 parent = parent.parentElement;
26345             }
26346             return parent;
26347         }
26348         
26349         // is ancestor a text element.
26350         var ac =  range.commonAncestorContainer;
26351         if (ac.nodeType == 3) {
26352             ac = ac.parentNode;
26353         }
26354         
26355         var ar = ac.childNodes;
26356          
26357         var nodes = [];
26358         var other_nodes = [];
26359         var has_other_nodes = false;
26360         for (var i=0;i<ar.length;i++) {
26361             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26362                 continue;
26363             }
26364             // fullly contained node.
26365             
26366             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26367                 nodes.push(ar[i]);
26368                 continue;
26369             }
26370             
26371             // probably selected..
26372             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26373                 other_nodes.push(ar[i]);
26374                 continue;
26375             }
26376             // outer..
26377             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26378                 continue;
26379             }
26380             
26381             
26382             has_other_nodes = true;
26383         }
26384         if (!nodes.length && other_nodes.length) {
26385             nodes= other_nodes;
26386         }
26387         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26388             return false;
26389         }
26390         
26391         return nodes[0];
26392     },
26393     createRange: function(sel)
26394     {
26395         // this has strange effects when using with 
26396         // top toolbar - not sure if it's a great idea.
26397         //this.editor.contentWindow.focus();
26398         if (typeof sel != "undefined") {
26399             try {
26400                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26401             } catch(e) {
26402                 return this.doc.createRange();
26403             }
26404         } else {
26405             return this.doc.createRange();
26406         }
26407     },
26408     getParentElement: function()
26409     {
26410         
26411         this.assignDocWin();
26412         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26413         
26414         var range = this.createRange(sel);
26415          
26416         try {
26417             var p = range.commonAncestorContainer;
26418             while (p.nodeType == 3) { // text node
26419                 p = p.parentNode;
26420             }
26421             return p;
26422         } catch (e) {
26423             return null;
26424         }
26425     
26426     },
26427     /***
26428      *
26429      * Range intersection.. the hard stuff...
26430      *  '-1' = before
26431      *  '0' = hits..
26432      *  '1' = after.
26433      *         [ -- selected range --- ]
26434      *   [fail]                        [fail]
26435      *
26436      *    basically..
26437      *      if end is before start or  hits it. fail.
26438      *      if start is after end or hits it fail.
26439      *
26440      *   if either hits (but other is outside. - then it's not 
26441      *   
26442      *    
26443      **/
26444     
26445     
26446     // @see http://www.thismuchiknow.co.uk/?p=64.
26447     rangeIntersectsNode : function(range, node)
26448     {
26449         var nodeRange = node.ownerDocument.createRange();
26450         try {
26451             nodeRange.selectNode(node);
26452         } catch (e) {
26453             nodeRange.selectNodeContents(node);
26454         }
26455     
26456         var rangeStartRange = range.cloneRange();
26457         rangeStartRange.collapse(true);
26458     
26459         var rangeEndRange = range.cloneRange();
26460         rangeEndRange.collapse(false);
26461     
26462         var nodeStartRange = nodeRange.cloneRange();
26463         nodeStartRange.collapse(true);
26464     
26465         var nodeEndRange = nodeRange.cloneRange();
26466         nodeEndRange.collapse(false);
26467     
26468         return rangeStartRange.compareBoundaryPoints(
26469                  Range.START_TO_START, nodeEndRange) == -1 &&
26470                rangeEndRange.compareBoundaryPoints(
26471                  Range.START_TO_START, nodeStartRange) == 1;
26472         
26473          
26474     },
26475     rangeCompareNode : function(range, node)
26476     {
26477         var nodeRange = node.ownerDocument.createRange();
26478         try {
26479             nodeRange.selectNode(node);
26480         } catch (e) {
26481             nodeRange.selectNodeContents(node);
26482         }
26483         
26484         
26485         range.collapse(true);
26486     
26487         nodeRange.collapse(true);
26488      
26489         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26490         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26491          
26492         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26493         
26494         var nodeIsBefore   =  ss == 1;
26495         var nodeIsAfter    = ee == -1;
26496         
26497         if (nodeIsBefore && nodeIsAfter) {
26498             return 0; // outer
26499         }
26500         if (!nodeIsBefore && nodeIsAfter) {
26501             return 1; //right trailed.
26502         }
26503         
26504         if (nodeIsBefore && !nodeIsAfter) {
26505             return 2;  // left trailed.
26506         }
26507         // fully contined.
26508         return 3;
26509     },
26510
26511     // private? - in a new class?
26512     cleanUpPaste :  function()
26513     {
26514         // cleans up the whole document..
26515         Roo.log('cleanuppaste');
26516         
26517         this.cleanUpChildren(this.doc.body);
26518         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26519         if (clean != this.doc.body.innerHTML) {
26520             this.doc.body.innerHTML = clean;
26521         }
26522         
26523     },
26524     
26525     cleanWordChars : function(input) {// change the chars to hex code
26526         var he = Roo.HtmlEditorCore;
26527         
26528         var output = input;
26529         Roo.each(he.swapCodes, function(sw) { 
26530             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26531             
26532             output = output.replace(swapper, sw[1]);
26533         });
26534         
26535         return output;
26536     },
26537     
26538     
26539     cleanUpChildren : function (n)
26540     {
26541         if (!n.childNodes.length) {
26542             return;
26543         }
26544         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26545            this.cleanUpChild(n.childNodes[i]);
26546         }
26547     },
26548     
26549     
26550         
26551     
26552     cleanUpChild : function (node)
26553     {
26554         var ed = this;
26555         //console.log(node);
26556         if (node.nodeName == "#text") {
26557             // clean up silly Windows -- stuff?
26558             return; 
26559         }
26560         if (node.nodeName == "#comment") {
26561             if (!this.allowComments) {
26562                 node.parentNode.removeChild(node);
26563             }
26564             // clean up silly Windows -- stuff?
26565             return; 
26566         }
26567         var lcname = node.tagName.toLowerCase();
26568         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26569         // whitelist of tags..
26570         
26571         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26572             // remove node.
26573             node.parentNode.removeChild(node);
26574             return;
26575             
26576         }
26577         
26578         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26579         
26580         // spans with no attributes - just remove them..
26581         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26582             remove_keep_children = true;
26583         }
26584         
26585         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26586         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26587         
26588         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26589         //    remove_keep_children = true;
26590         //}
26591         
26592         if (remove_keep_children) {
26593             this.cleanUpChildren(node);
26594             // inserts everything just before this node...
26595             while (node.childNodes.length) {
26596                 var cn = node.childNodes[0];
26597                 node.removeChild(cn);
26598                 node.parentNode.insertBefore(cn, node);
26599             }
26600             node.parentNode.removeChild(node);
26601             return;
26602         }
26603         
26604         if (!node.attributes || !node.attributes.length) {
26605             
26606           
26607             
26608             
26609             this.cleanUpChildren(node);
26610             return;
26611         }
26612         
26613         function cleanAttr(n,v)
26614         {
26615             
26616             if (v.match(/^\./) || v.match(/^\//)) {
26617                 return;
26618             }
26619             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26620                 return;
26621             }
26622             if (v.match(/^#/)) {
26623                 return;
26624             }
26625             if (v.match(/^\{/)) { // allow template editing.
26626                 return;
26627             }
26628 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26629             node.removeAttribute(n);
26630             
26631         }
26632         
26633         var cwhite = this.cwhite;
26634         var cblack = this.cblack;
26635             
26636         function cleanStyle(n,v)
26637         {
26638             if (v.match(/expression/)) { //XSS?? should we even bother..
26639                 node.removeAttribute(n);
26640                 return;
26641             }
26642             
26643             var parts = v.split(/;/);
26644             var clean = [];
26645             
26646             Roo.each(parts, function(p) {
26647                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26648                 if (!p.length) {
26649                     return true;
26650                 }
26651                 var l = p.split(':').shift().replace(/\s+/g,'');
26652                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26653                 
26654                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26655 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26656                     //node.removeAttribute(n);
26657                     return true;
26658                 }
26659                 //Roo.log()
26660                 // only allow 'c whitelisted system attributes'
26661                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26662 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26663                     //node.removeAttribute(n);
26664                     return true;
26665                 }
26666                 
26667                 
26668                  
26669                 
26670                 clean.push(p);
26671                 return true;
26672             });
26673             if (clean.length) { 
26674                 node.setAttribute(n, clean.join(';'));
26675             } else {
26676                 node.removeAttribute(n);
26677             }
26678             
26679         }
26680         
26681         
26682         for (var i = node.attributes.length-1; i > -1 ; i--) {
26683             var a = node.attributes[i];
26684             //console.log(a);
26685             
26686             if (a.name.toLowerCase().substr(0,2)=='on')  {
26687                 node.removeAttribute(a.name);
26688                 continue;
26689             }
26690             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26691                 node.removeAttribute(a.name);
26692                 continue;
26693             }
26694             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26695                 cleanAttr(a.name,a.value); // fixme..
26696                 continue;
26697             }
26698             if (a.name == 'style') {
26699                 cleanStyle(a.name,a.value);
26700                 continue;
26701             }
26702             /// clean up MS crap..
26703             // tecnically this should be a list of valid class'es..
26704             
26705             
26706             if (a.name == 'class') {
26707                 if (a.value.match(/^Mso/)) {
26708                     node.removeAttribute('class');
26709                 }
26710                 
26711                 if (a.value.match(/^body$/)) {
26712                     node.removeAttribute('class');
26713                 }
26714                 continue;
26715             }
26716             
26717             // style cleanup!?
26718             // class cleanup?
26719             
26720         }
26721         
26722         
26723         this.cleanUpChildren(node);
26724         
26725         
26726     },
26727     
26728     /**
26729      * Clean up MS wordisms...
26730      */
26731     cleanWord : function(node)
26732     {
26733         if (!node) {
26734             this.cleanWord(this.doc.body);
26735             return;
26736         }
26737         
26738         if(
26739                 node.nodeName == 'SPAN' &&
26740                 !node.hasAttributes() &&
26741                 node.childNodes.length == 1 &&
26742                 node.firstChild.nodeName == "#text"  
26743         ) {
26744             var textNode = node.firstChild;
26745             node.removeChild(textNode);
26746             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26747                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26748             }
26749             node.parentNode.insertBefore(textNode, node);
26750             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26751                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26752             }
26753             node.parentNode.removeChild(node);
26754         }
26755         
26756         if (node.nodeName == "#text") {
26757             // clean up silly Windows -- stuff?
26758             return; 
26759         }
26760         if (node.nodeName == "#comment") {
26761             node.parentNode.removeChild(node);
26762             // clean up silly Windows -- stuff?
26763             return; 
26764         }
26765         
26766         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26767             node.parentNode.removeChild(node);
26768             return;
26769         }
26770         //Roo.log(node.tagName);
26771         // remove - but keep children..
26772         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26773             //Roo.log('-- removed');
26774             while (node.childNodes.length) {
26775                 var cn = node.childNodes[0];
26776                 node.removeChild(cn);
26777                 node.parentNode.insertBefore(cn, node);
26778                 // move node to parent - and clean it..
26779                 this.cleanWord(cn);
26780             }
26781             node.parentNode.removeChild(node);
26782             /// no need to iterate chidlren = it's got none..
26783             //this.iterateChildren(node, this.cleanWord);
26784             return;
26785         }
26786         // clean styles
26787         if (node.className.length) {
26788             
26789             var cn = node.className.split(/\W+/);
26790             var cna = [];
26791             Roo.each(cn, function(cls) {
26792                 if (cls.match(/Mso[a-zA-Z]+/)) {
26793                     return;
26794                 }
26795                 cna.push(cls);
26796             });
26797             node.className = cna.length ? cna.join(' ') : '';
26798             if (!cna.length) {
26799                 node.removeAttribute("class");
26800             }
26801         }
26802         
26803         if (node.hasAttribute("lang")) {
26804             node.removeAttribute("lang");
26805         }
26806         
26807         if (node.hasAttribute("style")) {
26808             
26809             var styles = node.getAttribute("style").split(";");
26810             var nstyle = [];
26811             Roo.each(styles, function(s) {
26812                 if (!s.match(/:/)) {
26813                     return;
26814                 }
26815                 var kv = s.split(":");
26816                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26817                     return;
26818                 }
26819                 // what ever is left... we allow.
26820                 nstyle.push(s);
26821             });
26822             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26823             if (!nstyle.length) {
26824                 node.removeAttribute('style');
26825             }
26826         }
26827         this.iterateChildren(node, this.cleanWord);
26828         
26829         
26830         
26831     },
26832     /**
26833      * iterateChildren of a Node, calling fn each time, using this as the scole..
26834      * @param {DomNode} node node to iterate children of.
26835      * @param {Function} fn method of this class to call on each item.
26836      */
26837     iterateChildren : function(node, fn)
26838     {
26839         if (!node.childNodes.length) {
26840                 return;
26841         }
26842         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26843            fn.call(this, node.childNodes[i])
26844         }
26845     },
26846     
26847     
26848     /**
26849      * cleanTableWidths.
26850      *
26851      * Quite often pasting from word etc.. results in tables with column and widths.
26852      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26853      *
26854      */
26855     cleanTableWidths : function(node)
26856     {
26857          
26858          
26859         if (!node) {
26860             this.cleanTableWidths(this.doc.body);
26861             return;
26862         }
26863         
26864         // ignore list...
26865         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26866             return; 
26867         }
26868         Roo.log(node.tagName);
26869         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26870             this.iterateChildren(node, this.cleanTableWidths);
26871             return;
26872         }
26873         if (node.hasAttribute('width')) {
26874             node.removeAttribute('width');
26875         }
26876         
26877          
26878         if (node.hasAttribute("style")) {
26879             // pretty basic...
26880             
26881             var styles = node.getAttribute("style").split(";");
26882             var nstyle = [];
26883             Roo.each(styles, function(s) {
26884                 if (!s.match(/:/)) {
26885                     return;
26886                 }
26887                 var kv = s.split(":");
26888                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26889                     return;
26890                 }
26891                 // what ever is left... we allow.
26892                 nstyle.push(s);
26893             });
26894             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26895             if (!nstyle.length) {
26896                 node.removeAttribute('style');
26897             }
26898         }
26899         
26900         this.iterateChildren(node, this.cleanTableWidths);
26901         
26902         
26903     },
26904     
26905     
26906     
26907     
26908     domToHTML : function(currentElement, depth, nopadtext) {
26909         
26910         depth = depth || 0;
26911         nopadtext = nopadtext || false;
26912     
26913         if (!currentElement) {
26914             return this.domToHTML(this.doc.body);
26915         }
26916         
26917         //Roo.log(currentElement);
26918         var j;
26919         var allText = false;
26920         var nodeName = currentElement.nodeName;
26921         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26922         
26923         if  (nodeName == '#text') {
26924             
26925             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26926         }
26927         
26928         
26929         var ret = '';
26930         if (nodeName != 'BODY') {
26931              
26932             var i = 0;
26933             // Prints the node tagName, such as <A>, <IMG>, etc
26934             if (tagName) {
26935                 var attr = [];
26936                 for(i = 0; i < currentElement.attributes.length;i++) {
26937                     // quoting?
26938                     var aname = currentElement.attributes.item(i).name;
26939                     if (!currentElement.attributes.item(i).value.length) {
26940                         continue;
26941                     }
26942                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26943                 }
26944                 
26945                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26946             } 
26947             else {
26948                 
26949                 // eack
26950             }
26951         } else {
26952             tagName = false;
26953         }
26954         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26955             return ret;
26956         }
26957         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26958             nopadtext = true;
26959         }
26960         
26961         
26962         // Traverse the tree
26963         i = 0;
26964         var currentElementChild = currentElement.childNodes.item(i);
26965         var allText = true;
26966         var innerHTML  = '';
26967         lastnode = '';
26968         while (currentElementChild) {
26969             // Formatting code (indent the tree so it looks nice on the screen)
26970             var nopad = nopadtext;
26971             if (lastnode == 'SPAN') {
26972                 nopad  = true;
26973             }
26974             // text
26975             if  (currentElementChild.nodeName == '#text') {
26976                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26977                 toadd = nopadtext ? toadd : toadd.trim();
26978                 if (!nopad && toadd.length > 80) {
26979                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26980                 }
26981                 innerHTML  += toadd;
26982                 
26983                 i++;
26984                 currentElementChild = currentElement.childNodes.item(i);
26985                 lastNode = '';
26986                 continue;
26987             }
26988             allText = false;
26989             
26990             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26991                 
26992             // Recursively traverse the tree structure of the child node
26993             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26994             lastnode = currentElementChild.nodeName;
26995             i++;
26996             currentElementChild=currentElement.childNodes.item(i);
26997         }
26998         
26999         ret += innerHTML;
27000         
27001         if (!allText) {
27002                 // The remaining code is mostly for formatting the tree
27003             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27004         }
27005         
27006         
27007         if (tagName) {
27008             ret+= "</"+tagName+">";
27009         }
27010         return ret;
27011         
27012     },
27013         
27014     applyBlacklists : function()
27015     {
27016         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27017         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27018         
27019         this.white = [];
27020         this.black = [];
27021         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27022             if (b.indexOf(tag) > -1) {
27023                 return;
27024             }
27025             this.white.push(tag);
27026             
27027         }, this);
27028         
27029         Roo.each(w, function(tag) {
27030             if (b.indexOf(tag) > -1) {
27031                 return;
27032             }
27033             if (this.white.indexOf(tag) > -1) {
27034                 return;
27035             }
27036             this.white.push(tag);
27037             
27038         }, this);
27039         
27040         
27041         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27042             if (w.indexOf(tag) > -1) {
27043                 return;
27044             }
27045             this.black.push(tag);
27046             
27047         }, this);
27048         
27049         Roo.each(b, function(tag) {
27050             if (w.indexOf(tag) > -1) {
27051                 return;
27052             }
27053             if (this.black.indexOf(tag) > -1) {
27054                 return;
27055             }
27056             this.black.push(tag);
27057             
27058         }, this);
27059         
27060         
27061         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27062         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27063         
27064         this.cwhite = [];
27065         this.cblack = [];
27066         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27067             if (b.indexOf(tag) > -1) {
27068                 return;
27069             }
27070             this.cwhite.push(tag);
27071             
27072         }, this);
27073         
27074         Roo.each(w, function(tag) {
27075             if (b.indexOf(tag) > -1) {
27076                 return;
27077             }
27078             if (this.cwhite.indexOf(tag) > -1) {
27079                 return;
27080             }
27081             this.cwhite.push(tag);
27082             
27083         }, this);
27084         
27085         
27086         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27087             if (w.indexOf(tag) > -1) {
27088                 return;
27089             }
27090             this.cblack.push(tag);
27091             
27092         }, this);
27093         
27094         Roo.each(b, function(tag) {
27095             if (w.indexOf(tag) > -1) {
27096                 return;
27097             }
27098             if (this.cblack.indexOf(tag) > -1) {
27099                 return;
27100             }
27101             this.cblack.push(tag);
27102             
27103         }, this);
27104     },
27105     
27106     setStylesheets : function(stylesheets)
27107     {
27108         if(typeof(stylesheets) == 'string'){
27109             Roo.get(this.iframe.contentDocument.head).createChild({
27110                 tag : 'link',
27111                 rel : 'stylesheet',
27112                 type : 'text/css',
27113                 href : stylesheets
27114             });
27115             
27116             return;
27117         }
27118         var _this = this;
27119      
27120         Roo.each(stylesheets, function(s) {
27121             if(!s.length){
27122                 return;
27123             }
27124             
27125             Roo.get(_this.iframe.contentDocument.head).createChild({
27126                 tag : 'link',
27127                 rel : 'stylesheet',
27128                 type : 'text/css',
27129                 href : s
27130             });
27131         });
27132
27133         
27134     },
27135     
27136     removeStylesheets : function()
27137     {
27138         var _this = this;
27139         
27140         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27141             s.remove();
27142         });
27143     },
27144     
27145     setStyle : function(style)
27146     {
27147         Roo.get(this.iframe.contentDocument.head).createChild({
27148             tag : 'style',
27149             type : 'text/css',
27150             html : style
27151         });
27152
27153         return;
27154     }
27155     
27156     // hide stuff that is not compatible
27157     /**
27158      * @event blur
27159      * @hide
27160      */
27161     /**
27162      * @event change
27163      * @hide
27164      */
27165     /**
27166      * @event focus
27167      * @hide
27168      */
27169     /**
27170      * @event specialkey
27171      * @hide
27172      */
27173     /**
27174      * @cfg {String} fieldClass @hide
27175      */
27176     /**
27177      * @cfg {String} focusClass @hide
27178      */
27179     /**
27180      * @cfg {String} autoCreate @hide
27181      */
27182     /**
27183      * @cfg {String} inputType @hide
27184      */
27185     /**
27186      * @cfg {String} invalidClass @hide
27187      */
27188     /**
27189      * @cfg {String} invalidText @hide
27190      */
27191     /**
27192      * @cfg {String} msgFx @hide
27193      */
27194     /**
27195      * @cfg {String} validateOnBlur @hide
27196      */
27197 });
27198
27199 Roo.HtmlEditorCore.white = [
27200         'area', 'br', 'img', 'input', 'hr', 'wbr',
27201         
27202        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27203        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27204        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27205        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27206        'table',   'ul',         'xmp', 
27207        
27208        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27209       'thead',   'tr', 
27210      
27211       'dir', 'menu', 'ol', 'ul', 'dl',
27212        
27213       'embed',  'object'
27214 ];
27215
27216
27217 Roo.HtmlEditorCore.black = [
27218     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27219         'applet', // 
27220         'base',   'basefont', 'bgsound', 'blink',  'body', 
27221         'frame',  'frameset', 'head',    'html',   'ilayer', 
27222         'iframe', 'layer',  'link',     'meta',    'object',   
27223         'script', 'style' ,'title',  'xml' // clean later..
27224 ];
27225 Roo.HtmlEditorCore.clean = [
27226     'script', 'style', 'title', 'xml'
27227 ];
27228 Roo.HtmlEditorCore.remove = [
27229     'font'
27230 ];
27231 // attributes..
27232
27233 Roo.HtmlEditorCore.ablack = [
27234     'on'
27235 ];
27236     
27237 Roo.HtmlEditorCore.aclean = [ 
27238     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27239 ];
27240
27241 // protocols..
27242 Roo.HtmlEditorCore.pwhite= [
27243         'http',  'https',  'mailto'
27244 ];
27245
27246 // white listed style attributes.
27247 Roo.HtmlEditorCore.cwhite= [
27248       //  'text-align', /// default is to allow most things..
27249       
27250          
27251 //        'font-size'//??
27252 ];
27253
27254 // black listed style attributes.
27255 Roo.HtmlEditorCore.cblack= [
27256       //  'font-size' -- this can be set by the project 
27257 ];
27258
27259
27260 Roo.HtmlEditorCore.swapCodes   =[ 
27261     [    8211, "&#8211;" ], 
27262     [    8212, "&#8212;" ], 
27263     [    8216,  "'" ],  
27264     [    8217, "'" ],  
27265     [    8220, '"' ],  
27266     [    8221, '"' ],  
27267     [    8226, "*" ],  
27268     [    8230, "..." ]
27269 ]; 
27270
27271     /*
27272  * - LGPL
27273  *
27274  * HtmlEditor
27275  * 
27276  */
27277
27278 /**
27279  * @class Roo.bootstrap.HtmlEditor
27280  * @extends Roo.bootstrap.TextArea
27281  * Bootstrap HtmlEditor class
27282
27283  * @constructor
27284  * Create a new HtmlEditor
27285  * @param {Object} config The config object
27286  */
27287
27288 Roo.bootstrap.HtmlEditor = function(config){
27289     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27290     if (!this.toolbars) {
27291         this.toolbars = [];
27292     }
27293     
27294     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27295     this.addEvents({
27296             /**
27297              * @event initialize
27298              * Fires when the editor is fully initialized (including the iframe)
27299              * @param {HtmlEditor} this
27300              */
27301             initialize: true,
27302             /**
27303              * @event activate
27304              * Fires when the editor is first receives the focus. Any insertion must wait
27305              * until after this event.
27306              * @param {HtmlEditor} this
27307              */
27308             activate: true,
27309              /**
27310              * @event beforesync
27311              * Fires before the textarea is updated with content from the editor iframe. Return false
27312              * to cancel the sync.
27313              * @param {HtmlEditor} this
27314              * @param {String} html
27315              */
27316             beforesync: true,
27317              /**
27318              * @event beforepush
27319              * Fires before the iframe editor is updated with content from the textarea. Return false
27320              * to cancel the push.
27321              * @param {HtmlEditor} this
27322              * @param {String} html
27323              */
27324             beforepush: true,
27325              /**
27326              * @event sync
27327              * Fires when the textarea is updated with content from the editor iframe.
27328              * @param {HtmlEditor} this
27329              * @param {String} html
27330              */
27331             sync: true,
27332              /**
27333              * @event push
27334              * Fires when the iframe editor is updated with content from the textarea.
27335              * @param {HtmlEditor} this
27336              * @param {String} html
27337              */
27338             push: true,
27339              /**
27340              * @event editmodechange
27341              * Fires when the editor switches edit modes
27342              * @param {HtmlEditor} this
27343              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27344              */
27345             editmodechange: true,
27346             /**
27347              * @event editorevent
27348              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27349              * @param {HtmlEditor} this
27350              */
27351             editorevent: true,
27352             /**
27353              * @event firstfocus
27354              * Fires when on first focus - needed by toolbars..
27355              * @param {HtmlEditor} this
27356              */
27357             firstfocus: true,
27358             /**
27359              * @event autosave
27360              * Auto save the htmlEditor value as a file into Events
27361              * @param {HtmlEditor} this
27362              */
27363             autosave: true,
27364             /**
27365              * @event savedpreview
27366              * preview the saved version of htmlEditor
27367              * @param {HtmlEditor} this
27368              */
27369             savedpreview: true
27370         });
27371 };
27372
27373
27374 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27375     
27376     
27377       /**
27378      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27379      */
27380     toolbars : false,
27381     
27382      /**
27383     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27384     */
27385     btns : [],
27386    
27387      /**
27388      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27389      *                        Roo.resizable.
27390      */
27391     resizable : false,
27392      /**
27393      * @cfg {Number} height (in pixels)
27394      */   
27395     height: 300,
27396    /**
27397      * @cfg {Number} width (in pixels)
27398      */   
27399     width: false,
27400     
27401     /**
27402      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27403      * 
27404      */
27405     stylesheets: false,
27406     
27407     // id of frame..
27408     frameId: false,
27409     
27410     // private properties
27411     validationEvent : false,
27412     deferHeight: true,
27413     initialized : false,
27414     activated : false,
27415     
27416     onFocus : Roo.emptyFn,
27417     iframePad:3,
27418     hideMode:'offsets',
27419     
27420     tbContainer : false,
27421     
27422     bodyCls : '',
27423     
27424     toolbarContainer :function() {
27425         return this.wrap.select('.x-html-editor-tb',true).first();
27426     },
27427
27428     /**
27429      * Protected method that will not generally be called directly. It
27430      * is called when the editor creates its toolbar. Override this method if you need to
27431      * add custom toolbar buttons.
27432      * @param {HtmlEditor} editor
27433      */
27434     createToolbar : function(){
27435         Roo.log('renewing');
27436         Roo.log("create toolbars");
27437         
27438         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27439         this.toolbars[0].render(this.toolbarContainer());
27440         
27441         return;
27442         
27443 //        if (!editor.toolbars || !editor.toolbars.length) {
27444 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27445 //        }
27446 //        
27447 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27448 //            editor.toolbars[i] = Roo.factory(
27449 //                    typeof(editor.toolbars[i]) == 'string' ?
27450 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27451 //                Roo.bootstrap.HtmlEditor);
27452 //            editor.toolbars[i].init(editor);
27453 //        }
27454     },
27455
27456      
27457     // private
27458     onRender : function(ct, position)
27459     {
27460        // Roo.log("Call onRender: " + this.xtype);
27461         var _t = this;
27462         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27463       
27464         this.wrap = this.inputEl().wrap({
27465             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27466         });
27467         
27468         this.editorcore.onRender(ct, position);
27469          
27470         if (this.resizable) {
27471             this.resizeEl = new Roo.Resizable(this.wrap, {
27472                 pinned : true,
27473                 wrap: true,
27474                 dynamic : true,
27475                 minHeight : this.height,
27476                 height: this.height,
27477                 handles : this.resizable,
27478                 width: this.width,
27479                 listeners : {
27480                     resize : function(r, w, h) {
27481                         _t.onResize(w,h); // -something
27482                     }
27483                 }
27484             });
27485             
27486         }
27487         this.createToolbar(this);
27488        
27489         
27490         if(!this.width && this.resizable){
27491             this.setSize(this.wrap.getSize());
27492         }
27493         if (this.resizeEl) {
27494             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27495             // should trigger onReize..
27496         }
27497         
27498     },
27499
27500     // private
27501     onResize : function(w, h)
27502     {
27503         Roo.log('resize: ' +w + ',' + h );
27504         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27505         var ew = false;
27506         var eh = false;
27507         
27508         if(this.inputEl() ){
27509             if(typeof w == 'number'){
27510                 var aw = w - this.wrap.getFrameWidth('lr');
27511                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27512                 ew = aw;
27513             }
27514             if(typeof h == 'number'){
27515                  var tbh = -11;  // fixme it needs to tool bar size!
27516                 for (var i =0; i < this.toolbars.length;i++) {
27517                     // fixme - ask toolbars for heights?
27518                     tbh += this.toolbars[i].el.getHeight();
27519                     //if (this.toolbars[i].footer) {
27520                     //    tbh += this.toolbars[i].footer.el.getHeight();
27521                     //}
27522                 }
27523               
27524                 
27525                 
27526                 
27527                 
27528                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27529                 ah -= 5; // knock a few pixes off for look..
27530                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27531                 var eh = ah;
27532             }
27533         }
27534         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27535         this.editorcore.onResize(ew,eh);
27536         
27537     },
27538
27539     /**
27540      * Toggles the editor between standard and source edit mode.
27541      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27542      */
27543     toggleSourceEdit : function(sourceEditMode)
27544     {
27545         this.editorcore.toggleSourceEdit(sourceEditMode);
27546         
27547         if(this.editorcore.sourceEditMode){
27548             Roo.log('editor - showing textarea');
27549             
27550 //            Roo.log('in');
27551 //            Roo.log(this.syncValue());
27552             this.syncValue();
27553             this.inputEl().removeClass(['hide', 'x-hidden']);
27554             this.inputEl().dom.removeAttribute('tabIndex');
27555             this.inputEl().focus();
27556         }else{
27557             Roo.log('editor - hiding textarea');
27558 //            Roo.log('out')
27559 //            Roo.log(this.pushValue()); 
27560             this.pushValue();
27561             
27562             this.inputEl().addClass(['hide', 'x-hidden']);
27563             this.inputEl().dom.setAttribute('tabIndex', -1);
27564             //this.deferFocus();
27565         }
27566          
27567         if(this.resizable){
27568             this.setSize(this.wrap.getSize());
27569         }
27570         
27571         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27572     },
27573  
27574     // private (for BoxComponent)
27575     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27576
27577     // private (for BoxComponent)
27578     getResizeEl : function(){
27579         return this.wrap;
27580     },
27581
27582     // private (for BoxComponent)
27583     getPositionEl : function(){
27584         return this.wrap;
27585     },
27586
27587     // private
27588     initEvents : function(){
27589         this.originalValue = this.getValue();
27590     },
27591
27592 //    /**
27593 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27594 //     * @method
27595 //     */
27596 //    markInvalid : Roo.emptyFn,
27597 //    /**
27598 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27599 //     * @method
27600 //     */
27601 //    clearInvalid : Roo.emptyFn,
27602
27603     setValue : function(v){
27604         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27605         this.editorcore.pushValue();
27606     },
27607
27608      
27609     // private
27610     deferFocus : function(){
27611         this.focus.defer(10, this);
27612     },
27613
27614     // doc'ed in Field
27615     focus : function(){
27616         this.editorcore.focus();
27617         
27618     },
27619       
27620
27621     // private
27622     onDestroy : function(){
27623         
27624         
27625         
27626         if(this.rendered){
27627             
27628             for (var i =0; i < this.toolbars.length;i++) {
27629                 // fixme - ask toolbars for heights?
27630                 this.toolbars[i].onDestroy();
27631             }
27632             
27633             this.wrap.dom.innerHTML = '';
27634             this.wrap.remove();
27635         }
27636     },
27637
27638     // private
27639     onFirstFocus : function(){
27640         //Roo.log("onFirstFocus");
27641         this.editorcore.onFirstFocus();
27642          for (var i =0; i < this.toolbars.length;i++) {
27643             this.toolbars[i].onFirstFocus();
27644         }
27645         
27646     },
27647     
27648     // private
27649     syncValue : function()
27650     {   
27651         this.editorcore.syncValue();
27652     },
27653     
27654     pushValue : function()
27655     {   
27656         this.editorcore.pushValue();
27657     }
27658      
27659     
27660     // hide stuff that is not compatible
27661     /**
27662      * @event blur
27663      * @hide
27664      */
27665     /**
27666      * @event change
27667      * @hide
27668      */
27669     /**
27670      * @event focus
27671      * @hide
27672      */
27673     /**
27674      * @event specialkey
27675      * @hide
27676      */
27677     /**
27678      * @cfg {String} fieldClass @hide
27679      */
27680     /**
27681      * @cfg {String} focusClass @hide
27682      */
27683     /**
27684      * @cfg {String} autoCreate @hide
27685      */
27686     /**
27687      * @cfg {String} inputType @hide
27688      */
27689      
27690     /**
27691      * @cfg {String} invalidText @hide
27692      */
27693     /**
27694      * @cfg {String} msgFx @hide
27695      */
27696     /**
27697      * @cfg {String} validateOnBlur @hide
27698      */
27699 });
27700  
27701     
27702    
27703    
27704    
27705       
27706 Roo.namespace('Roo.bootstrap.htmleditor');
27707 /**
27708  * @class Roo.bootstrap.HtmlEditorToolbar1
27709  * Basic Toolbar
27710  * 
27711  * @example
27712  * Usage:
27713  *
27714  new Roo.bootstrap.HtmlEditor({
27715     ....
27716     toolbars : [
27717         new Roo.bootstrap.HtmlEditorToolbar1({
27718             disable : { fonts: 1 , format: 1, ..., ... , ...],
27719             btns : [ .... ]
27720         })
27721     }
27722      
27723  * 
27724  * @cfg {Object} disable List of elements to disable..
27725  * @cfg {Array} btns List of additional buttons.
27726  * 
27727  * 
27728  * NEEDS Extra CSS? 
27729  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27730  */
27731  
27732 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27733 {
27734     
27735     Roo.apply(this, config);
27736     
27737     // default disabled, based on 'good practice'..
27738     this.disable = this.disable || {};
27739     Roo.applyIf(this.disable, {
27740         fontSize : true,
27741         colors : true,
27742         specialElements : true
27743     });
27744     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27745     
27746     this.editor = config.editor;
27747     this.editorcore = config.editor.editorcore;
27748     
27749     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27750     
27751     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27752     // dont call parent... till later.
27753 }
27754 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27755      
27756     bar : true,
27757     
27758     editor : false,
27759     editorcore : false,
27760     
27761     
27762     formats : [
27763         "p" ,  
27764         "h1","h2","h3","h4","h5","h6", 
27765         "pre", "code", 
27766         "abbr", "acronym", "address", "cite", "samp", "var",
27767         'div','span'
27768     ],
27769     
27770     onRender : function(ct, position)
27771     {
27772        // Roo.log("Call onRender: " + this.xtype);
27773         
27774        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27775        Roo.log(this.el);
27776        this.el.dom.style.marginBottom = '0';
27777        var _this = this;
27778        var editorcore = this.editorcore;
27779        var editor= this.editor;
27780        
27781        var children = [];
27782        var btn = function(id,cmd , toggle, handler, html){
27783        
27784             var  event = toggle ? 'toggle' : 'click';
27785        
27786             var a = {
27787                 size : 'sm',
27788                 xtype: 'Button',
27789                 xns: Roo.bootstrap,
27790                 //glyphicon : id,
27791                 fa: id,
27792                 cmd : id || cmd,
27793                 enableToggle:toggle !== false,
27794                 html : html || '',
27795                 pressed : toggle ? false : null,
27796                 listeners : {}
27797             };
27798             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27799                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27800             };
27801             children.push(a);
27802             return a;
27803        }
27804        
27805     //    var cb_box = function...
27806         
27807         var style = {
27808                 xtype: 'Button',
27809                 size : 'sm',
27810                 xns: Roo.bootstrap,
27811                 fa : 'font',
27812                 //html : 'submit'
27813                 menu : {
27814                     xtype: 'Menu',
27815                     xns: Roo.bootstrap,
27816                     items:  []
27817                 }
27818         };
27819         Roo.each(this.formats, function(f) {
27820             style.menu.items.push({
27821                 xtype :'MenuItem',
27822                 xns: Roo.bootstrap,
27823                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27824                 tagname : f,
27825                 listeners : {
27826                     click : function()
27827                     {
27828                         editorcore.insertTag(this.tagname);
27829                         editor.focus();
27830                     }
27831                 }
27832                 
27833             });
27834         });
27835         children.push(style);   
27836         
27837         btn('bold',false,true);
27838         btn('italic',false,true);
27839         btn('align-left', 'justifyleft',true);
27840         btn('align-center', 'justifycenter',true);
27841         btn('align-right' , 'justifyright',true);
27842         btn('link', false, false, function(btn) {
27843             //Roo.log("create link?");
27844             var url = prompt(this.createLinkText, this.defaultLinkValue);
27845             if(url && url != 'http:/'+'/'){
27846                 this.editorcore.relayCmd('createlink', url);
27847             }
27848         }),
27849         btn('list','insertunorderedlist',true);
27850         btn('pencil', false,true, function(btn){
27851                 Roo.log(this);
27852                 this.toggleSourceEdit(btn.pressed);
27853         });
27854         
27855         if (this.editor.btns.length > 0) {
27856             for (var i = 0; i<this.editor.btns.length; i++) {
27857                 children.push(this.editor.btns[i]);
27858             }
27859         }
27860         
27861         /*
27862         var cog = {
27863                 xtype: 'Button',
27864                 size : 'sm',
27865                 xns: Roo.bootstrap,
27866                 glyphicon : 'cog',
27867                 //html : 'submit'
27868                 menu : {
27869                     xtype: 'Menu',
27870                     xns: Roo.bootstrap,
27871                     items:  []
27872                 }
27873         };
27874         
27875         cog.menu.items.push({
27876             xtype :'MenuItem',
27877             xns: Roo.bootstrap,
27878             html : Clean styles,
27879             tagname : f,
27880             listeners : {
27881                 click : function()
27882                 {
27883                     editorcore.insertTag(this.tagname);
27884                     editor.focus();
27885                 }
27886             }
27887             
27888         });
27889        */
27890         
27891          
27892        this.xtype = 'NavSimplebar';
27893         
27894         for(var i=0;i< children.length;i++) {
27895             
27896             this.buttons.add(this.addxtypeChild(children[i]));
27897             
27898         }
27899         
27900         editor.on('editorevent', this.updateToolbar, this);
27901     },
27902     onBtnClick : function(id)
27903     {
27904        this.editorcore.relayCmd(id);
27905        this.editorcore.focus();
27906     },
27907     
27908     /**
27909      * Protected method that will not generally be called directly. It triggers
27910      * a toolbar update by reading the markup state of the current selection in the editor.
27911      */
27912     updateToolbar: function(){
27913
27914         if(!this.editorcore.activated){
27915             this.editor.onFirstFocus(); // is this neeed?
27916             return;
27917         }
27918
27919         var btns = this.buttons; 
27920         var doc = this.editorcore.doc;
27921         btns.get('bold').setActive(doc.queryCommandState('bold'));
27922         btns.get('italic').setActive(doc.queryCommandState('italic'));
27923         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27924         
27925         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27926         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27927         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27928         
27929         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27930         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27931          /*
27932         
27933         var ans = this.editorcore.getAllAncestors();
27934         if (this.formatCombo) {
27935             
27936             
27937             var store = this.formatCombo.store;
27938             this.formatCombo.setValue("");
27939             for (var i =0; i < ans.length;i++) {
27940                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27941                     // select it..
27942                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27943                     break;
27944                 }
27945             }
27946         }
27947         
27948         
27949         
27950         // hides menus... - so this cant be on a menu...
27951         Roo.bootstrap.MenuMgr.hideAll();
27952         */
27953         Roo.bootstrap.MenuMgr.hideAll();
27954         //this.editorsyncValue();
27955     },
27956     onFirstFocus: function() {
27957         this.buttons.each(function(item){
27958            item.enable();
27959         });
27960     },
27961     toggleSourceEdit : function(sourceEditMode){
27962         
27963           
27964         if(sourceEditMode){
27965             Roo.log("disabling buttons");
27966            this.buttons.each( function(item){
27967                 if(item.cmd != 'pencil'){
27968                     item.disable();
27969                 }
27970             });
27971           
27972         }else{
27973             Roo.log("enabling buttons");
27974             if(this.editorcore.initialized){
27975                 this.buttons.each( function(item){
27976                     item.enable();
27977                 });
27978             }
27979             
27980         }
27981         Roo.log("calling toggole on editor");
27982         // tell the editor that it's been pressed..
27983         this.editor.toggleSourceEdit(sourceEditMode);
27984        
27985     }
27986 });
27987
27988
27989
27990
27991  
27992 /*
27993  * - LGPL
27994  */
27995
27996 /**
27997  * @class Roo.bootstrap.Markdown
27998  * @extends Roo.bootstrap.TextArea
27999  * Bootstrap Showdown editable area
28000  * @cfg {string} content
28001  * 
28002  * @constructor
28003  * Create a new Showdown
28004  */
28005
28006 Roo.bootstrap.Markdown = function(config){
28007     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28008    
28009 };
28010
28011 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28012     
28013     editing :false,
28014     
28015     initEvents : function()
28016     {
28017         
28018         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28019         this.markdownEl = this.el.createChild({
28020             cls : 'roo-markdown-area'
28021         });
28022         this.inputEl().addClass('d-none');
28023         if (this.getValue() == '') {
28024             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28025             
28026         } else {
28027             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28028         }
28029         this.markdownEl.on('click', this.toggleTextEdit, this);
28030         this.on('blur', this.toggleTextEdit, this);
28031         this.on('specialkey', this.resizeTextArea, this);
28032     },
28033     
28034     toggleTextEdit : function()
28035     {
28036         var sh = this.markdownEl.getHeight();
28037         this.inputEl().addClass('d-none');
28038         this.markdownEl.addClass('d-none');
28039         if (!this.editing) {
28040             // show editor?
28041             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28042             this.inputEl().removeClass('d-none');
28043             this.inputEl().focus();
28044             this.editing = true;
28045             return;
28046         }
28047         // show showdown...
28048         this.updateMarkdown();
28049         this.markdownEl.removeClass('d-none');
28050         this.editing = false;
28051         return;
28052     },
28053     updateMarkdown : function()
28054     {
28055         if (this.getValue() == '') {
28056             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28057             return;
28058         }
28059  
28060         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28061     },
28062     
28063     resizeTextArea: function () {
28064         
28065         var sh = 100;
28066         Roo.log([sh, this.getValue().split("\n").length * 30]);
28067         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28068     },
28069     setValue : function(val)
28070     {
28071         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28072         if (!this.editing) {
28073             this.updateMarkdown();
28074         }
28075         
28076     },
28077     focus : function()
28078     {
28079         if (!this.editing) {
28080             this.toggleTextEdit();
28081         }
28082         
28083     }
28084
28085
28086 });/*
28087  * Based on:
28088  * Ext JS Library 1.1.1
28089  * Copyright(c) 2006-2007, Ext JS, LLC.
28090  *
28091  * Originally Released Under LGPL - original licence link has changed is not relivant.
28092  *
28093  * Fork - LGPL
28094  * <script type="text/javascript">
28095  */
28096  
28097 /**
28098  * @class Roo.bootstrap.PagingToolbar
28099  * @extends Roo.bootstrap.NavSimplebar
28100  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28101  * @constructor
28102  * Create a new PagingToolbar
28103  * @param {Object} config The config object
28104  * @param {Roo.data.Store} store
28105  */
28106 Roo.bootstrap.PagingToolbar = function(config)
28107 {
28108     // old args format still supported... - xtype is prefered..
28109         // created from xtype...
28110     
28111     this.ds = config.dataSource;
28112     
28113     if (config.store && !this.ds) {
28114         this.store= Roo.factory(config.store, Roo.data);
28115         this.ds = this.store;
28116         this.ds.xmodule = this.xmodule || false;
28117     }
28118     
28119     this.toolbarItems = [];
28120     if (config.items) {
28121         this.toolbarItems = config.items;
28122     }
28123     
28124     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28125     
28126     this.cursor = 0;
28127     
28128     if (this.ds) { 
28129         this.bind(this.ds);
28130     }
28131     
28132     if (Roo.bootstrap.version == 4) {
28133         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28134     } else {
28135         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28136     }
28137     
28138 };
28139
28140 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28141     /**
28142      * @cfg {Roo.data.Store} dataSource
28143      * The underlying data store providing the paged data
28144      */
28145     /**
28146      * @cfg {String/HTMLElement/Element} container
28147      * container The id or element that will contain the toolbar
28148      */
28149     /**
28150      * @cfg {Boolean} displayInfo
28151      * True to display the displayMsg (defaults to false)
28152      */
28153     /**
28154      * @cfg {Number} pageSize
28155      * The number of records to display per page (defaults to 20)
28156      */
28157     pageSize: 20,
28158     /**
28159      * @cfg {String} displayMsg
28160      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28161      */
28162     displayMsg : 'Displaying {0} - {1} of {2}',
28163     /**
28164      * @cfg {String} emptyMsg
28165      * The message to display when no records are found (defaults to "No data to display")
28166      */
28167     emptyMsg : 'No data to display',
28168     /**
28169      * Customizable piece of the default paging text (defaults to "Page")
28170      * @type String
28171      */
28172     beforePageText : "Page",
28173     /**
28174      * Customizable piece of the default paging text (defaults to "of %0")
28175      * @type String
28176      */
28177     afterPageText : "of {0}",
28178     /**
28179      * Customizable piece of the default paging text (defaults to "First Page")
28180      * @type String
28181      */
28182     firstText : "First Page",
28183     /**
28184      * Customizable piece of the default paging text (defaults to "Previous Page")
28185      * @type String
28186      */
28187     prevText : "Previous Page",
28188     /**
28189      * Customizable piece of the default paging text (defaults to "Next Page")
28190      * @type String
28191      */
28192     nextText : "Next Page",
28193     /**
28194      * Customizable piece of the default paging text (defaults to "Last Page")
28195      * @type String
28196      */
28197     lastText : "Last Page",
28198     /**
28199      * Customizable piece of the default paging text (defaults to "Refresh")
28200      * @type String
28201      */
28202     refreshText : "Refresh",
28203
28204     buttons : false,
28205     // private
28206     onRender : function(ct, position) 
28207     {
28208         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28209         this.navgroup.parentId = this.id;
28210         this.navgroup.onRender(this.el, null);
28211         // add the buttons to the navgroup
28212         
28213         if(this.displayInfo){
28214             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28215             this.displayEl = this.el.select('.x-paging-info', true).first();
28216 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28217 //            this.displayEl = navel.el.select('span',true).first();
28218         }
28219         
28220         var _this = this;
28221         
28222         if(this.buttons){
28223             Roo.each(_this.buttons, function(e){ // this might need to use render????
28224                Roo.factory(e).render(_this.el);
28225             });
28226         }
28227             
28228         Roo.each(_this.toolbarItems, function(e) {
28229             _this.navgroup.addItem(e);
28230         });
28231         
28232         
28233         this.first = this.navgroup.addItem({
28234             tooltip: this.firstText,
28235             cls: "prev btn-outline-secondary",
28236             html : ' <i class="fa fa-step-backward"></i>',
28237             disabled: true,
28238             preventDefault: true,
28239             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28240         });
28241         
28242         this.prev =  this.navgroup.addItem({
28243             tooltip: this.prevText,
28244             cls: "prev btn-outline-secondary",
28245             html : ' <i class="fa fa-backward"></i>',
28246             disabled: true,
28247             preventDefault: true,
28248             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28249         });
28250     //this.addSeparator();
28251         
28252         
28253         var field = this.navgroup.addItem( {
28254             tagtype : 'span',
28255             cls : 'x-paging-position  btn-outline-secondary',
28256              disabled: true,
28257             html : this.beforePageText  +
28258                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28259                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28260          } ); //?? escaped?
28261         
28262         this.field = field.el.select('input', true).first();
28263         this.field.on("keydown", this.onPagingKeydown, this);
28264         this.field.on("focus", function(){this.dom.select();});
28265     
28266     
28267         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28268         //this.field.setHeight(18);
28269         //this.addSeparator();
28270         this.next = this.navgroup.addItem({
28271             tooltip: this.nextText,
28272             cls: "next btn-outline-secondary",
28273             html : ' <i class="fa fa-forward"></i>',
28274             disabled: true,
28275             preventDefault: true,
28276             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28277         });
28278         this.last = this.navgroup.addItem({
28279             tooltip: this.lastText,
28280             html : ' <i class="fa fa-step-forward"></i>',
28281             cls: "next btn-outline-secondary",
28282             disabled: true,
28283             preventDefault: true,
28284             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28285         });
28286     //this.addSeparator();
28287         this.loading = this.navgroup.addItem({
28288             tooltip: this.refreshText,
28289             cls: "btn-outline-secondary",
28290             html : ' <i class="fa fa-refresh"></i>',
28291             preventDefault: true,
28292             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28293         });
28294         
28295     },
28296
28297     // private
28298     updateInfo : function(){
28299         if(this.displayEl){
28300             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28301             var msg = count == 0 ?
28302                 this.emptyMsg :
28303                 String.format(
28304                     this.displayMsg,
28305                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28306                 );
28307             this.displayEl.update(msg);
28308         }
28309     },
28310
28311     // private
28312     onLoad : function(ds, r, o)
28313     {
28314         this.cursor = o.params && o.params.start ? o.params.start : 0;
28315         
28316         var d = this.getPageData(),
28317             ap = d.activePage,
28318             ps = d.pages;
28319         
28320         
28321         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28322         this.field.dom.value = ap;
28323         this.first.setDisabled(ap == 1);
28324         this.prev.setDisabled(ap == 1);
28325         this.next.setDisabled(ap == ps);
28326         this.last.setDisabled(ap == ps);
28327         this.loading.enable();
28328         this.updateInfo();
28329     },
28330
28331     // private
28332     getPageData : function(){
28333         var total = this.ds.getTotalCount();
28334         return {
28335             total : total,
28336             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28337             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28338         };
28339     },
28340
28341     // private
28342     onLoadError : function(){
28343         this.loading.enable();
28344     },
28345
28346     // private
28347     onPagingKeydown : function(e){
28348         var k = e.getKey();
28349         var d = this.getPageData();
28350         if(k == e.RETURN){
28351             var v = this.field.dom.value, pageNum;
28352             if(!v || isNaN(pageNum = parseInt(v, 10))){
28353                 this.field.dom.value = d.activePage;
28354                 return;
28355             }
28356             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28357             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28358             e.stopEvent();
28359         }
28360         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))
28361         {
28362           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28363           this.field.dom.value = pageNum;
28364           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28365           e.stopEvent();
28366         }
28367         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28368         {
28369           var v = this.field.dom.value, pageNum; 
28370           var increment = (e.shiftKey) ? 10 : 1;
28371           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28372                 increment *= -1;
28373           }
28374           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28375             this.field.dom.value = d.activePage;
28376             return;
28377           }
28378           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28379           {
28380             this.field.dom.value = parseInt(v, 10) + increment;
28381             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28382             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28383           }
28384           e.stopEvent();
28385         }
28386     },
28387
28388     // private
28389     beforeLoad : function(){
28390         if(this.loading){
28391             this.loading.disable();
28392         }
28393     },
28394
28395     // private
28396     onClick : function(which){
28397         
28398         var ds = this.ds;
28399         if (!ds) {
28400             return;
28401         }
28402         
28403         switch(which){
28404             case "first":
28405                 ds.load({params:{start: 0, limit: this.pageSize}});
28406             break;
28407             case "prev":
28408                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28409             break;
28410             case "next":
28411                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28412             break;
28413             case "last":
28414                 var total = ds.getTotalCount();
28415                 var extra = total % this.pageSize;
28416                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28417                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28418             break;
28419             case "refresh":
28420                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28421             break;
28422         }
28423     },
28424
28425     /**
28426      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28427      * @param {Roo.data.Store} store The data store to unbind
28428      */
28429     unbind : function(ds){
28430         ds.un("beforeload", this.beforeLoad, this);
28431         ds.un("load", this.onLoad, this);
28432         ds.un("loadexception", this.onLoadError, this);
28433         ds.un("remove", this.updateInfo, this);
28434         ds.un("add", this.updateInfo, this);
28435         this.ds = undefined;
28436     },
28437
28438     /**
28439      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28440      * @param {Roo.data.Store} store The data store to bind
28441      */
28442     bind : function(ds){
28443         ds.on("beforeload", this.beforeLoad, this);
28444         ds.on("load", this.onLoad, this);
28445         ds.on("loadexception", this.onLoadError, this);
28446         ds.on("remove", this.updateInfo, this);
28447         ds.on("add", this.updateInfo, this);
28448         this.ds = ds;
28449     }
28450 });/*
28451  * - LGPL
28452  *
28453  * element
28454  * 
28455  */
28456
28457 /**
28458  * @class Roo.bootstrap.MessageBar
28459  * @extends Roo.bootstrap.Component
28460  * Bootstrap MessageBar class
28461  * @cfg {String} html contents of the MessageBar
28462  * @cfg {String} weight (info | success | warning | danger) default info
28463  * @cfg {String} beforeClass insert the bar before the given class
28464  * @cfg {Boolean} closable (true | false) default false
28465  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28466  * 
28467  * @constructor
28468  * Create a new Element
28469  * @param {Object} config The config object
28470  */
28471
28472 Roo.bootstrap.MessageBar = function(config){
28473     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28474 };
28475
28476 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28477     
28478     html: '',
28479     weight: 'info',
28480     closable: false,
28481     fixed: false,
28482     beforeClass: 'bootstrap-sticky-wrap',
28483     
28484     getAutoCreate : function(){
28485         
28486         var cfg = {
28487             tag: 'div',
28488             cls: 'alert alert-dismissable alert-' + this.weight,
28489             cn: [
28490                 {
28491                     tag: 'span',
28492                     cls: 'message',
28493                     html: this.html || ''
28494                 }
28495             ]
28496         };
28497         
28498         if(this.fixed){
28499             cfg.cls += ' alert-messages-fixed';
28500         }
28501         
28502         if(this.closable){
28503             cfg.cn.push({
28504                 tag: 'button',
28505                 cls: 'close',
28506                 html: 'x'
28507             });
28508         }
28509         
28510         return cfg;
28511     },
28512     
28513     onRender : function(ct, position)
28514     {
28515         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28516         
28517         if(!this.el){
28518             var cfg = Roo.apply({},  this.getAutoCreate());
28519             cfg.id = Roo.id();
28520             
28521             if (this.cls) {
28522                 cfg.cls += ' ' + this.cls;
28523             }
28524             if (this.style) {
28525                 cfg.style = this.style;
28526             }
28527             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28528             
28529             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28530         }
28531         
28532         this.el.select('>button.close').on('click', this.hide, this);
28533         
28534     },
28535     
28536     show : function()
28537     {
28538         if (!this.rendered) {
28539             this.render();
28540         }
28541         
28542         this.el.show();
28543         
28544         this.fireEvent('show', this);
28545         
28546     },
28547     
28548     hide : function()
28549     {
28550         if (!this.rendered) {
28551             this.render();
28552         }
28553         
28554         this.el.hide();
28555         
28556         this.fireEvent('hide', this);
28557     },
28558     
28559     update : function()
28560     {
28561 //        var e = this.el.dom.firstChild;
28562 //        
28563 //        if(this.closable){
28564 //            e = e.nextSibling;
28565 //        }
28566 //        
28567 //        e.data = this.html || '';
28568
28569         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28570     }
28571    
28572 });
28573
28574  
28575
28576      /*
28577  * - LGPL
28578  *
28579  * Graph
28580  * 
28581  */
28582
28583
28584 /**
28585  * @class Roo.bootstrap.Graph
28586  * @extends Roo.bootstrap.Component
28587  * Bootstrap Graph class
28588 > Prameters
28589  -sm {number} sm 4
28590  -md {number} md 5
28591  @cfg {String} graphtype  bar | vbar | pie
28592  @cfg {number} g_x coodinator | centre x (pie)
28593  @cfg {number} g_y coodinator | centre y (pie)
28594  @cfg {number} g_r radius (pie)
28595  @cfg {number} g_height height of the chart (respected by all elements in the set)
28596  @cfg {number} g_width width of the chart (respected by all elements in the set)
28597  @cfg {Object} title The title of the chart
28598     
28599  -{Array}  values
28600  -opts (object) options for the chart 
28601      o {
28602      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28603      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28604      o vgutter (number)
28605      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.
28606      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28607      o to
28608      o stretch (boolean)
28609      o }
28610  -opts (object) options for the pie
28611      o{
28612      o cut
28613      o startAngle (number)
28614      o endAngle (number)
28615      } 
28616  *
28617  * @constructor
28618  * Create a new Input
28619  * @param {Object} config The config object
28620  */
28621
28622 Roo.bootstrap.Graph = function(config){
28623     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28624     
28625     this.addEvents({
28626         // img events
28627         /**
28628          * @event click
28629          * The img click event for the img.
28630          * @param {Roo.EventObject} e
28631          */
28632         "click" : true
28633     });
28634 };
28635
28636 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28637     
28638     sm: 4,
28639     md: 5,
28640     graphtype: 'bar',
28641     g_height: 250,
28642     g_width: 400,
28643     g_x: 50,
28644     g_y: 50,
28645     g_r: 30,
28646     opts:{
28647         //g_colors: this.colors,
28648         g_type: 'soft',
28649         g_gutter: '20%'
28650
28651     },
28652     title : false,
28653
28654     getAutoCreate : function(){
28655         
28656         var cfg = {
28657             tag: 'div',
28658             html : null
28659         };
28660         
28661         
28662         return  cfg;
28663     },
28664
28665     onRender : function(ct,position){
28666         
28667         
28668         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28669         
28670         if (typeof(Raphael) == 'undefined') {
28671             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28672             return;
28673         }
28674         
28675         this.raphael = Raphael(this.el.dom);
28676         
28677                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28678                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28679                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28680                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28681                 /*
28682                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28683                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28684                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28685                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28686                 
28687                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28688                 r.barchart(330, 10, 300, 220, data1);
28689                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28690                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28691                 */
28692                 
28693                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28694                 // r.barchart(30, 30, 560, 250,  xdata, {
28695                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28696                 //     axis : "0 0 1 1",
28697                 //     axisxlabels :  xdata
28698                 //     //yvalues : cols,
28699                    
28700                 // });
28701 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28702 //        
28703 //        this.load(null,xdata,{
28704 //                axis : "0 0 1 1",
28705 //                axisxlabels :  xdata
28706 //                });
28707
28708     },
28709
28710     load : function(graphtype,xdata,opts)
28711     {
28712         this.raphael.clear();
28713         if(!graphtype) {
28714             graphtype = this.graphtype;
28715         }
28716         if(!opts){
28717             opts = this.opts;
28718         }
28719         var r = this.raphael,
28720             fin = function () {
28721                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28722             },
28723             fout = function () {
28724                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28725             },
28726             pfin = function() {
28727                 this.sector.stop();
28728                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28729
28730                 if (this.label) {
28731                     this.label[0].stop();
28732                     this.label[0].attr({ r: 7.5 });
28733                     this.label[1].attr({ "font-weight": 800 });
28734                 }
28735             },
28736             pfout = function() {
28737                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28738
28739                 if (this.label) {
28740                     this.label[0].animate({ r: 5 }, 500, "bounce");
28741                     this.label[1].attr({ "font-weight": 400 });
28742                 }
28743             };
28744
28745         switch(graphtype){
28746             case 'bar':
28747                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28748                 break;
28749             case 'hbar':
28750                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28751                 break;
28752             case 'pie':
28753 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28754 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28755 //            
28756                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28757                 
28758                 break;
28759
28760         }
28761         
28762         if(this.title){
28763             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28764         }
28765         
28766     },
28767     
28768     setTitle: function(o)
28769     {
28770         this.title = o;
28771     },
28772     
28773     initEvents: function() {
28774         
28775         if(!this.href){
28776             this.el.on('click', this.onClick, this);
28777         }
28778     },
28779     
28780     onClick : function(e)
28781     {
28782         Roo.log('img onclick');
28783         this.fireEvent('click', this, e);
28784     }
28785    
28786 });
28787
28788  
28789 /*
28790  * - LGPL
28791  *
28792  * numberBox
28793  * 
28794  */
28795 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28796
28797 /**
28798  * @class Roo.bootstrap.dash.NumberBox
28799  * @extends Roo.bootstrap.Component
28800  * Bootstrap NumberBox class
28801  * @cfg {String} headline Box headline
28802  * @cfg {String} content Box content
28803  * @cfg {String} icon Box icon
28804  * @cfg {String} footer Footer text
28805  * @cfg {String} fhref Footer href
28806  * 
28807  * @constructor
28808  * Create a new NumberBox
28809  * @param {Object} config The config object
28810  */
28811
28812
28813 Roo.bootstrap.dash.NumberBox = function(config){
28814     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28815     
28816 };
28817
28818 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28819     
28820     headline : '',
28821     content : '',
28822     icon : '',
28823     footer : '',
28824     fhref : '',
28825     ficon : '',
28826     
28827     getAutoCreate : function(){
28828         
28829         var cfg = {
28830             tag : 'div',
28831             cls : 'small-box ',
28832             cn : [
28833                 {
28834                     tag : 'div',
28835                     cls : 'inner',
28836                     cn :[
28837                         {
28838                             tag : 'h3',
28839                             cls : 'roo-headline',
28840                             html : this.headline
28841                         },
28842                         {
28843                             tag : 'p',
28844                             cls : 'roo-content',
28845                             html : this.content
28846                         }
28847                     ]
28848                 }
28849             ]
28850         };
28851         
28852         if(this.icon){
28853             cfg.cn.push({
28854                 tag : 'div',
28855                 cls : 'icon',
28856                 cn :[
28857                     {
28858                         tag : 'i',
28859                         cls : 'ion ' + this.icon
28860                     }
28861                 ]
28862             });
28863         }
28864         
28865         if(this.footer){
28866             var footer = {
28867                 tag : 'a',
28868                 cls : 'small-box-footer',
28869                 href : this.fhref || '#',
28870                 html : this.footer
28871             };
28872             
28873             cfg.cn.push(footer);
28874             
28875         }
28876         
28877         return  cfg;
28878     },
28879
28880     onRender : function(ct,position){
28881         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28882
28883
28884        
28885                 
28886     },
28887
28888     setHeadline: function (value)
28889     {
28890         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28891     },
28892     
28893     setFooter: function (value, href)
28894     {
28895         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28896         
28897         if(href){
28898             this.el.select('a.small-box-footer',true).first().attr('href', href);
28899         }
28900         
28901     },
28902
28903     setContent: function (value)
28904     {
28905         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28906     },
28907
28908     initEvents: function() 
28909     {   
28910         
28911     }
28912     
28913 });
28914
28915  
28916 /*
28917  * - LGPL
28918  *
28919  * TabBox
28920  * 
28921  */
28922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28923
28924 /**
28925  * @class Roo.bootstrap.dash.TabBox
28926  * @extends Roo.bootstrap.Component
28927  * Bootstrap TabBox class
28928  * @cfg {String} title Title of the TabBox
28929  * @cfg {String} icon Icon of the TabBox
28930  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28931  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28932  * 
28933  * @constructor
28934  * Create a new TabBox
28935  * @param {Object} config The config object
28936  */
28937
28938
28939 Roo.bootstrap.dash.TabBox = function(config){
28940     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28941     this.addEvents({
28942         // raw events
28943         /**
28944          * @event addpane
28945          * When a pane is added
28946          * @param {Roo.bootstrap.dash.TabPane} pane
28947          */
28948         "addpane" : true,
28949         /**
28950          * @event activatepane
28951          * When a pane is activated
28952          * @param {Roo.bootstrap.dash.TabPane} pane
28953          */
28954         "activatepane" : true
28955         
28956          
28957     });
28958     
28959     this.panes = [];
28960 };
28961
28962 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28963
28964     title : '',
28965     icon : false,
28966     showtabs : true,
28967     tabScrollable : false,
28968     
28969     getChildContainer : function()
28970     {
28971         return this.el.select('.tab-content', true).first();
28972     },
28973     
28974     getAutoCreate : function(){
28975         
28976         var header = {
28977             tag: 'li',
28978             cls: 'pull-left header',
28979             html: this.title,
28980             cn : []
28981         };
28982         
28983         if(this.icon){
28984             header.cn.push({
28985                 tag: 'i',
28986                 cls: 'fa ' + this.icon
28987             });
28988         }
28989         
28990         var h = {
28991             tag: 'ul',
28992             cls: 'nav nav-tabs pull-right',
28993             cn: [
28994                 header
28995             ]
28996         };
28997         
28998         if(this.tabScrollable){
28999             h = {
29000                 tag: 'div',
29001                 cls: 'tab-header',
29002                 cn: [
29003                     {
29004                         tag: 'ul',
29005                         cls: 'nav nav-tabs pull-right',
29006                         cn: [
29007                             header
29008                         ]
29009                     }
29010                 ]
29011             };
29012         }
29013         
29014         var cfg = {
29015             tag: 'div',
29016             cls: 'nav-tabs-custom',
29017             cn: [
29018                 h,
29019                 {
29020                     tag: 'div',
29021                     cls: 'tab-content no-padding',
29022                     cn: []
29023                 }
29024             ]
29025         };
29026
29027         return  cfg;
29028     },
29029     initEvents : function()
29030     {
29031         //Roo.log('add add pane handler');
29032         this.on('addpane', this.onAddPane, this);
29033     },
29034      /**
29035      * Updates the box title
29036      * @param {String} html to set the title to.
29037      */
29038     setTitle : function(value)
29039     {
29040         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29041     },
29042     onAddPane : function(pane)
29043     {
29044         this.panes.push(pane);
29045         //Roo.log('addpane');
29046         //Roo.log(pane);
29047         // tabs are rendere left to right..
29048         if(!this.showtabs){
29049             return;
29050         }
29051         
29052         var ctr = this.el.select('.nav-tabs', true).first();
29053          
29054          
29055         var existing = ctr.select('.nav-tab',true);
29056         var qty = existing.getCount();;
29057         
29058         
29059         var tab = ctr.createChild({
29060             tag : 'li',
29061             cls : 'nav-tab' + (qty ? '' : ' active'),
29062             cn : [
29063                 {
29064                     tag : 'a',
29065                     href:'#',
29066                     html : pane.title
29067                 }
29068             ]
29069         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29070         pane.tab = tab;
29071         
29072         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29073         if (!qty) {
29074             pane.el.addClass('active');
29075         }
29076         
29077                 
29078     },
29079     onTabClick : function(ev,un,ob,pane)
29080     {
29081         //Roo.log('tab - prev default');
29082         ev.preventDefault();
29083         
29084         
29085         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29086         pane.tab.addClass('active');
29087         //Roo.log(pane.title);
29088         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29089         // technically we should have a deactivate event.. but maybe add later.
29090         // and it should not de-activate the selected tab...
29091         this.fireEvent('activatepane', pane);
29092         pane.el.addClass('active');
29093         pane.fireEvent('activate');
29094         
29095         
29096     },
29097     
29098     getActivePane : function()
29099     {
29100         var r = false;
29101         Roo.each(this.panes, function(p) {
29102             if(p.el.hasClass('active')){
29103                 r = p;
29104                 return false;
29105             }
29106             
29107             return;
29108         });
29109         
29110         return r;
29111     }
29112     
29113     
29114 });
29115
29116  
29117 /*
29118  * - LGPL
29119  *
29120  * Tab pane
29121  * 
29122  */
29123 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29124 /**
29125  * @class Roo.bootstrap.TabPane
29126  * @extends Roo.bootstrap.Component
29127  * Bootstrap TabPane class
29128  * @cfg {Boolean} active (false | true) Default false
29129  * @cfg {String} title title of panel
29130
29131  * 
29132  * @constructor
29133  * Create a new TabPane
29134  * @param {Object} config The config object
29135  */
29136
29137 Roo.bootstrap.dash.TabPane = function(config){
29138     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29139     
29140     this.addEvents({
29141         // raw events
29142         /**
29143          * @event activate
29144          * When a pane is activated
29145          * @param {Roo.bootstrap.dash.TabPane} pane
29146          */
29147         "activate" : true
29148          
29149     });
29150 };
29151
29152 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29153     
29154     active : false,
29155     title : '',
29156     
29157     // the tabBox that this is attached to.
29158     tab : false,
29159      
29160     getAutoCreate : function() 
29161     {
29162         var cfg = {
29163             tag: 'div',
29164             cls: 'tab-pane'
29165         };
29166         
29167         if(this.active){
29168             cfg.cls += ' active';
29169         }
29170         
29171         return cfg;
29172     },
29173     initEvents  : function()
29174     {
29175         //Roo.log('trigger add pane handler');
29176         this.parent().fireEvent('addpane', this)
29177     },
29178     
29179      /**
29180      * Updates the tab title 
29181      * @param {String} html to set the title to.
29182      */
29183     setTitle: function(str)
29184     {
29185         if (!this.tab) {
29186             return;
29187         }
29188         this.title = str;
29189         this.tab.select('a', true).first().dom.innerHTML = str;
29190         
29191     }
29192     
29193     
29194     
29195 });
29196
29197  
29198
29199
29200  /*
29201  * - LGPL
29202  *
29203  * menu
29204  * 
29205  */
29206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29207
29208 /**
29209  * @class Roo.bootstrap.menu.Menu
29210  * @extends Roo.bootstrap.Component
29211  * Bootstrap Menu class - container for Menu
29212  * @cfg {String} html Text of the menu
29213  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29214  * @cfg {String} icon Font awesome icon
29215  * @cfg {String} pos Menu align to (top | bottom) default bottom
29216  * 
29217  * 
29218  * @constructor
29219  * Create a new Menu
29220  * @param {Object} config The config object
29221  */
29222
29223
29224 Roo.bootstrap.menu.Menu = function(config){
29225     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29226     
29227     this.addEvents({
29228         /**
29229          * @event beforeshow
29230          * Fires before this menu is displayed
29231          * @param {Roo.bootstrap.menu.Menu} this
29232          */
29233         beforeshow : true,
29234         /**
29235          * @event beforehide
29236          * Fires before this menu is hidden
29237          * @param {Roo.bootstrap.menu.Menu} this
29238          */
29239         beforehide : true,
29240         /**
29241          * @event show
29242          * Fires after this menu is displayed
29243          * @param {Roo.bootstrap.menu.Menu} this
29244          */
29245         show : true,
29246         /**
29247          * @event hide
29248          * Fires after this menu is hidden
29249          * @param {Roo.bootstrap.menu.Menu} this
29250          */
29251         hide : true,
29252         /**
29253          * @event click
29254          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29255          * @param {Roo.bootstrap.menu.Menu} this
29256          * @param {Roo.EventObject} e
29257          */
29258         click : true
29259     });
29260     
29261 };
29262
29263 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29264     
29265     submenu : false,
29266     html : '',
29267     weight : 'default',
29268     icon : false,
29269     pos : 'bottom',
29270     
29271     
29272     getChildContainer : function() {
29273         if(this.isSubMenu){
29274             return this.el;
29275         }
29276         
29277         return this.el.select('ul.dropdown-menu', true).first();  
29278     },
29279     
29280     getAutoCreate : function()
29281     {
29282         var text = [
29283             {
29284                 tag : 'span',
29285                 cls : 'roo-menu-text',
29286                 html : this.html
29287             }
29288         ];
29289         
29290         if(this.icon){
29291             text.unshift({
29292                 tag : 'i',
29293                 cls : 'fa ' + this.icon
29294             })
29295         }
29296         
29297         
29298         var cfg = {
29299             tag : 'div',
29300             cls : 'btn-group',
29301             cn : [
29302                 {
29303                     tag : 'button',
29304                     cls : 'dropdown-button btn btn-' + this.weight,
29305                     cn : text
29306                 },
29307                 {
29308                     tag : 'button',
29309                     cls : 'dropdown-toggle btn btn-' + this.weight,
29310                     cn : [
29311                         {
29312                             tag : 'span',
29313                             cls : 'caret'
29314                         }
29315                     ]
29316                 },
29317                 {
29318                     tag : 'ul',
29319                     cls : 'dropdown-menu'
29320                 }
29321             ]
29322             
29323         };
29324         
29325         if(this.pos == 'top'){
29326             cfg.cls += ' dropup';
29327         }
29328         
29329         if(this.isSubMenu){
29330             cfg = {
29331                 tag : 'ul',
29332                 cls : 'dropdown-menu'
29333             }
29334         }
29335         
29336         return cfg;
29337     },
29338     
29339     onRender : function(ct, position)
29340     {
29341         this.isSubMenu = ct.hasClass('dropdown-submenu');
29342         
29343         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29344     },
29345     
29346     initEvents : function() 
29347     {
29348         if(this.isSubMenu){
29349             return;
29350         }
29351         
29352         this.hidden = true;
29353         
29354         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29355         this.triggerEl.on('click', this.onTriggerPress, this);
29356         
29357         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29358         this.buttonEl.on('click', this.onClick, this);
29359         
29360     },
29361     
29362     list : function()
29363     {
29364         if(this.isSubMenu){
29365             return this.el;
29366         }
29367         
29368         return this.el.select('ul.dropdown-menu', true).first();
29369     },
29370     
29371     onClick : function(e)
29372     {
29373         this.fireEvent("click", this, e);
29374     },
29375     
29376     onTriggerPress  : function(e)
29377     {   
29378         if (this.isVisible()) {
29379             this.hide();
29380         } else {
29381             this.show();
29382         }
29383     },
29384     
29385     isVisible : function(){
29386         return !this.hidden;
29387     },
29388     
29389     show : function()
29390     {
29391         this.fireEvent("beforeshow", this);
29392         
29393         this.hidden = false;
29394         this.el.addClass('open');
29395         
29396         Roo.get(document).on("mouseup", this.onMouseUp, this);
29397         
29398         this.fireEvent("show", this);
29399         
29400         
29401     },
29402     
29403     hide : function()
29404     {
29405         this.fireEvent("beforehide", this);
29406         
29407         this.hidden = true;
29408         this.el.removeClass('open');
29409         
29410         Roo.get(document).un("mouseup", this.onMouseUp);
29411         
29412         this.fireEvent("hide", this);
29413     },
29414     
29415     onMouseUp : function()
29416     {
29417         this.hide();
29418     }
29419     
29420 });
29421
29422  
29423  /*
29424  * - LGPL
29425  *
29426  * menu item
29427  * 
29428  */
29429 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29430
29431 /**
29432  * @class Roo.bootstrap.menu.Item
29433  * @extends Roo.bootstrap.Component
29434  * Bootstrap MenuItem class
29435  * @cfg {Boolean} submenu (true | false) default false
29436  * @cfg {String} html text of the item
29437  * @cfg {String} href the link
29438  * @cfg {Boolean} disable (true | false) default false
29439  * @cfg {Boolean} preventDefault (true | false) default true
29440  * @cfg {String} icon Font awesome icon
29441  * @cfg {String} pos Submenu align to (left | right) default right 
29442  * 
29443  * 
29444  * @constructor
29445  * Create a new Item
29446  * @param {Object} config The config object
29447  */
29448
29449
29450 Roo.bootstrap.menu.Item = function(config){
29451     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29452     this.addEvents({
29453         /**
29454          * @event mouseover
29455          * Fires when the mouse is hovering over this menu
29456          * @param {Roo.bootstrap.menu.Item} this
29457          * @param {Roo.EventObject} e
29458          */
29459         mouseover : true,
29460         /**
29461          * @event mouseout
29462          * Fires when the mouse exits this menu
29463          * @param {Roo.bootstrap.menu.Item} this
29464          * @param {Roo.EventObject} e
29465          */
29466         mouseout : true,
29467         // raw events
29468         /**
29469          * @event click
29470          * The raw click event for the entire grid.
29471          * @param {Roo.EventObject} e
29472          */
29473         click : true
29474     });
29475 };
29476
29477 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29478     
29479     submenu : false,
29480     href : '',
29481     html : '',
29482     preventDefault: true,
29483     disable : false,
29484     icon : false,
29485     pos : 'right',
29486     
29487     getAutoCreate : function()
29488     {
29489         var text = [
29490             {
29491                 tag : 'span',
29492                 cls : 'roo-menu-item-text',
29493                 html : this.html
29494             }
29495         ];
29496         
29497         if(this.icon){
29498             text.unshift({
29499                 tag : 'i',
29500                 cls : 'fa ' + this.icon
29501             })
29502         }
29503         
29504         var cfg = {
29505             tag : 'li',
29506             cn : [
29507                 {
29508                     tag : 'a',
29509                     href : this.href || '#',
29510                     cn : text
29511                 }
29512             ]
29513         };
29514         
29515         if(this.disable){
29516             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29517         }
29518         
29519         if(this.submenu){
29520             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29521             
29522             if(this.pos == 'left'){
29523                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29524             }
29525         }
29526         
29527         return cfg;
29528     },
29529     
29530     initEvents : function() 
29531     {
29532         this.el.on('mouseover', this.onMouseOver, this);
29533         this.el.on('mouseout', this.onMouseOut, this);
29534         
29535         this.el.select('a', true).first().on('click', this.onClick, this);
29536         
29537     },
29538     
29539     onClick : function(e)
29540     {
29541         if(this.preventDefault){
29542             e.preventDefault();
29543         }
29544         
29545         this.fireEvent("click", this, e);
29546     },
29547     
29548     onMouseOver : function(e)
29549     {
29550         if(this.submenu && this.pos == 'left'){
29551             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29552         }
29553         
29554         this.fireEvent("mouseover", this, e);
29555     },
29556     
29557     onMouseOut : function(e)
29558     {
29559         this.fireEvent("mouseout", this, e);
29560     }
29561 });
29562
29563  
29564
29565  /*
29566  * - LGPL
29567  *
29568  * menu separator
29569  * 
29570  */
29571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29572
29573 /**
29574  * @class Roo.bootstrap.menu.Separator
29575  * @extends Roo.bootstrap.Component
29576  * Bootstrap Separator class
29577  * 
29578  * @constructor
29579  * Create a new Separator
29580  * @param {Object} config The config object
29581  */
29582
29583
29584 Roo.bootstrap.menu.Separator = function(config){
29585     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29586 };
29587
29588 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29589     
29590     getAutoCreate : function(){
29591         var cfg = {
29592             tag : 'li',
29593             cls: 'dropdown-divider divider'
29594         };
29595         
29596         return cfg;
29597     }
29598    
29599 });
29600
29601  
29602
29603  /*
29604  * - LGPL
29605  *
29606  * Tooltip
29607  * 
29608  */
29609
29610 /**
29611  * @class Roo.bootstrap.Tooltip
29612  * Bootstrap Tooltip class
29613  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29614  * to determine which dom element triggers the tooltip.
29615  * 
29616  * It needs to add support for additional attributes like tooltip-position
29617  * 
29618  * @constructor
29619  * Create a new Toolti
29620  * @param {Object} config The config object
29621  */
29622
29623 Roo.bootstrap.Tooltip = function(config){
29624     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29625     
29626     this.alignment = Roo.bootstrap.Tooltip.alignment;
29627     
29628     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29629         this.alignment = config.alignment;
29630     }
29631     
29632 };
29633
29634 Roo.apply(Roo.bootstrap.Tooltip, {
29635     /**
29636      * @function init initialize tooltip monitoring.
29637      * @static
29638      */
29639     currentEl : false,
29640     currentTip : false,
29641     currentRegion : false,
29642     
29643     //  init : delay?
29644     
29645     init : function()
29646     {
29647         Roo.get(document).on('mouseover', this.enter ,this);
29648         Roo.get(document).on('mouseout', this.leave, this);
29649          
29650         
29651         this.currentTip = new Roo.bootstrap.Tooltip();
29652     },
29653     
29654     enter : function(ev)
29655     {
29656         var dom = ev.getTarget();
29657         
29658         //Roo.log(['enter',dom]);
29659         var el = Roo.fly(dom);
29660         if (this.currentEl) {
29661             //Roo.log(dom);
29662             //Roo.log(this.currentEl);
29663             //Roo.log(this.currentEl.contains(dom));
29664             if (this.currentEl == el) {
29665                 return;
29666             }
29667             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29668                 return;
29669             }
29670
29671         }
29672         
29673         if (this.currentTip.el) {
29674             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29675         }    
29676         //Roo.log(ev);
29677         
29678         if(!el || el.dom == document){
29679             return;
29680         }
29681         
29682         var bindEl = el; 
29683         var pel = false;
29684         if (!el.attr('tooltip')) {
29685             pel = el.findParent("[tooltip]");
29686             if (pel) {
29687                 bindEl = Roo.get(pel);
29688             }
29689         }
29690         
29691        
29692         
29693         // you can not look for children, as if el is the body.. then everythign is the child..
29694         if (!pel && !el.attr('tooltip')) { //
29695             if (!el.select("[tooltip]").elements.length) {
29696                 return;
29697             }
29698             // is the mouse over this child...?
29699             bindEl = el.select("[tooltip]").first();
29700             var xy = ev.getXY();
29701             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29702                 //Roo.log("not in region.");
29703                 return;
29704             }
29705             //Roo.log("child element over..");
29706             
29707         }
29708         this.currentEl = el;
29709         this.currentTip.bind(bindEl);
29710         this.currentRegion = Roo.lib.Region.getRegion(dom);
29711         this.currentTip.enter();
29712         
29713     },
29714     leave : function(ev)
29715     {
29716         var dom = ev.getTarget();
29717         //Roo.log(['leave',dom]);
29718         if (!this.currentEl) {
29719             return;
29720         }
29721         
29722         
29723         if (dom != this.currentEl.dom) {
29724             return;
29725         }
29726         var xy = ev.getXY();
29727         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29728             return;
29729         }
29730         // only activate leave if mouse cursor is outside... bounding box..
29731         
29732         
29733         
29734         
29735         if (this.currentTip) {
29736             this.currentTip.leave();
29737         }
29738         //Roo.log('clear currentEl');
29739         this.currentEl = false;
29740         
29741         
29742     },
29743     alignment : {
29744         'left' : ['r-l', [-2,0], 'right'],
29745         'right' : ['l-r', [2,0], 'left'],
29746         'bottom' : ['t-b', [0,2], 'top'],
29747         'top' : [ 'b-t', [0,-2], 'bottom']
29748     }
29749     
29750 });
29751
29752
29753 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29754     
29755     
29756     bindEl : false,
29757     
29758     delay : null, // can be { show : 300 , hide: 500}
29759     
29760     timeout : null,
29761     
29762     hoverState : null, //???
29763     
29764     placement : 'bottom', 
29765     
29766     alignment : false,
29767     
29768     getAutoCreate : function(){
29769     
29770         var cfg = {
29771            cls : 'tooltip',   
29772            role : 'tooltip',
29773            cn : [
29774                 {
29775                     cls : 'tooltip-arrow arrow'
29776                 },
29777                 {
29778                     cls : 'tooltip-inner'
29779                 }
29780            ]
29781         };
29782         
29783         return cfg;
29784     },
29785     bind : function(el)
29786     {
29787         this.bindEl = el;
29788     },
29789     
29790     initEvents : function()
29791     {
29792         this.arrowEl = this.el.select('.arrow', true).first();
29793         this.innerEl = this.el.select('.tooltip-inner', true).first();
29794     },
29795     
29796     enter : function () {
29797        
29798         if (this.timeout != null) {
29799             clearTimeout(this.timeout);
29800         }
29801         
29802         this.hoverState = 'in';
29803          //Roo.log("enter - show");
29804         if (!this.delay || !this.delay.show) {
29805             this.show();
29806             return;
29807         }
29808         var _t = this;
29809         this.timeout = setTimeout(function () {
29810             if (_t.hoverState == 'in') {
29811                 _t.show();
29812             }
29813         }, this.delay.show);
29814     },
29815     leave : function()
29816     {
29817         clearTimeout(this.timeout);
29818     
29819         this.hoverState = 'out';
29820          if (!this.delay || !this.delay.hide) {
29821             this.hide();
29822             return;
29823         }
29824        
29825         var _t = this;
29826         this.timeout = setTimeout(function () {
29827             //Roo.log("leave - timeout");
29828             
29829             if (_t.hoverState == 'out') {
29830                 _t.hide();
29831                 Roo.bootstrap.Tooltip.currentEl = false;
29832             }
29833         }, delay);
29834     },
29835     
29836     show : function (msg)
29837     {
29838         if (!this.el) {
29839             this.render(document.body);
29840         }
29841         // set content.
29842         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29843         
29844         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29845         
29846         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29847         
29848         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29849                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29850         
29851         var placement = typeof this.placement == 'function' ?
29852             this.placement.call(this, this.el, on_el) :
29853             this.placement;
29854             
29855         var autoToken = /\s?auto?\s?/i;
29856         var autoPlace = autoToken.test(placement);
29857         if (autoPlace) {
29858             placement = placement.replace(autoToken, '') || 'top';
29859         }
29860         
29861         //this.el.detach()
29862         //this.el.setXY([0,0]);
29863         this.el.show();
29864         //this.el.dom.style.display='block';
29865         
29866         //this.el.appendTo(on_el);
29867         
29868         var p = this.getPosition();
29869         var box = this.el.getBox();
29870         
29871         if (autoPlace) {
29872             // fixme..
29873         }
29874         
29875         var align = this.alignment[placement];
29876         
29877         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29878         
29879         if(placement == 'top' || placement == 'bottom'){
29880             if(xy[0] < 0){
29881                 placement = 'right';
29882             }
29883             
29884             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29885                 placement = 'left';
29886             }
29887             
29888             var scroll = Roo.select('body', true).first().getScroll();
29889             
29890             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29891                 placement = 'top';
29892             }
29893             
29894             align = this.alignment[placement];
29895             
29896             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29897             
29898         }
29899         
29900         var elems = document.getElementsByTagName('div');
29901         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29902         for (var i = 0; i < elems.length; i++) {
29903           var zindex = Number.parseInt(
29904                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29905                 10
29906           );
29907           if (zindex > highest) {
29908             highest = zindex;
29909           }
29910         }
29911         
29912         
29913         
29914         this.el.dom.style.zIndex = highest;
29915         
29916         this.el.alignTo(this.bindEl, align[0],align[1]);
29917         //var arrow = this.el.select('.arrow',true).first();
29918         //arrow.set(align[2], 
29919         
29920         this.el.addClass(placement);
29921         this.el.addClass("bs-tooltip-"+ placement);
29922         
29923         this.el.addClass('in fade show');
29924         
29925         this.hoverState = null;
29926         
29927         if (this.el.hasClass('fade')) {
29928             // fade it?
29929         }
29930         
29931         
29932         
29933         
29934         
29935     },
29936     hide : function()
29937     {
29938          
29939         if (!this.el) {
29940             return;
29941         }
29942         //this.el.setXY([0,0]);
29943         this.el.removeClass(['show', 'in']);
29944         //this.el.hide();
29945         
29946     }
29947     
29948 });
29949  
29950
29951  /*
29952  * - LGPL
29953  *
29954  * Location Picker
29955  * 
29956  */
29957
29958 /**
29959  * @class Roo.bootstrap.LocationPicker
29960  * @extends Roo.bootstrap.Component
29961  * Bootstrap LocationPicker class
29962  * @cfg {Number} latitude Position when init default 0
29963  * @cfg {Number} longitude Position when init default 0
29964  * @cfg {Number} zoom default 15
29965  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29966  * @cfg {Boolean} mapTypeControl default false
29967  * @cfg {Boolean} disableDoubleClickZoom default false
29968  * @cfg {Boolean} scrollwheel default true
29969  * @cfg {Boolean} streetViewControl default false
29970  * @cfg {Number} radius default 0
29971  * @cfg {String} locationName
29972  * @cfg {Boolean} draggable default true
29973  * @cfg {Boolean} enableAutocomplete default false
29974  * @cfg {Boolean} enableReverseGeocode default true
29975  * @cfg {String} markerTitle
29976  * 
29977  * @constructor
29978  * Create a new LocationPicker
29979  * @param {Object} config The config object
29980  */
29981
29982
29983 Roo.bootstrap.LocationPicker = function(config){
29984     
29985     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29986     
29987     this.addEvents({
29988         /**
29989          * @event initial
29990          * Fires when the picker initialized.
29991          * @param {Roo.bootstrap.LocationPicker} this
29992          * @param {Google Location} location
29993          */
29994         initial : true,
29995         /**
29996          * @event positionchanged
29997          * Fires when the picker position changed.
29998          * @param {Roo.bootstrap.LocationPicker} this
29999          * @param {Google Location} location
30000          */
30001         positionchanged : true,
30002         /**
30003          * @event resize
30004          * Fires when the map resize.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          */
30007         resize : true,
30008         /**
30009          * @event show
30010          * Fires when the map show.
30011          * @param {Roo.bootstrap.LocationPicker} this
30012          */
30013         show : true,
30014         /**
30015          * @event hide
30016          * Fires when the map hide.
30017          * @param {Roo.bootstrap.LocationPicker} this
30018          */
30019         hide : true,
30020         /**
30021          * @event mapClick
30022          * Fires when click the map.
30023          * @param {Roo.bootstrap.LocationPicker} this
30024          * @param {Map event} e
30025          */
30026         mapClick : true,
30027         /**
30028          * @event mapRightClick
30029          * Fires when right click the map.
30030          * @param {Roo.bootstrap.LocationPicker} this
30031          * @param {Map event} e
30032          */
30033         mapRightClick : true,
30034         /**
30035          * @event markerClick
30036          * Fires when click the marker.
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          * @param {Map event} e
30039          */
30040         markerClick : true,
30041         /**
30042          * @event markerRightClick
30043          * Fires when right click the marker.
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          * @param {Map event} e
30046          */
30047         markerRightClick : true,
30048         /**
30049          * @event OverlayViewDraw
30050          * Fires when OverlayView Draw
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          */
30053         OverlayViewDraw : true,
30054         /**
30055          * @event OverlayViewOnAdd
30056          * Fires when OverlayView Draw
30057          * @param {Roo.bootstrap.LocationPicker} this
30058          */
30059         OverlayViewOnAdd : true,
30060         /**
30061          * @event OverlayViewOnRemove
30062          * Fires when OverlayView Draw
30063          * @param {Roo.bootstrap.LocationPicker} this
30064          */
30065         OverlayViewOnRemove : true,
30066         /**
30067          * @event OverlayViewShow
30068          * Fires when OverlayView Draw
30069          * @param {Roo.bootstrap.LocationPicker} this
30070          * @param {Pixel} cpx
30071          */
30072         OverlayViewShow : true,
30073         /**
30074          * @event OverlayViewHide
30075          * Fires when OverlayView Draw
30076          * @param {Roo.bootstrap.LocationPicker} this
30077          */
30078         OverlayViewHide : true,
30079         /**
30080          * @event loadexception
30081          * Fires when load google lib failed.
30082          * @param {Roo.bootstrap.LocationPicker} this
30083          */
30084         loadexception : true
30085     });
30086         
30087 };
30088
30089 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30090     
30091     gMapContext: false,
30092     
30093     latitude: 0,
30094     longitude: 0,
30095     zoom: 15,
30096     mapTypeId: false,
30097     mapTypeControl: false,
30098     disableDoubleClickZoom: false,
30099     scrollwheel: true,
30100     streetViewControl: false,
30101     radius: 0,
30102     locationName: '',
30103     draggable: true,
30104     enableAutocomplete: false,
30105     enableReverseGeocode: true,
30106     markerTitle: '',
30107     
30108     getAutoCreate: function()
30109     {
30110
30111         var cfg = {
30112             tag: 'div',
30113             cls: 'roo-location-picker'
30114         };
30115         
30116         return cfg
30117     },
30118     
30119     initEvents: function(ct, position)
30120     {       
30121         if(!this.el.getWidth() || this.isApplied()){
30122             return;
30123         }
30124         
30125         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30126         
30127         this.initial();
30128     },
30129     
30130     initial: function()
30131     {
30132         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30133             this.fireEvent('loadexception', this);
30134             return;
30135         }
30136         
30137         if(!this.mapTypeId){
30138             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30139         }
30140         
30141         this.gMapContext = this.GMapContext();
30142         
30143         this.initOverlayView();
30144         
30145         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30146         
30147         var _this = this;
30148                 
30149         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30150             _this.setPosition(_this.gMapContext.marker.position);
30151         });
30152         
30153         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30154             _this.fireEvent('mapClick', this, event);
30155             
30156         });
30157
30158         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30159             _this.fireEvent('mapRightClick', this, event);
30160             
30161         });
30162         
30163         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30164             _this.fireEvent('markerClick', this, event);
30165             
30166         });
30167
30168         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30169             _this.fireEvent('markerRightClick', this, event);
30170             
30171         });
30172         
30173         this.setPosition(this.gMapContext.location);
30174         
30175         this.fireEvent('initial', this, this.gMapContext.location);
30176     },
30177     
30178     initOverlayView: function()
30179     {
30180         var _this = this;
30181         
30182         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30183             
30184             draw: function()
30185             {
30186                 _this.fireEvent('OverlayViewDraw', _this);
30187             },
30188             
30189             onAdd: function()
30190             {
30191                 _this.fireEvent('OverlayViewOnAdd', _this);
30192             },
30193             
30194             onRemove: function()
30195             {
30196                 _this.fireEvent('OverlayViewOnRemove', _this);
30197             },
30198             
30199             show: function(cpx)
30200             {
30201                 _this.fireEvent('OverlayViewShow', _this, cpx);
30202             },
30203             
30204             hide: function()
30205             {
30206                 _this.fireEvent('OverlayViewHide', _this);
30207             }
30208             
30209         });
30210     },
30211     
30212     fromLatLngToContainerPixel: function(event)
30213     {
30214         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30215     },
30216     
30217     isApplied: function() 
30218     {
30219         return this.getGmapContext() == false ? false : true;
30220     },
30221     
30222     getGmapContext: function() 
30223     {
30224         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30225     },
30226     
30227     GMapContext: function() 
30228     {
30229         var position = new google.maps.LatLng(this.latitude, this.longitude);
30230         
30231         var _map = new google.maps.Map(this.el.dom, {
30232             center: position,
30233             zoom: this.zoom,
30234             mapTypeId: this.mapTypeId,
30235             mapTypeControl: this.mapTypeControl,
30236             disableDoubleClickZoom: this.disableDoubleClickZoom,
30237             scrollwheel: this.scrollwheel,
30238             streetViewControl: this.streetViewControl,
30239             locationName: this.locationName,
30240             draggable: this.draggable,
30241             enableAutocomplete: this.enableAutocomplete,
30242             enableReverseGeocode: this.enableReverseGeocode
30243         });
30244         
30245         var _marker = new google.maps.Marker({
30246             position: position,
30247             map: _map,
30248             title: this.markerTitle,
30249             draggable: this.draggable
30250         });
30251         
30252         return {
30253             map: _map,
30254             marker: _marker,
30255             circle: null,
30256             location: position,
30257             radius: this.radius,
30258             locationName: this.locationName,
30259             addressComponents: {
30260                 formatted_address: null,
30261                 addressLine1: null,
30262                 addressLine2: null,
30263                 streetName: null,
30264                 streetNumber: null,
30265                 city: null,
30266                 district: null,
30267                 state: null,
30268                 stateOrProvince: null
30269             },
30270             settings: this,
30271             domContainer: this.el.dom,
30272             geodecoder: new google.maps.Geocoder()
30273         };
30274     },
30275     
30276     drawCircle: function(center, radius, options) 
30277     {
30278         if (this.gMapContext.circle != null) {
30279             this.gMapContext.circle.setMap(null);
30280         }
30281         if (radius > 0) {
30282             radius *= 1;
30283             options = Roo.apply({}, options, {
30284                 strokeColor: "#0000FF",
30285                 strokeOpacity: .35,
30286                 strokeWeight: 2,
30287                 fillColor: "#0000FF",
30288                 fillOpacity: .2
30289             });
30290             
30291             options.map = this.gMapContext.map;
30292             options.radius = radius;
30293             options.center = center;
30294             this.gMapContext.circle = new google.maps.Circle(options);
30295             return this.gMapContext.circle;
30296         }
30297         
30298         return null;
30299     },
30300     
30301     setPosition: function(location) 
30302     {
30303         this.gMapContext.location = location;
30304         this.gMapContext.marker.setPosition(location);
30305         this.gMapContext.map.panTo(location);
30306         this.drawCircle(location, this.gMapContext.radius, {});
30307         
30308         var _this = this;
30309         
30310         if (this.gMapContext.settings.enableReverseGeocode) {
30311             this.gMapContext.geodecoder.geocode({
30312                 latLng: this.gMapContext.location
30313             }, function(results, status) {
30314                 
30315                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30316                     _this.gMapContext.locationName = results[0].formatted_address;
30317                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30318                     
30319                     _this.fireEvent('positionchanged', this, location);
30320                 }
30321             });
30322             
30323             return;
30324         }
30325         
30326         this.fireEvent('positionchanged', this, location);
30327     },
30328     
30329     resize: function()
30330     {
30331         google.maps.event.trigger(this.gMapContext.map, "resize");
30332         
30333         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30334         
30335         this.fireEvent('resize', this);
30336     },
30337     
30338     setPositionByLatLng: function(latitude, longitude)
30339     {
30340         this.setPosition(new google.maps.LatLng(latitude, longitude));
30341     },
30342     
30343     getCurrentPosition: function() 
30344     {
30345         return {
30346             latitude: this.gMapContext.location.lat(),
30347             longitude: this.gMapContext.location.lng()
30348         };
30349     },
30350     
30351     getAddressName: function() 
30352     {
30353         return this.gMapContext.locationName;
30354     },
30355     
30356     getAddressComponents: function() 
30357     {
30358         return this.gMapContext.addressComponents;
30359     },
30360     
30361     address_component_from_google_geocode: function(address_components) 
30362     {
30363         var result = {};
30364         
30365         for (var i = 0; i < address_components.length; i++) {
30366             var component = address_components[i];
30367             if (component.types.indexOf("postal_code") >= 0) {
30368                 result.postalCode = component.short_name;
30369             } else if (component.types.indexOf("street_number") >= 0) {
30370                 result.streetNumber = component.short_name;
30371             } else if (component.types.indexOf("route") >= 0) {
30372                 result.streetName = component.short_name;
30373             } else if (component.types.indexOf("neighborhood") >= 0) {
30374                 result.city = component.short_name;
30375             } else if (component.types.indexOf("locality") >= 0) {
30376                 result.city = component.short_name;
30377             } else if (component.types.indexOf("sublocality") >= 0) {
30378                 result.district = component.short_name;
30379             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30380                 result.stateOrProvince = component.short_name;
30381             } else if (component.types.indexOf("country") >= 0) {
30382                 result.country = component.short_name;
30383             }
30384         }
30385         
30386         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30387         result.addressLine2 = "";
30388         return result;
30389     },
30390     
30391     setZoomLevel: function(zoom)
30392     {
30393         this.gMapContext.map.setZoom(zoom);
30394     },
30395     
30396     show: function()
30397     {
30398         if(!this.el){
30399             return;
30400         }
30401         
30402         this.el.show();
30403         
30404         this.resize();
30405         
30406         this.fireEvent('show', this);
30407     },
30408     
30409     hide: function()
30410     {
30411         if(!this.el){
30412             return;
30413         }
30414         
30415         this.el.hide();
30416         
30417         this.fireEvent('hide', this);
30418     }
30419     
30420 });
30421
30422 Roo.apply(Roo.bootstrap.LocationPicker, {
30423     
30424     OverlayView : function(map, options)
30425     {
30426         options = options || {};
30427         
30428         this.setMap(map);
30429     }
30430     
30431     
30432 });/**
30433  * @class Roo.bootstrap.Alert
30434  * @extends Roo.bootstrap.Component
30435  * Bootstrap Alert class - shows an alert area box
30436  * eg
30437  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30438   Enter a valid email address
30439 </div>
30440  * @licence LGPL
30441  * @cfg {String} title The title of alert
30442  * @cfg {String} html The content of alert
30443  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30444  * @cfg {String} fa font-awesomeicon
30445  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30446  * @cfg {Boolean} close true to show a x closer
30447  * 
30448  * 
30449  * @constructor
30450  * Create a new alert
30451  * @param {Object} config The config object
30452  */
30453
30454
30455 Roo.bootstrap.Alert = function(config){
30456     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30457     
30458 };
30459
30460 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30461     
30462     title: '',
30463     html: '',
30464     weight: false,
30465     fa: false,
30466     faicon: false, // BC
30467     close : false,
30468     
30469     
30470     getAutoCreate : function()
30471     {
30472         
30473         var cfg = {
30474             tag : 'div',
30475             cls : 'alert',
30476             cn : [
30477                 {
30478                     tag: 'button',
30479                     type :  "button",
30480                     cls: "close",
30481                     html : '×',
30482                     style : this.close ? '' : 'display:none'
30483                 },
30484                 {
30485                     tag : 'i',
30486                     cls : 'roo-alert-icon'
30487                     
30488                 },
30489                 {
30490                     tag : 'b',
30491                     cls : 'roo-alert-title',
30492                     html : this.title
30493                 },
30494                 {
30495                     tag : 'span',
30496                     cls : 'roo-alert-text',
30497                     html : this.html
30498                 }
30499             ]
30500         };
30501         
30502         if(this.faicon){
30503             cfg.cn[0].cls += ' fa ' + this.faicon;
30504         }
30505         if(this.fa){
30506             cfg.cn[0].cls += ' fa ' + this.fa;
30507         }
30508         
30509         if(this.weight){
30510             cfg.cls += ' alert-' + this.weight;
30511         }
30512         
30513         return cfg;
30514     },
30515     
30516     initEvents: function() 
30517     {
30518         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30519         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30520         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30521         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30522         if (this.seconds > 0) {
30523             this.hide.defer(this.seconds, this);
30524         }
30525     },
30526     /**
30527      * Set the Title Message HTML
30528      * @param {String} html
30529      */
30530     setTitle : function(str)
30531     {
30532         this.titleEl.dom.innerHTML = str;
30533     },
30534      
30535      /**
30536      * Set the Body Message HTML
30537      * @param {String} html
30538      */
30539     setHtml : function(str)
30540     {
30541         this.htmlEl.dom.innerHTML = str;
30542     },
30543     /**
30544      * Set the Weight of the alert
30545      * @param {String} (success|info|warning|danger) weight
30546      */
30547     
30548     setWeight : function(weight)
30549     {
30550         if(this.weight){
30551             this.el.removeClass('alert-' + this.weight);
30552         }
30553         
30554         this.weight = weight;
30555         
30556         this.el.addClass('alert-' + this.weight);
30557     },
30558       /**
30559      * Set the Icon of the alert
30560      * @param {String} see fontawsome names (name without the 'fa-' bit)
30561      */
30562     setIcon : function(icon)
30563     {
30564         if(this.faicon){
30565             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30566         }
30567         
30568         this.faicon = icon;
30569         
30570         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30571     },
30572     /**
30573      * Hide the Alert
30574      */
30575     hide: function() 
30576     {
30577         this.el.hide();   
30578     },
30579     /**
30580      * Show the Alert
30581      */
30582     show: function() 
30583     {  
30584         this.el.show();   
30585     }
30586     
30587 });
30588
30589  
30590 /*
30591 * Licence: LGPL
30592 */
30593
30594 /**
30595  * @class Roo.bootstrap.UploadCropbox
30596  * @extends Roo.bootstrap.Component
30597  * Bootstrap UploadCropbox class
30598  * @cfg {String} emptyText show when image has been loaded
30599  * @cfg {String} rotateNotify show when image too small to rotate
30600  * @cfg {Number} errorTimeout default 3000
30601  * @cfg {Number} minWidth default 300
30602  * @cfg {Number} minHeight default 300
30603  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30604  * @cfg {Boolean} isDocument (true|false) default false
30605  * @cfg {String} url action url
30606  * @cfg {String} paramName default 'imageUpload'
30607  * @cfg {String} method default POST
30608  * @cfg {Boolean} loadMask (true|false) default true
30609  * @cfg {Boolean} loadingText default 'Loading...'
30610  * 
30611  * @constructor
30612  * Create a new UploadCropbox
30613  * @param {Object} config The config object
30614  */
30615
30616 Roo.bootstrap.UploadCropbox = function(config){
30617     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30618     
30619     this.addEvents({
30620         /**
30621          * @event beforeselectfile
30622          * Fire before select file
30623          * @param {Roo.bootstrap.UploadCropbox} this
30624          */
30625         "beforeselectfile" : true,
30626         /**
30627          * @event initial
30628          * Fire after initEvent
30629          * @param {Roo.bootstrap.UploadCropbox} this
30630          */
30631         "initial" : true,
30632         /**
30633          * @event crop
30634          * Fire after initEvent
30635          * @param {Roo.bootstrap.UploadCropbox} this
30636          * @param {String} data
30637          */
30638         "crop" : true,
30639         /**
30640          * @event prepare
30641          * Fire when preparing the file data
30642          * @param {Roo.bootstrap.UploadCropbox} this
30643          * @param {Object} file
30644          */
30645         "prepare" : true,
30646         /**
30647          * @event exception
30648          * Fire when get exception
30649          * @param {Roo.bootstrap.UploadCropbox} this
30650          * @param {XMLHttpRequest} xhr
30651          */
30652         "exception" : true,
30653         /**
30654          * @event beforeloadcanvas
30655          * Fire before load the canvas
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          * @param {String} src
30658          */
30659         "beforeloadcanvas" : true,
30660         /**
30661          * @event trash
30662          * Fire when trash image
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          */
30665         "trash" : true,
30666         /**
30667          * @event download
30668          * Fire when download the image
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          */
30671         "download" : true,
30672         /**
30673          * @event footerbuttonclick
30674          * Fire when footerbuttonclick
30675          * @param {Roo.bootstrap.UploadCropbox} this
30676          * @param {String} type
30677          */
30678         "footerbuttonclick" : true,
30679         /**
30680          * @event resize
30681          * Fire when resize
30682          * @param {Roo.bootstrap.UploadCropbox} this
30683          */
30684         "resize" : true,
30685         /**
30686          * @event rotate
30687          * Fire when rotate the image
30688          * @param {Roo.bootstrap.UploadCropbox} this
30689          * @param {String} pos
30690          */
30691         "rotate" : true,
30692         /**
30693          * @event inspect
30694          * Fire when inspect the file
30695          * @param {Roo.bootstrap.UploadCropbox} this
30696          * @param {Object} file
30697          */
30698         "inspect" : true,
30699         /**
30700          * @event upload
30701          * Fire when xhr upload the file
30702          * @param {Roo.bootstrap.UploadCropbox} this
30703          * @param {Object} data
30704          */
30705         "upload" : true,
30706         /**
30707          * @event arrange
30708          * Fire when arrange the file data
30709          * @param {Roo.bootstrap.UploadCropbox} this
30710          * @param {Object} formData
30711          */
30712         "arrange" : true
30713     });
30714     
30715     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30716 };
30717
30718 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30719     
30720     emptyText : 'Click to upload image',
30721     rotateNotify : 'Image is too small to rotate',
30722     errorTimeout : 3000,
30723     scale : 0,
30724     baseScale : 1,
30725     rotate : 0,
30726     dragable : false,
30727     pinching : false,
30728     mouseX : 0,
30729     mouseY : 0,
30730     cropData : false,
30731     minWidth : 300,
30732     minHeight : 300,
30733     file : false,
30734     exif : {},
30735     baseRotate : 1,
30736     cropType : 'image/jpeg',
30737     buttons : false,
30738     canvasLoaded : false,
30739     isDocument : false,
30740     method : 'POST',
30741     paramName : 'imageUpload',
30742     loadMask : true,
30743     loadingText : 'Loading...',
30744     maskEl : false,
30745     
30746     getAutoCreate : function()
30747     {
30748         var cfg = {
30749             tag : 'div',
30750             cls : 'roo-upload-cropbox',
30751             cn : [
30752                 {
30753                     tag : 'input',
30754                     cls : 'roo-upload-cropbox-selector',
30755                     type : 'file'
30756                 },
30757                 {
30758                     tag : 'div',
30759                     cls : 'roo-upload-cropbox-body',
30760                     style : 'cursor:pointer',
30761                     cn : [
30762                         {
30763                             tag : 'div',
30764                             cls : 'roo-upload-cropbox-preview'
30765                         },
30766                         {
30767                             tag : 'div',
30768                             cls : 'roo-upload-cropbox-thumb'
30769                         },
30770                         {
30771                             tag : 'div',
30772                             cls : 'roo-upload-cropbox-empty-notify',
30773                             html : this.emptyText
30774                         },
30775                         {
30776                             tag : 'div',
30777                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30778                             html : this.rotateNotify
30779                         }
30780                     ]
30781                 },
30782                 {
30783                     tag : 'div',
30784                     cls : 'roo-upload-cropbox-footer',
30785                     cn : {
30786                         tag : 'div',
30787                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30788                         cn : []
30789                     }
30790                 }
30791             ]
30792         };
30793         
30794         return cfg;
30795     },
30796     
30797     onRender : function(ct, position)
30798     {
30799         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30800         
30801         if (this.buttons.length) {
30802             
30803             Roo.each(this.buttons, function(bb) {
30804                 
30805                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30806                 
30807                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30808                 
30809             }, this);
30810         }
30811         
30812         if(this.loadMask){
30813             this.maskEl = this.el;
30814         }
30815     },
30816     
30817     initEvents : function()
30818     {
30819         this.urlAPI = (window.createObjectURL && window) || 
30820                                 (window.URL && URL.revokeObjectURL && URL) || 
30821                                 (window.webkitURL && webkitURL);
30822                         
30823         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30824         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30825         
30826         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30827         this.selectorEl.hide();
30828         
30829         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30830         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30831         
30832         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30833         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30834         this.thumbEl.hide();
30835         
30836         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30837         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30838         
30839         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30840         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30841         this.errorEl.hide();
30842         
30843         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30844         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30845         this.footerEl.hide();
30846         
30847         this.setThumbBoxSize();
30848         
30849         this.bind();
30850         
30851         this.resize();
30852         
30853         this.fireEvent('initial', this);
30854     },
30855
30856     bind : function()
30857     {
30858         var _this = this;
30859         
30860         window.addEventListener("resize", function() { _this.resize(); } );
30861         
30862         this.bodyEl.on('click', this.beforeSelectFile, this);
30863         
30864         if(Roo.isTouch){
30865             this.bodyEl.on('touchstart', this.onTouchStart, this);
30866             this.bodyEl.on('touchmove', this.onTouchMove, this);
30867             this.bodyEl.on('touchend', this.onTouchEnd, this);
30868         }
30869         
30870         if(!Roo.isTouch){
30871             this.bodyEl.on('mousedown', this.onMouseDown, this);
30872             this.bodyEl.on('mousemove', this.onMouseMove, this);
30873             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30874             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30875             Roo.get(document).on('mouseup', this.onMouseUp, this);
30876         }
30877         
30878         this.selectorEl.on('change', this.onFileSelected, this);
30879     },
30880     
30881     reset : function()
30882     {    
30883         this.scale = 0;
30884         this.baseScale = 1;
30885         this.rotate = 0;
30886         this.baseRotate = 1;
30887         this.dragable = false;
30888         this.pinching = false;
30889         this.mouseX = 0;
30890         this.mouseY = 0;
30891         this.cropData = false;
30892         this.notifyEl.dom.innerHTML = this.emptyText;
30893         
30894         this.selectorEl.dom.value = '';
30895         
30896     },
30897     
30898     resize : function()
30899     {
30900         if(this.fireEvent('resize', this) != false){
30901             this.setThumbBoxPosition();
30902             this.setCanvasPosition();
30903         }
30904     },
30905     
30906     onFooterButtonClick : function(e, el, o, type)
30907     {
30908         switch (type) {
30909             case 'rotate-left' :
30910                 this.onRotateLeft(e);
30911                 break;
30912             case 'rotate-right' :
30913                 this.onRotateRight(e);
30914                 break;
30915             case 'picture' :
30916                 this.beforeSelectFile(e);
30917                 break;
30918             case 'trash' :
30919                 this.trash(e);
30920                 break;
30921             case 'crop' :
30922                 this.crop(e);
30923                 break;
30924             case 'download' :
30925                 this.download(e);
30926                 break;
30927             default :
30928                 break;
30929         }
30930         
30931         this.fireEvent('footerbuttonclick', this, type);
30932     },
30933     
30934     beforeSelectFile : function(e)
30935     {
30936         e.preventDefault();
30937         
30938         if(this.fireEvent('beforeselectfile', this) != false){
30939             this.selectorEl.dom.click();
30940         }
30941     },
30942     
30943     onFileSelected : function(e)
30944     {
30945         e.preventDefault();
30946         
30947         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30948             return;
30949         }
30950         
30951         var file = this.selectorEl.dom.files[0];
30952         
30953         if(this.fireEvent('inspect', this, file) != false){
30954             this.prepare(file);
30955         }
30956         
30957     },
30958     
30959     trash : function(e)
30960     {
30961         this.fireEvent('trash', this);
30962     },
30963     
30964     download : function(e)
30965     {
30966         this.fireEvent('download', this);
30967     },
30968     
30969     loadCanvas : function(src)
30970     {   
30971         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30972             
30973             this.reset();
30974             
30975             this.imageEl = document.createElement('img');
30976             
30977             var _this = this;
30978             
30979             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30980             
30981             this.imageEl.src = src;
30982         }
30983     },
30984     
30985     onLoadCanvas : function()
30986     {   
30987         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30988         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30989         
30990         this.bodyEl.un('click', this.beforeSelectFile, this);
30991         
30992         this.notifyEl.hide();
30993         this.thumbEl.show();
30994         this.footerEl.show();
30995         
30996         this.baseRotateLevel();
30997         
30998         if(this.isDocument){
30999             this.setThumbBoxSize();
31000         }
31001         
31002         this.setThumbBoxPosition();
31003         
31004         this.baseScaleLevel();
31005         
31006         this.draw();
31007         
31008         this.resize();
31009         
31010         this.canvasLoaded = true;
31011         
31012         if(this.loadMask){
31013             this.maskEl.unmask();
31014         }
31015         
31016     },
31017     
31018     setCanvasPosition : function()
31019     {   
31020         if(!this.canvasEl){
31021             return;
31022         }
31023         
31024         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31025         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31026         
31027         this.previewEl.setLeft(pw);
31028         this.previewEl.setTop(ph);
31029         
31030     },
31031     
31032     onMouseDown : function(e)
31033     {   
31034         e.stopEvent();
31035         
31036         this.dragable = true;
31037         this.pinching = false;
31038         
31039         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31040             this.dragable = false;
31041             return;
31042         }
31043         
31044         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31045         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31046         
31047     },
31048     
31049     onMouseMove : function(e)
31050     {   
31051         e.stopEvent();
31052         
31053         if(!this.canvasLoaded){
31054             return;
31055         }
31056         
31057         if (!this.dragable){
31058             return;
31059         }
31060         
31061         var minX = Math.ceil(this.thumbEl.getLeft(true));
31062         var minY = Math.ceil(this.thumbEl.getTop(true));
31063         
31064         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31065         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31066         
31067         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31068         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31069         
31070         x = x - this.mouseX;
31071         y = y - this.mouseY;
31072         
31073         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31074         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31075         
31076         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31077         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31078         
31079         this.previewEl.setLeft(bgX);
31080         this.previewEl.setTop(bgY);
31081         
31082         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31083         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31084     },
31085     
31086     onMouseUp : function(e)
31087     {   
31088         e.stopEvent();
31089         
31090         this.dragable = false;
31091     },
31092     
31093     onMouseWheel : function(e)
31094     {   
31095         e.stopEvent();
31096         
31097         this.startScale = this.scale;
31098         
31099         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31100         
31101         if(!this.zoomable()){
31102             this.scale = this.startScale;
31103             return;
31104         }
31105         
31106         this.draw();
31107         
31108         return;
31109     },
31110     
31111     zoomable : function()
31112     {
31113         var minScale = this.thumbEl.getWidth() / this.minWidth;
31114         
31115         if(this.minWidth < this.minHeight){
31116             minScale = this.thumbEl.getHeight() / this.minHeight;
31117         }
31118         
31119         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31120         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31121         
31122         if(
31123                 this.isDocument &&
31124                 (this.rotate == 0 || this.rotate == 180) && 
31125                 (
31126                     width > this.imageEl.OriginWidth || 
31127                     height > this.imageEl.OriginHeight ||
31128                     (width < this.minWidth && height < this.minHeight)
31129                 )
31130         ){
31131             return false;
31132         }
31133         
31134         if(
31135                 this.isDocument &&
31136                 (this.rotate == 90 || this.rotate == 270) && 
31137                 (
31138                     width > this.imageEl.OriginWidth || 
31139                     height > this.imageEl.OriginHeight ||
31140                     (width < this.minHeight && height < this.minWidth)
31141                 )
31142         ){
31143             return false;
31144         }
31145         
31146         if(
31147                 !this.isDocument &&
31148                 (this.rotate == 0 || this.rotate == 180) && 
31149                 (
31150                     width < this.minWidth || 
31151                     width > this.imageEl.OriginWidth || 
31152                     height < this.minHeight || 
31153                     height > this.imageEl.OriginHeight
31154                 )
31155         ){
31156             return false;
31157         }
31158         
31159         if(
31160                 !this.isDocument &&
31161                 (this.rotate == 90 || this.rotate == 270) && 
31162                 (
31163                     width < this.minHeight || 
31164                     width > this.imageEl.OriginWidth || 
31165                     height < this.minWidth || 
31166                     height > this.imageEl.OriginHeight
31167                 )
31168         ){
31169             return false;
31170         }
31171         
31172         return true;
31173         
31174     },
31175     
31176     onRotateLeft : function(e)
31177     {   
31178         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31179             
31180             var minScale = this.thumbEl.getWidth() / this.minWidth;
31181             
31182             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31183             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31184             
31185             this.startScale = this.scale;
31186             
31187             while (this.getScaleLevel() < minScale){
31188             
31189                 this.scale = this.scale + 1;
31190                 
31191                 if(!this.zoomable()){
31192                     break;
31193                 }
31194                 
31195                 if(
31196                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31197                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31198                 ){
31199                     continue;
31200                 }
31201                 
31202                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31203
31204                 this.draw();
31205                 
31206                 return;
31207             }
31208             
31209             this.scale = this.startScale;
31210             
31211             this.onRotateFail();
31212             
31213             return false;
31214         }
31215         
31216         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31217
31218         if(this.isDocument){
31219             this.setThumbBoxSize();
31220             this.setThumbBoxPosition();
31221             this.setCanvasPosition();
31222         }
31223         
31224         this.draw();
31225         
31226         this.fireEvent('rotate', this, 'left');
31227         
31228     },
31229     
31230     onRotateRight : function(e)
31231     {
31232         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31233             
31234             var minScale = this.thumbEl.getWidth() / this.minWidth;
31235         
31236             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31237             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31238             
31239             this.startScale = this.scale;
31240             
31241             while (this.getScaleLevel() < minScale){
31242             
31243                 this.scale = this.scale + 1;
31244                 
31245                 if(!this.zoomable()){
31246                     break;
31247                 }
31248                 
31249                 if(
31250                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31251                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31252                 ){
31253                     continue;
31254                 }
31255                 
31256                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31257
31258                 this.draw();
31259                 
31260                 return;
31261             }
31262             
31263             this.scale = this.startScale;
31264             
31265             this.onRotateFail();
31266             
31267             return false;
31268         }
31269         
31270         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31271
31272         if(this.isDocument){
31273             this.setThumbBoxSize();
31274             this.setThumbBoxPosition();
31275             this.setCanvasPosition();
31276         }
31277         
31278         this.draw();
31279         
31280         this.fireEvent('rotate', this, 'right');
31281     },
31282     
31283     onRotateFail : function()
31284     {
31285         this.errorEl.show(true);
31286         
31287         var _this = this;
31288         
31289         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31290     },
31291     
31292     draw : function()
31293     {
31294         this.previewEl.dom.innerHTML = '';
31295         
31296         var canvasEl = document.createElement("canvas");
31297         
31298         var contextEl = canvasEl.getContext("2d");
31299         
31300         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31301         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31302         var center = this.imageEl.OriginWidth / 2;
31303         
31304         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31305             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31306             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31307             center = this.imageEl.OriginHeight / 2;
31308         }
31309         
31310         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31311         
31312         contextEl.translate(center, center);
31313         contextEl.rotate(this.rotate * Math.PI / 180);
31314
31315         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31316         
31317         this.canvasEl = document.createElement("canvas");
31318         
31319         this.contextEl = this.canvasEl.getContext("2d");
31320         
31321         switch (this.rotate) {
31322             case 0 :
31323                 
31324                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31325                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31326                 
31327                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31328                 
31329                 break;
31330             case 90 : 
31331                 
31332                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31333                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31334                 
31335                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31336                     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);
31337                     break;
31338                 }
31339                 
31340                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31341                 
31342                 break;
31343             case 180 :
31344                 
31345                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31346                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31347                 
31348                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31349                     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);
31350                     break;
31351                 }
31352                 
31353                 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);
31354                 
31355                 break;
31356             case 270 :
31357                 
31358                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31359                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31360         
31361                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31362                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31363                     break;
31364                 }
31365                 
31366                 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);
31367                 
31368                 break;
31369             default : 
31370                 break;
31371         }
31372         
31373         this.previewEl.appendChild(this.canvasEl);
31374         
31375         this.setCanvasPosition();
31376     },
31377     
31378     crop : function()
31379     {
31380         if(!this.canvasLoaded){
31381             return;
31382         }
31383         
31384         var imageCanvas = document.createElement("canvas");
31385         
31386         var imageContext = imageCanvas.getContext("2d");
31387         
31388         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31389         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31390         
31391         var center = imageCanvas.width / 2;
31392         
31393         imageContext.translate(center, center);
31394         
31395         imageContext.rotate(this.rotate * Math.PI / 180);
31396         
31397         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31398         
31399         var canvas = document.createElement("canvas");
31400         
31401         var context = canvas.getContext("2d");
31402                 
31403         canvas.width = this.minWidth;
31404         canvas.height = this.minHeight;
31405
31406         switch (this.rotate) {
31407             case 0 :
31408                 
31409                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31410                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (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                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31445                 
31446                 break;
31447             case 90 : 
31448                 
31449                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31450                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31451                 
31452                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31453                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31454                 
31455                 var targetWidth = this.minWidth - 2 * x;
31456                 var targetHeight = this.minHeight - 2 * y;
31457                 
31458                 var scale = 1;
31459                 
31460                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31461                     scale = targetWidth / width;
31462                 }
31463                 
31464                 if(x > 0 && y == 0){
31465                     scale = targetHeight / height;
31466                 }
31467                 
31468                 if(x > 0 && y > 0){
31469                     scale = targetWidth / width;
31470                     
31471                     if(width < height){
31472                         scale = targetHeight / height;
31473                     }
31474                 }
31475                 
31476                 context.scale(scale, scale);
31477                 
31478                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31479                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31480
31481                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31482                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31483                 
31484                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31485                 
31486                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31487                 
31488                 break;
31489             case 180 :
31490                 
31491                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31492                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31493                 
31494                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31495                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31496                 
31497                 var targetWidth = this.minWidth - 2 * x;
31498                 var targetHeight = this.minHeight - 2 * y;
31499                 
31500                 var scale = 1;
31501                 
31502                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31503                     scale = targetWidth / width;
31504                 }
31505                 
31506                 if(x > 0 && y == 0){
31507                     scale = targetHeight / height;
31508                 }
31509                 
31510                 if(x > 0 && y > 0){
31511                     scale = targetWidth / width;
31512                     
31513                     if(width < height){
31514                         scale = targetHeight / height;
31515                     }
31516                 }
31517                 
31518                 context.scale(scale, scale);
31519                 
31520                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31521                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31522
31523                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31524                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31525
31526                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31527                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31528                 
31529                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31530                 
31531                 break;
31532             case 270 :
31533                 
31534                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31535                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31536                 
31537                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31538                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31539                 
31540                 var targetWidth = this.minWidth - 2 * x;
31541                 var targetHeight = this.minHeight - 2 * y;
31542                 
31543                 var scale = 1;
31544                 
31545                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31546                     scale = targetWidth / width;
31547                 }
31548                 
31549                 if(x > 0 && y == 0){
31550                     scale = targetHeight / height;
31551                 }
31552                 
31553                 if(x > 0 && y > 0){
31554                     scale = targetWidth / width;
31555                     
31556                     if(width < height){
31557                         scale = targetHeight / height;
31558                     }
31559                 }
31560                 
31561                 context.scale(scale, scale);
31562                 
31563                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31564                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31565
31566                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31567                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31568                 
31569                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31570                 
31571                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31572                 
31573                 break;
31574             default : 
31575                 break;
31576         }
31577         
31578         this.cropData = canvas.toDataURL(this.cropType);
31579         
31580         if(this.fireEvent('crop', this, this.cropData) !== false){
31581             this.process(this.file, this.cropData);
31582         }
31583         
31584         return;
31585         
31586     },
31587     
31588     setThumbBoxSize : function()
31589     {
31590         var width, height;
31591         
31592         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31593             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31594             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31595             
31596             this.minWidth = width;
31597             this.minHeight = height;
31598             
31599             if(this.rotate == 90 || this.rotate == 270){
31600                 this.minWidth = height;
31601                 this.minHeight = width;
31602             }
31603         }
31604         
31605         height = 300;
31606         width = Math.ceil(this.minWidth * height / this.minHeight);
31607         
31608         if(this.minWidth > this.minHeight){
31609             width = 300;
31610             height = Math.ceil(this.minHeight * width / this.minWidth);
31611         }
31612         
31613         this.thumbEl.setStyle({
31614             width : width + 'px',
31615             height : height + 'px'
31616         });
31617
31618         return;
31619             
31620     },
31621     
31622     setThumbBoxPosition : function()
31623     {
31624         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31625         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31626         
31627         this.thumbEl.setLeft(x);
31628         this.thumbEl.setTop(y);
31629         
31630     },
31631     
31632     baseRotateLevel : function()
31633     {
31634         this.baseRotate = 1;
31635         
31636         if(
31637                 typeof(this.exif) != 'undefined' &&
31638                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31639                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31640         ){
31641             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31642         }
31643         
31644         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31645         
31646     },
31647     
31648     baseScaleLevel : function()
31649     {
31650         var width, height;
31651         
31652         if(this.isDocument){
31653             
31654             if(this.baseRotate == 6 || this.baseRotate == 8){
31655             
31656                 height = this.thumbEl.getHeight();
31657                 this.baseScale = height / this.imageEl.OriginWidth;
31658
31659                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31660                     width = this.thumbEl.getWidth();
31661                     this.baseScale = width / this.imageEl.OriginHeight;
31662                 }
31663
31664                 return;
31665             }
31666
31667             height = this.thumbEl.getHeight();
31668             this.baseScale = height / this.imageEl.OriginHeight;
31669
31670             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31671                 width = this.thumbEl.getWidth();
31672                 this.baseScale = width / this.imageEl.OriginWidth;
31673             }
31674
31675             return;
31676         }
31677         
31678         if(this.baseRotate == 6 || this.baseRotate == 8){
31679             
31680             width = this.thumbEl.getHeight();
31681             this.baseScale = width / this.imageEl.OriginHeight;
31682             
31683             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31684                 height = this.thumbEl.getWidth();
31685                 this.baseScale = height / this.imageEl.OriginHeight;
31686             }
31687             
31688             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31689                 height = this.thumbEl.getWidth();
31690                 this.baseScale = height / this.imageEl.OriginHeight;
31691                 
31692                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31693                     width = this.thumbEl.getHeight();
31694                     this.baseScale = width / this.imageEl.OriginWidth;
31695                 }
31696             }
31697             
31698             return;
31699         }
31700         
31701         width = this.thumbEl.getWidth();
31702         this.baseScale = width / this.imageEl.OriginWidth;
31703         
31704         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31705             height = this.thumbEl.getHeight();
31706             this.baseScale = height / this.imageEl.OriginHeight;
31707         }
31708         
31709         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31710             
31711             height = this.thumbEl.getHeight();
31712             this.baseScale = height / this.imageEl.OriginHeight;
31713             
31714             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31715                 width = this.thumbEl.getWidth();
31716                 this.baseScale = width / this.imageEl.OriginWidth;
31717             }
31718             
31719         }
31720         
31721         return;
31722     },
31723     
31724     getScaleLevel : function()
31725     {
31726         return this.baseScale * Math.pow(1.1, this.scale);
31727     },
31728     
31729     onTouchStart : function(e)
31730     {
31731         if(!this.canvasLoaded){
31732             this.beforeSelectFile(e);
31733             return;
31734         }
31735         
31736         var touches = e.browserEvent.touches;
31737         
31738         if(!touches){
31739             return;
31740         }
31741         
31742         if(touches.length == 1){
31743             this.onMouseDown(e);
31744             return;
31745         }
31746         
31747         if(touches.length != 2){
31748             return;
31749         }
31750         
31751         var coords = [];
31752         
31753         for(var i = 0, finger; finger = touches[i]; i++){
31754             coords.push(finger.pageX, finger.pageY);
31755         }
31756         
31757         var x = Math.pow(coords[0] - coords[2], 2);
31758         var y = Math.pow(coords[1] - coords[3], 2);
31759         
31760         this.startDistance = Math.sqrt(x + y);
31761         
31762         this.startScale = this.scale;
31763         
31764         this.pinching = true;
31765         this.dragable = false;
31766         
31767     },
31768     
31769     onTouchMove : function(e)
31770     {
31771         if(!this.pinching && !this.dragable){
31772             return;
31773         }
31774         
31775         var touches = e.browserEvent.touches;
31776         
31777         if(!touches){
31778             return;
31779         }
31780         
31781         if(this.dragable){
31782             this.onMouseMove(e);
31783             return;
31784         }
31785         
31786         var coords = [];
31787         
31788         for(var i = 0, finger; finger = touches[i]; i++){
31789             coords.push(finger.pageX, finger.pageY);
31790         }
31791         
31792         var x = Math.pow(coords[0] - coords[2], 2);
31793         var y = Math.pow(coords[1] - coords[3], 2);
31794         
31795         this.endDistance = Math.sqrt(x + y);
31796         
31797         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31798         
31799         if(!this.zoomable()){
31800             this.scale = this.startScale;
31801             return;
31802         }
31803         
31804         this.draw();
31805         
31806     },
31807     
31808     onTouchEnd : function(e)
31809     {
31810         this.pinching = false;
31811         this.dragable = false;
31812         
31813     },
31814     
31815     process : function(file, crop)
31816     {
31817         if(this.loadMask){
31818             this.maskEl.mask(this.loadingText);
31819         }
31820         
31821         this.xhr = new XMLHttpRequest();
31822         
31823         file.xhr = this.xhr;
31824
31825         this.xhr.open(this.method, this.url, true);
31826         
31827         var headers = {
31828             "Accept": "application/json",
31829             "Cache-Control": "no-cache",
31830             "X-Requested-With": "XMLHttpRequest"
31831         };
31832         
31833         for (var headerName in headers) {
31834             var headerValue = headers[headerName];
31835             if (headerValue) {
31836                 this.xhr.setRequestHeader(headerName, headerValue);
31837             }
31838         }
31839         
31840         var _this = this;
31841         
31842         this.xhr.onload = function()
31843         {
31844             _this.xhrOnLoad(_this.xhr);
31845         }
31846         
31847         this.xhr.onerror = function()
31848         {
31849             _this.xhrOnError(_this.xhr);
31850         }
31851         
31852         var formData = new FormData();
31853
31854         formData.append('returnHTML', 'NO');
31855         
31856         if(crop){
31857             formData.append('crop', crop);
31858         }
31859         
31860         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31861             formData.append(this.paramName, file, file.name);
31862         }
31863         
31864         if(typeof(file.filename) != 'undefined'){
31865             formData.append('filename', file.filename);
31866         }
31867         
31868         if(typeof(file.mimetype) != 'undefined'){
31869             formData.append('mimetype', file.mimetype);
31870         }
31871         
31872         if(this.fireEvent('arrange', this, formData) != false){
31873             this.xhr.send(formData);
31874         };
31875     },
31876     
31877     xhrOnLoad : function(xhr)
31878     {
31879         if(this.loadMask){
31880             this.maskEl.unmask();
31881         }
31882         
31883         if (xhr.readyState !== 4) {
31884             this.fireEvent('exception', this, xhr);
31885             return;
31886         }
31887
31888         var response = Roo.decode(xhr.responseText);
31889         
31890         if(!response.success){
31891             this.fireEvent('exception', this, xhr);
31892             return;
31893         }
31894         
31895         var response = Roo.decode(xhr.responseText);
31896         
31897         this.fireEvent('upload', this, response);
31898         
31899     },
31900     
31901     xhrOnError : function()
31902     {
31903         if(this.loadMask){
31904             this.maskEl.unmask();
31905         }
31906         
31907         Roo.log('xhr on error');
31908         
31909         var response = Roo.decode(xhr.responseText);
31910           
31911         Roo.log(response);
31912         
31913     },
31914     
31915     prepare : function(file)
31916     {   
31917         if(this.loadMask){
31918             this.maskEl.mask(this.loadingText);
31919         }
31920         
31921         this.file = false;
31922         this.exif = {};
31923         
31924         if(typeof(file) === 'string'){
31925             this.loadCanvas(file);
31926             return;
31927         }
31928         
31929         if(!file || !this.urlAPI){
31930             return;
31931         }
31932         
31933         this.file = file;
31934         this.cropType = file.type;
31935         
31936         var _this = this;
31937         
31938         if(this.fireEvent('prepare', this, this.file) != false){
31939             
31940             var reader = new FileReader();
31941             
31942             reader.onload = function (e) {
31943                 if (e.target.error) {
31944                     Roo.log(e.target.error);
31945                     return;
31946                 }
31947                 
31948                 var buffer = e.target.result,
31949                     dataView = new DataView(buffer),
31950                     offset = 2,
31951                     maxOffset = dataView.byteLength - 4,
31952                     markerBytes,
31953                     markerLength;
31954                 
31955                 if (dataView.getUint16(0) === 0xffd8) {
31956                     while (offset < maxOffset) {
31957                         markerBytes = dataView.getUint16(offset);
31958                         
31959                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31960                             markerLength = dataView.getUint16(offset + 2) + 2;
31961                             if (offset + markerLength > dataView.byteLength) {
31962                                 Roo.log('Invalid meta data: Invalid segment size.');
31963                                 break;
31964                             }
31965                             
31966                             if(markerBytes == 0xffe1){
31967                                 _this.parseExifData(
31968                                     dataView,
31969                                     offset,
31970                                     markerLength
31971                                 );
31972                             }
31973                             
31974                             offset += markerLength;
31975                             
31976                             continue;
31977                         }
31978                         
31979                         break;
31980                     }
31981                     
31982                 }
31983                 
31984                 var url = _this.urlAPI.createObjectURL(_this.file);
31985                 
31986                 _this.loadCanvas(url);
31987                 
31988                 return;
31989             }
31990             
31991             reader.readAsArrayBuffer(this.file);
31992             
31993         }
31994         
31995     },
31996     
31997     parseExifData : function(dataView, offset, length)
31998     {
31999         var tiffOffset = offset + 10,
32000             littleEndian,
32001             dirOffset;
32002     
32003         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32004             // No Exif data, might be XMP data instead
32005             return;
32006         }
32007         
32008         // Check for the ASCII code for "Exif" (0x45786966):
32009         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32010             // No Exif data, might be XMP data instead
32011             return;
32012         }
32013         if (tiffOffset + 8 > dataView.byteLength) {
32014             Roo.log('Invalid Exif data: Invalid segment size.');
32015             return;
32016         }
32017         // Check for the two null bytes:
32018         if (dataView.getUint16(offset + 8) !== 0x0000) {
32019             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32020             return;
32021         }
32022         // Check the byte alignment:
32023         switch (dataView.getUint16(tiffOffset)) {
32024         case 0x4949:
32025             littleEndian = true;
32026             break;
32027         case 0x4D4D:
32028             littleEndian = false;
32029             break;
32030         default:
32031             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32032             return;
32033         }
32034         // Check for the TIFF tag marker (0x002A):
32035         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32036             Roo.log('Invalid Exif data: Missing TIFF marker.');
32037             return;
32038         }
32039         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32040         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32041         
32042         this.parseExifTags(
32043             dataView,
32044             tiffOffset,
32045             tiffOffset + dirOffset,
32046             littleEndian
32047         );
32048     },
32049     
32050     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32051     {
32052         var tagsNumber,
32053             dirEndOffset,
32054             i;
32055         if (dirOffset + 6 > dataView.byteLength) {
32056             Roo.log('Invalid Exif data: Invalid directory offset.');
32057             return;
32058         }
32059         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32060         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32061         if (dirEndOffset + 4 > dataView.byteLength) {
32062             Roo.log('Invalid Exif data: Invalid directory size.');
32063             return;
32064         }
32065         for (i = 0; i < tagsNumber; i += 1) {
32066             this.parseExifTag(
32067                 dataView,
32068                 tiffOffset,
32069                 dirOffset + 2 + 12 * i, // tag offset
32070                 littleEndian
32071             );
32072         }
32073         // Return the offset to the next directory:
32074         return dataView.getUint32(dirEndOffset, littleEndian);
32075     },
32076     
32077     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32078     {
32079         var tag = dataView.getUint16(offset, littleEndian);
32080         
32081         this.exif[tag] = this.getExifValue(
32082             dataView,
32083             tiffOffset,
32084             offset,
32085             dataView.getUint16(offset + 2, littleEndian), // tag type
32086             dataView.getUint32(offset + 4, littleEndian), // tag length
32087             littleEndian
32088         );
32089     },
32090     
32091     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32092     {
32093         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32094             tagSize,
32095             dataOffset,
32096             values,
32097             i,
32098             str,
32099             c;
32100     
32101         if (!tagType) {
32102             Roo.log('Invalid Exif data: Invalid tag type.');
32103             return;
32104         }
32105         
32106         tagSize = tagType.size * length;
32107         // Determine if the value is contained in the dataOffset bytes,
32108         // or if the value at the dataOffset is a pointer to the actual data:
32109         dataOffset = tagSize > 4 ?
32110                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32111         if (dataOffset + tagSize > dataView.byteLength) {
32112             Roo.log('Invalid Exif data: Invalid data offset.');
32113             return;
32114         }
32115         if (length === 1) {
32116             return tagType.getValue(dataView, dataOffset, littleEndian);
32117         }
32118         values = [];
32119         for (i = 0; i < length; i += 1) {
32120             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32121         }
32122         
32123         if (tagType.ascii) {
32124             str = '';
32125             // Concatenate the chars:
32126             for (i = 0; i < values.length; i += 1) {
32127                 c = values[i];
32128                 // Ignore the terminating NULL byte(s):
32129                 if (c === '\u0000') {
32130                     break;
32131                 }
32132                 str += c;
32133             }
32134             return str;
32135         }
32136         return values;
32137     }
32138     
32139 });
32140
32141 Roo.apply(Roo.bootstrap.UploadCropbox, {
32142     tags : {
32143         'Orientation': 0x0112
32144     },
32145     
32146     Orientation: {
32147             1: 0, //'top-left',
32148 //            2: 'top-right',
32149             3: 180, //'bottom-right',
32150 //            4: 'bottom-left',
32151 //            5: 'left-top',
32152             6: 90, //'right-top',
32153 //            7: 'right-bottom',
32154             8: 270 //'left-bottom'
32155     },
32156     
32157     exifTagTypes : {
32158         // byte, 8-bit unsigned int:
32159         1: {
32160             getValue: function (dataView, dataOffset) {
32161                 return dataView.getUint8(dataOffset);
32162             },
32163             size: 1
32164         },
32165         // ascii, 8-bit byte:
32166         2: {
32167             getValue: function (dataView, dataOffset) {
32168                 return String.fromCharCode(dataView.getUint8(dataOffset));
32169             },
32170             size: 1,
32171             ascii: true
32172         },
32173         // short, 16 bit int:
32174         3: {
32175             getValue: function (dataView, dataOffset, littleEndian) {
32176                 return dataView.getUint16(dataOffset, littleEndian);
32177             },
32178             size: 2
32179         },
32180         // long, 32 bit int:
32181         4: {
32182             getValue: function (dataView, dataOffset, littleEndian) {
32183                 return dataView.getUint32(dataOffset, littleEndian);
32184             },
32185             size: 4
32186         },
32187         // rational = two long values, first is numerator, second is denominator:
32188         5: {
32189             getValue: function (dataView, dataOffset, littleEndian) {
32190                 return dataView.getUint32(dataOffset, littleEndian) /
32191                     dataView.getUint32(dataOffset + 4, littleEndian);
32192             },
32193             size: 8
32194         },
32195         // slong, 32 bit signed int:
32196         9: {
32197             getValue: function (dataView, dataOffset, littleEndian) {
32198                 return dataView.getInt32(dataOffset, littleEndian);
32199             },
32200             size: 4
32201         },
32202         // srational, two slongs, first is numerator, second is denominator:
32203         10: {
32204             getValue: function (dataView, dataOffset, littleEndian) {
32205                 return dataView.getInt32(dataOffset, littleEndian) /
32206                     dataView.getInt32(dataOffset + 4, littleEndian);
32207             },
32208             size: 8
32209         }
32210     },
32211     
32212     footer : {
32213         STANDARD : [
32214             {
32215                 tag : 'div',
32216                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32217                 action : 'rotate-left',
32218                 cn : [
32219                     {
32220                         tag : 'button',
32221                         cls : 'btn btn-default',
32222                         html : '<i class="fa fa-undo"></i>'
32223                     }
32224                 ]
32225             },
32226             {
32227                 tag : 'div',
32228                 cls : 'btn-group roo-upload-cropbox-picture',
32229                 action : 'picture',
32230                 cn : [
32231                     {
32232                         tag : 'button',
32233                         cls : 'btn btn-default',
32234                         html : '<i class="fa fa-picture-o"></i>'
32235                     }
32236                 ]
32237             },
32238             {
32239                 tag : 'div',
32240                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32241                 action : 'rotate-right',
32242                 cn : [
32243                     {
32244                         tag : 'button',
32245                         cls : 'btn btn-default',
32246                         html : '<i class="fa fa-repeat"></i>'
32247                     }
32248                 ]
32249             }
32250         ],
32251         DOCUMENT : [
32252             {
32253                 tag : 'div',
32254                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32255                 action : 'rotate-left',
32256                 cn : [
32257                     {
32258                         tag : 'button',
32259                         cls : 'btn btn-default',
32260                         html : '<i class="fa fa-undo"></i>'
32261                     }
32262                 ]
32263             },
32264             {
32265                 tag : 'div',
32266                 cls : 'btn-group roo-upload-cropbox-download',
32267                 action : 'download',
32268                 cn : [
32269                     {
32270                         tag : 'button',
32271                         cls : 'btn btn-default',
32272                         html : '<i class="fa fa-download"></i>'
32273                     }
32274                 ]
32275             },
32276             {
32277                 tag : 'div',
32278                 cls : 'btn-group roo-upload-cropbox-crop',
32279                 action : 'crop',
32280                 cn : [
32281                     {
32282                         tag : 'button',
32283                         cls : 'btn btn-default',
32284                         html : '<i class="fa fa-crop"></i>'
32285                     }
32286                 ]
32287             },
32288             {
32289                 tag : 'div',
32290                 cls : 'btn-group roo-upload-cropbox-trash',
32291                 action : 'trash',
32292                 cn : [
32293                     {
32294                         tag : 'button',
32295                         cls : 'btn btn-default',
32296                         html : '<i class="fa fa-trash"></i>'
32297                     }
32298                 ]
32299             },
32300             {
32301                 tag : 'div',
32302                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32303                 action : 'rotate-right',
32304                 cn : [
32305                     {
32306                         tag : 'button',
32307                         cls : 'btn btn-default',
32308                         html : '<i class="fa fa-repeat"></i>'
32309                     }
32310                 ]
32311             }
32312         ],
32313         ROTATOR : [
32314             {
32315                 tag : 'div',
32316                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32317                 action : 'rotate-left',
32318                 cn : [
32319                     {
32320                         tag : 'button',
32321                         cls : 'btn btn-default',
32322                         html : '<i class="fa fa-undo"></i>'
32323                     }
32324                 ]
32325             },
32326             {
32327                 tag : 'div',
32328                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32329                 action : 'rotate-right',
32330                 cn : [
32331                     {
32332                         tag : 'button',
32333                         cls : 'btn btn-default',
32334                         html : '<i class="fa fa-repeat"></i>'
32335                     }
32336                 ]
32337             }
32338         ]
32339     }
32340 });
32341
32342 /*
32343 * Licence: LGPL
32344 */
32345
32346 /**
32347  * @class Roo.bootstrap.DocumentManager
32348  * @extends Roo.bootstrap.Component
32349  * Bootstrap DocumentManager class
32350  * @cfg {String} paramName default 'imageUpload'
32351  * @cfg {String} toolTipName default 'filename'
32352  * @cfg {String} method default POST
32353  * @cfg {String} url action url
32354  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32355  * @cfg {Boolean} multiple multiple upload default true
32356  * @cfg {Number} thumbSize default 300
32357  * @cfg {String} fieldLabel
32358  * @cfg {Number} labelWidth default 4
32359  * @cfg {String} labelAlign (left|top) default left
32360  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32361 * @cfg {Number} labellg set the width of label (1-12)
32362  * @cfg {Number} labelmd set the width of label (1-12)
32363  * @cfg {Number} labelsm set the width of label (1-12)
32364  * @cfg {Number} labelxs set the width of label (1-12)
32365  * 
32366  * @constructor
32367  * Create a new DocumentManager
32368  * @param {Object} config The config object
32369  */
32370
32371 Roo.bootstrap.DocumentManager = function(config){
32372     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32373     
32374     this.files = [];
32375     this.delegates = [];
32376     
32377     this.addEvents({
32378         /**
32379          * @event initial
32380          * Fire when initial the DocumentManager
32381          * @param {Roo.bootstrap.DocumentManager} this
32382          */
32383         "initial" : true,
32384         /**
32385          * @event inspect
32386          * inspect selected file
32387          * @param {Roo.bootstrap.DocumentManager} this
32388          * @param {File} file
32389          */
32390         "inspect" : true,
32391         /**
32392          * @event exception
32393          * Fire when xhr load exception
32394          * @param {Roo.bootstrap.DocumentManager} this
32395          * @param {XMLHttpRequest} xhr
32396          */
32397         "exception" : true,
32398         /**
32399          * @event afterupload
32400          * Fire when xhr load exception
32401          * @param {Roo.bootstrap.DocumentManager} this
32402          * @param {XMLHttpRequest} xhr
32403          */
32404         "afterupload" : true,
32405         /**
32406          * @event prepare
32407          * prepare the form data
32408          * @param {Roo.bootstrap.DocumentManager} this
32409          * @param {Object} formData
32410          */
32411         "prepare" : true,
32412         /**
32413          * @event remove
32414          * Fire when remove the file
32415          * @param {Roo.bootstrap.DocumentManager} this
32416          * @param {Object} file
32417          */
32418         "remove" : true,
32419         /**
32420          * @event refresh
32421          * Fire after refresh the file
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          */
32424         "refresh" : true,
32425         /**
32426          * @event click
32427          * Fire after click the image
32428          * @param {Roo.bootstrap.DocumentManager} this
32429          * @param {Object} file
32430          */
32431         "click" : true,
32432         /**
32433          * @event edit
32434          * Fire when upload a image and editable set to true
32435          * @param {Roo.bootstrap.DocumentManager} this
32436          * @param {Object} file
32437          */
32438         "edit" : true,
32439         /**
32440          * @event beforeselectfile
32441          * Fire before select file
32442          * @param {Roo.bootstrap.DocumentManager} this
32443          */
32444         "beforeselectfile" : true,
32445         /**
32446          * @event process
32447          * Fire before process file
32448          * @param {Roo.bootstrap.DocumentManager} this
32449          * @param {Object} file
32450          */
32451         "process" : true,
32452         /**
32453          * @event previewrendered
32454          * Fire when preview rendered
32455          * @param {Roo.bootstrap.DocumentManager} this
32456          * @param {Object} file
32457          */
32458         "previewrendered" : true,
32459         /**
32460          */
32461         "previewResize" : true
32462         
32463     });
32464 };
32465
32466 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32467     
32468     boxes : 0,
32469     inputName : '',
32470     thumbSize : 300,
32471     multiple : true,
32472     files : false,
32473     method : 'POST',
32474     url : '',
32475     paramName : 'imageUpload',
32476     toolTipName : 'filename',
32477     fieldLabel : '',
32478     labelWidth : 4,
32479     labelAlign : 'left',
32480     editable : true,
32481     delegates : false,
32482     xhr : false, 
32483     
32484     labellg : 0,
32485     labelmd : 0,
32486     labelsm : 0,
32487     labelxs : 0,
32488     
32489     getAutoCreate : function()
32490     {   
32491         var managerWidget = {
32492             tag : 'div',
32493             cls : 'roo-document-manager',
32494             cn : [
32495                 {
32496                     tag : 'input',
32497                     cls : 'roo-document-manager-selector',
32498                     type : 'file'
32499                 },
32500                 {
32501                     tag : 'div',
32502                     cls : 'roo-document-manager-uploader',
32503                     cn : [
32504                         {
32505                             tag : 'div',
32506                             cls : 'roo-document-manager-upload-btn',
32507                             html : '<i class="fa fa-plus"></i>'
32508                         }
32509                     ]
32510                     
32511                 }
32512             ]
32513         };
32514         
32515         var content = [
32516             {
32517                 tag : 'div',
32518                 cls : 'column col-md-12',
32519                 cn : managerWidget
32520             }
32521         ];
32522         
32523         if(this.fieldLabel.length){
32524             
32525             content = [
32526                 {
32527                     tag : 'div',
32528                     cls : 'column col-md-12',
32529                     html : this.fieldLabel
32530                 },
32531                 {
32532                     tag : 'div',
32533                     cls : 'column col-md-12',
32534                     cn : managerWidget
32535                 }
32536             ];
32537
32538             if(this.labelAlign == 'left'){
32539                 content = [
32540                     {
32541                         tag : 'div',
32542                         cls : 'column',
32543                         html : this.fieldLabel
32544                     },
32545                     {
32546                         tag : 'div',
32547                         cls : 'column',
32548                         cn : managerWidget
32549                     }
32550                 ];
32551                 
32552                 if(this.labelWidth > 12){
32553                     content[0].style = "width: " + this.labelWidth + 'px';
32554                 }
32555
32556                 if(this.labelWidth < 13 && this.labelmd == 0){
32557                     this.labelmd = this.labelWidth;
32558                 }
32559
32560                 if(this.labellg > 0){
32561                     content[0].cls += ' col-lg-' + this.labellg;
32562                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32563                 }
32564
32565                 if(this.labelmd > 0){
32566                     content[0].cls += ' col-md-' + this.labelmd;
32567                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32568                 }
32569
32570                 if(this.labelsm > 0){
32571                     content[0].cls += ' col-sm-' + this.labelsm;
32572                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32573                 }
32574
32575                 if(this.labelxs > 0){
32576                     content[0].cls += ' col-xs-' + this.labelxs;
32577                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32578                 }
32579                 
32580             }
32581         }
32582         
32583         var cfg = {
32584             tag : 'div',
32585             cls : 'row clearfix',
32586             cn : content
32587         };
32588         
32589         return cfg;
32590         
32591     },
32592     
32593     initEvents : function()
32594     {
32595         this.managerEl = this.el.select('.roo-document-manager', true).first();
32596         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32597         
32598         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32599         this.selectorEl.hide();
32600         
32601         if(this.multiple){
32602             this.selectorEl.attr('multiple', 'multiple');
32603         }
32604         
32605         this.selectorEl.on('change', this.onFileSelected, this);
32606         
32607         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32608         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32609         
32610         this.uploader.on('click', this.onUploaderClick, this);
32611         
32612         this.renderProgressDialog();
32613         
32614         var _this = this;
32615         
32616         window.addEventListener("resize", function() { _this.refresh(); } );
32617         
32618         this.fireEvent('initial', this);
32619     },
32620     
32621     renderProgressDialog : function()
32622     {
32623         var _this = this;
32624         
32625         this.progressDialog = new Roo.bootstrap.Modal({
32626             cls : 'roo-document-manager-progress-dialog',
32627             allow_close : false,
32628             animate : false,
32629             title : '',
32630             buttons : [
32631                 {
32632                     name  :'cancel',
32633                     weight : 'danger',
32634                     html : 'Cancel'
32635                 }
32636             ], 
32637             listeners : { 
32638                 btnclick : function() {
32639                     _this.uploadCancel();
32640                     this.hide();
32641                 }
32642             }
32643         });
32644          
32645         this.progressDialog.render(Roo.get(document.body));
32646          
32647         this.progress = new Roo.bootstrap.Progress({
32648             cls : 'roo-document-manager-progress',
32649             active : true,
32650             striped : true
32651         });
32652         
32653         this.progress.render(this.progressDialog.getChildContainer());
32654         
32655         this.progressBar = new Roo.bootstrap.ProgressBar({
32656             cls : 'roo-document-manager-progress-bar',
32657             aria_valuenow : 0,
32658             aria_valuemin : 0,
32659             aria_valuemax : 12,
32660             panel : 'success'
32661         });
32662         
32663         this.progressBar.render(this.progress.getChildContainer());
32664     },
32665     
32666     onUploaderClick : function(e)
32667     {
32668         e.preventDefault();
32669      
32670         if(this.fireEvent('beforeselectfile', this) != false){
32671             this.selectorEl.dom.click();
32672         }
32673         
32674     },
32675     
32676     onFileSelected : function(e)
32677     {
32678         e.preventDefault();
32679         
32680         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32681             return;
32682         }
32683         
32684         Roo.each(this.selectorEl.dom.files, function(file){
32685             if(this.fireEvent('inspect', this, file) != false){
32686                 this.files.push(file);
32687             }
32688         }, this);
32689         
32690         this.queue();
32691         
32692     },
32693     
32694     queue : function()
32695     {
32696         this.selectorEl.dom.value = '';
32697         
32698         if(!this.files || !this.files.length){
32699             return;
32700         }
32701         
32702         if(this.boxes > 0 && this.files.length > this.boxes){
32703             this.files = this.files.slice(0, this.boxes);
32704         }
32705         
32706         this.uploader.show();
32707         
32708         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32709             this.uploader.hide();
32710         }
32711         
32712         var _this = this;
32713         
32714         var files = [];
32715         
32716         var docs = [];
32717         
32718         Roo.each(this.files, function(file){
32719             
32720             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32721                 var f = this.renderPreview(file);
32722                 files.push(f);
32723                 return;
32724             }
32725             
32726             if(file.type.indexOf('image') != -1){
32727                 this.delegates.push(
32728                     (function(){
32729                         _this.process(file);
32730                     }).createDelegate(this)
32731                 );
32732         
32733                 return;
32734             }
32735             
32736             docs.push(
32737                 (function(){
32738                     _this.process(file);
32739                 }).createDelegate(this)
32740             );
32741             
32742         }, this);
32743         
32744         this.files = files;
32745         
32746         this.delegates = this.delegates.concat(docs);
32747         
32748         if(!this.delegates.length){
32749             this.refresh();
32750             return;
32751         }
32752         
32753         this.progressBar.aria_valuemax = this.delegates.length;
32754         
32755         this.arrange();
32756         
32757         return;
32758     },
32759     
32760     arrange : function()
32761     {
32762         if(!this.delegates.length){
32763             this.progressDialog.hide();
32764             this.refresh();
32765             return;
32766         }
32767         
32768         var delegate = this.delegates.shift();
32769         
32770         this.progressDialog.show();
32771         
32772         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32773         
32774         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32775         
32776         delegate();
32777     },
32778     
32779     refresh : function()
32780     {
32781         this.uploader.show();
32782         
32783         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32784             this.uploader.hide();
32785         }
32786         
32787         Roo.isTouch ? this.closable(false) : this.closable(true);
32788         
32789         this.fireEvent('refresh', this);
32790     },
32791     
32792     onRemove : function(e, el, o)
32793     {
32794         e.preventDefault();
32795         
32796         this.fireEvent('remove', this, o);
32797         
32798     },
32799     
32800     remove : function(o)
32801     {
32802         var files = [];
32803         
32804         Roo.each(this.files, function(file){
32805             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32806                 files.push(file);
32807                 return;
32808             }
32809
32810             o.target.remove();
32811
32812         }, this);
32813         
32814         this.files = files;
32815         
32816         this.refresh();
32817     },
32818     
32819     clear : function()
32820     {
32821         Roo.each(this.files, function(file){
32822             if(!file.target){
32823                 return;
32824             }
32825             
32826             file.target.remove();
32827
32828         }, this);
32829         
32830         this.files = [];
32831         
32832         this.refresh();
32833     },
32834     
32835     onClick : function(e, el, o)
32836     {
32837         e.preventDefault();
32838         
32839         this.fireEvent('click', this, o);
32840         
32841     },
32842     
32843     closable : function(closable)
32844     {
32845         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32846             
32847             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32848             
32849             if(closable){
32850                 el.show();
32851                 return;
32852             }
32853             
32854             el.hide();
32855             
32856         }, this);
32857     },
32858     
32859     xhrOnLoad : function(xhr)
32860     {
32861         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32862             el.remove();
32863         }, this);
32864         
32865         if (xhr.readyState !== 4) {
32866             this.arrange();
32867             this.fireEvent('exception', this, xhr);
32868             return;
32869         }
32870
32871         var response = Roo.decode(xhr.responseText);
32872         
32873         if(!response.success){
32874             this.arrange();
32875             this.fireEvent('exception', this, xhr);
32876             return;
32877         }
32878         
32879         var file = this.renderPreview(response.data);
32880         
32881         this.files.push(file);
32882         
32883         this.arrange();
32884         
32885         this.fireEvent('afterupload', this, xhr);
32886         
32887     },
32888     
32889     xhrOnError : function(xhr)
32890     {
32891         Roo.log('xhr on error');
32892         
32893         var response = Roo.decode(xhr.responseText);
32894           
32895         Roo.log(response);
32896         
32897         this.arrange();
32898     },
32899     
32900     process : function(file)
32901     {
32902         if(this.fireEvent('process', this, file) !== false){
32903             if(this.editable && file.type.indexOf('image') != -1){
32904                 this.fireEvent('edit', this, file);
32905                 return;
32906             }
32907
32908             this.uploadStart(file, false);
32909
32910             return;
32911         }
32912         
32913     },
32914     
32915     uploadStart : function(file, crop)
32916     {
32917         this.xhr = new XMLHttpRequest();
32918         
32919         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32920             this.arrange();
32921             return;
32922         }
32923         
32924         file.xhr = this.xhr;
32925             
32926         this.managerEl.createChild({
32927             tag : 'div',
32928             cls : 'roo-document-manager-loading',
32929             cn : [
32930                 {
32931                     tag : 'div',
32932                     tooltip : file.name,
32933                     cls : 'roo-document-manager-thumb',
32934                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32935                 }
32936             ]
32937
32938         });
32939
32940         this.xhr.open(this.method, this.url, true);
32941         
32942         var headers = {
32943             "Accept": "application/json",
32944             "Cache-Control": "no-cache",
32945             "X-Requested-With": "XMLHttpRequest"
32946         };
32947         
32948         for (var headerName in headers) {
32949             var headerValue = headers[headerName];
32950             if (headerValue) {
32951                 this.xhr.setRequestHeader(headerName, headerValue);
32952             }
32953         }
32954         
32955         var _this = this;
32956         
32957         this.xhr.onload = function()
32958         {
32959             _this.xhrOnLoad(_this.xhr);
32960         }
32961         
32962         this.xhr.onerror = function()
32963         {
32964             _this.xhrOnError(_this.xhr);
32965         }
32966         
32967         var formData = new FormData();
32968
32969         formData.append('returnHTML', 'NO');
32970         
32971         if(crop){
32972             formData.append('crop', crop);
32973         }
32974         
32975         formData.append(this.paramName, file, file.name);
32976         
32977         var options = {
32978             file : file, 
32979             manually : false
32980         };
32981         
32982         if(this.fireEvent('prepare', this, formData, options) != false){
32983             
32984             if(options.manually){
32985                 return;
32986             }
32987             
32988             this.xhr.send(formData);
32989             return;
32990         };
32991         
32992         this.uploadCancel();
32993     },
32994     
32995     uploadCancel : function()
32996     {
32997         if (this.xhr) {
32998             this.xhr.abort();
32999         }
33000         
33001         this.delegates = [];
33002         
33003         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33004             el.remove();
33005         }, this);
33006         
33007         this.arrange();
33008     },
33009     
33010     renderPreview : function(file)
33011     {
33012         if(typeof(file.target) != 'undefined' && file.target){
33013             return file;
33014         }
33015         
33016         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33017         
33018         var previewEl = this.managerEl.createChild({
33019             tag : 'div',
33020             cls : 'roo-document-manager-preview',
33021             cn : [
33022                 {
33023                     tag : 'div',
33024                     tooltip : file[this.toolTipName],
33025                     cls : 'roo-document-manager-thumb',
33026                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33027                 },
33028                 {
33029                     tag : 'button',
33030                     cls : 'close',
33031                     html : '<i class="fa fa-times-circle"></i>'
33032                 }
33033             ]
33034         });
33035
33036         var close = previewEl.select('button.close', true).first();
33037
33038         close.on('click', this.onRemove, this, file);
33039
33040         file.target = previewEl;
33041
33042         var image = previewEl.select('img', true).first();
33043         
33044         var _this = this;
33045         
33046         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33047         
33048         image.on('click', this.onClick, this, file);
33049         
33050         this.fireEvent('previewrendered', this, file);
33051         
33052         return file;
33053         
33054     },
33055     
33056     onPreviewLoad : function(file, image)
33057     {
33058         if(typeof(file.target) == 'undefined' || !file.target){
33059             return;
33060         }
33061         
33062         var width = image.dom.naturalWidth || image.dom.width;
33063         var height = image.dom.naturalHeight || image.dom.height;
33064         
33065         if(!this.previewResize) {
33066             return;
33067         }
33068         
33069         if(width > height){
33070             file.target.addClass('wide');
33071             return;
33072         }
33073         
33074         file.target.addClass('tall');
33075         return;
33076         
33077     },
33078     
33079     uploadFromSource : function(file, crop)
33080     {
33081         this.xhr = new XMLHttpRequest();
33082         
33083         this.managerEl.createChild({
33084             tag : 'div',
33085             cls : 'roo-document-manager-loading',
33086             cn : [
33087                 {
33088                     tag : 'div',
33089                     tooltip : file.name,
33090                     cls : 'roo-document-manager-thumb',
33091                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33092                 }
33093             ]
33094
33095         });
33096
33097         this.xhr.open(this.method, this.url, true);
33098         
33099         var headers = {
33100             "Accept": "application/json",
33101             "Cache-Control": "no-cache",
33102             "X-Requested-With": "XMLHttpRequest"
33103         };
33104         
33105         for (var headerName in headers) {
33106             var headerValue = headers[headerName];
33107             if (headerValue) {
33108                 this.xhr.setRequestHeader(headerName, headerValue);
33109             }
33110         }
33111         
33112         var _this = this;
33113         
33114         this.xhr.onload = function()
33115         {
33116             _this.xhrOnLoad(_this.xhr);
33117         }
33118         
33119         this.xhr.onerror = function()
33120         {
33121             _this.xhrOnError(_this.xhr);
33122         }
33123         
33124         var formData = new FormData();
33125
33126         formData.append('returnHTML', 'NO');
33127         
33128         formData.append('crop', crop);
33129         
33130         if(typeof(file.filename) != 'undefined'){
33131             formData.append('filename', file.filename);
33132         }
33133         
33134         if(typeof(file.mimetype) != 'undefined'){
33135             formData.append('mimetype', file.mimetype);
33136         }
33137         
33138         Roo.log(formData);
33139         
33140         if(this.fireEvent('prepare', this, formData) != false){
33141             this.xhr.send(formData);
33142         };
33143     }
33144 });
33145
33146 /*
33147 * Licence: LGPL
33148 */
33149
33150 /**
33151  * @class Roo.bootstrap.DocumentViewer
33152  * @extends Roo.bootstrap.Component
33153  * Bootstrap DocumentViewer class
33154  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33155  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33156  * 
33157  * @constructor
33158  * Create a new DocumentViewer
33159  * @param {Object} config The config object
33160  */
33161
33162 Roo.bootstrap.DocumentViewer = function(config){
33163     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33164     
33165     this.addEvents({
33166         /**
33167          * @event initial
33168          * Fire after initEvent
33169          * @param {Roo.bootstrap.DocumentViewer} this
33170          */
33171         "initial" : true,
33172         /**
33173          * @event click
33174          * Fire after click
33175          * @param {Roo.bootstrap.DocumentViewer} this
33176          */
33177         "click" : true,
33178         /**
33179          * @event download
33180          * Fire after download button
33181          * @param {Roo.bootstrap.DocumentViewer} this
33182          */
33183         "download" : true,
33184         /**
33185          * @event trash
33186          * Fire after trash button
33187          * @param {Roo.bootstrap.DocumentViewer} this
33188          */
33189         "trash" : true
33190         
33191     });
33192 };
33193
33194 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33195     
33196     showDownload : true,
33197     
33198     showTrash : true,
33199     
33200     getAutoCreate : function()
33201     {
33202         var cfg = {
33203             tag : 'div',
33204             cls : 'roo-document-viewer',
33205             cn : [
33206                 {
33207                     tag : 'div',
33208                     cls : 'roo-document-viewer-body',
33209                     cn : [
33210                         {
33211                             tag : 'div',
33212                             cls : 'roo-document-viewer-thumb',
33213                             cn : [
33214                                 {
33215                                     tag : 'img',
33216                                     cls : 'roo-document-viewer-image'
33217                                 }
33218                             ]
33219                         }
33220                     ]
33221                 },
33222                 {
33223                     tag : 'div',
33224                     cls : 'roo-document-viewer-footer',
33225                     cn : {
33226                         tag : 'div',
33227                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33228                         cn : [
33229                             {
33230                                 tag : 'div',
33231                                 cls : 'btn-group roo-document-viewer-download',
33232                                 cn : [
33233                                     {
33234                                         tag : 'button',
33235                                         cls : 'btn btn-default',
33236                                         html : '<i class="fa fa-download"></i>'
33237                                     }
33238                                 ]
33239                             },
33240                             {
33241                                 tag : 'div',
33242                                 cls : 'btn-group roo-document-viewer-trash',
33243                                 cn : [
33244                                     {
33245                                         tag : 'button',
33246                                         cls : 'btn btn-default',
33247                                         html : '<i class="fa fa-trash"></i>'
33248                                     }
33249                                 ]
33250                             }
33251                         ]
33252                     }
33253                 }
33254             ]
33255         };
33256         
33257         return cfg;
33258     },
33259     
33260     initEvents : function()
33261     {
33262         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33263         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33264         
33265         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33266         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33267         
33268         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33269         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33270         
33271         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33272         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33273         
33274         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33275         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33276         
33277         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33278         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33279         
33280         this.bodyEl.on('click', this.onClick, this);
33281         this.downloadBtn.on('click', this.onDownload, this);
33282         this.trashBtn.on('click', this.onTrash, this);
33283         
33284         this.downloadBtn.hide();
33285         this.trashBtn.hide();
33286         
33287         if(this.showDownload){
33288             this.downloadBtn.show();
33289         }
33290         
33291         if(this.showTrash){
33292             this.trashBtn.show();
33293         }
33294         
33295         if(!this.showDownload && !this.showTrash) {
33296             this.footerEl.hide();
33297         }
33298         
33299     },
33300     
33301     initial : function()
33302     {
33303         this.fireEvent('initial', this);
33304         
33305     },
33306     
33307     onClick : function(e)
33308     {
33309         e.preventDefault();
33310         
33311         this.fireEvent('click', this);
33312     },
33313     
33314     onDownload : function(e)
33315     {
33316         e.preventDefault();
33317         
33318         this.fireEvent('download', this);
33319     },
33320     
33321     onTrash : function(e)
33322     {
33323         e.preventDefault();
33324         
33325         this.fireEvent('trash', this);
33326     }
33327     
33328 });
33329 /*
33330  * - LGPL
33331  *
33332  * nav progress bar
33333  * 
33334  */
33335
33336 /**
33337  * @class Roo.bootstrap.NavProgressBar
33338  * @extends Roo.bootstrap.Component
33339  * Bootstrap NavProgressBar class
33340  * 
33341  * @constructor
33342  * Create a new nav progress bar
33343  * @param {Object} config The config object
33344  */
33345
33346 Roo.bootstrap.NavProgressBar = function(config){
33347     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33348
33349     this.bullets = this.bullets || [];
33350    
33351 //    Roo.bootstrap.NavProgressBar.register(this);
33352      this.addEvents({
33353         /**
33354              * @event changed
33355              * Fires when the active item changes
33356              * @param {Roo.bootstrap.NavProgressBar} this
33357              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33358              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33359          */
33360         'changed': true
33361      });
33362     
33363 };
33364
33365 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33366     
33367     bullets : [],
33368     barItems : [],
33369     
33370     getAutoCreate : function()
33371     {
33372         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33373         
33374         cfg = {
33375             tag : 'div',
33376             cls : 'roo-navigation-bar-group',
33377             cn : [
33378                 {
33379                     tag : 'div',
33380                     cls : 'roo-navigation-top-bar'
33381                 },
33382                 {
33383                     tag : 'div',
33384                     cls : 'roo-navigation-bullets-bar',
33385                     cn : [
33386                         {
33387                             tag : 'ul',
33388                             cls : 'roo-navigation-bar'
33389                         }
33390                     ]
33391                 },
33392                 
33393                 {
33394                     tag : 'div',
33395                     cls : 'roo-navigation-bottom-bar'
33396                 }
33397             ]
33398             
33399         };
33400         
33401         return cfg;
33402         
33403     },
33404     
33405     initEvents: function() 
33406     {
33407         
33408     },
33409     
33410     onRender : function(ct, position) 
33411     {
33412         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33413         
33414         if(this.bullets.length){
33415             Roo.each(this.bullets, function(b){
33416                this.addItem(b);
33417             }, this);
33418         }
33419         
33420         this.format();
33421         
33422     },
33423     
33424     addItem : function(cfg)
33425     {
33426         var item = new Roo.bootstrap.NavProgressItem(cfg);
33427         
33428         item.parentId = this.id;
33429         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33430         
33431         if(cfg.html){
33432             var top = new Roo.bootstrap.Element({
33433                 tag : 'div',
33434                 cls : 'roo-navigation-bar-text'
33435             });
33436             
33437             var bottom = new Roo.bootstrap.Element({
33438                 tag : 'div',
33439                 cls : 'roo-navigation-bar-text'
33440             });
33441             
33442             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33443             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33444             
33445             var topText = new Roo.bootstrap.Element({
33446                 tag : 'span',
33447                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33448             });
33449             
33450             var bottomText = new Roo.bootstrap.Element({
33451                 tag : 'span',
33452                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33453             });
33454             
33455             topText.onRender(top.el, null);
33456             bottomText.onRender(bottom.el, null);
33457             
33458             item.topEl = top;
33459             item.bottomEl = bottom;
33460         }
33461         
33462         this.barItems.push(item);
33463         
33464         return item;
33465     },
33466     
33467     getActive : function()
33468     {
33469         var active = false;
33470         
33471         Roo.each(this.barItems, function(v){
33472             
33473             if (!v.isActive()) {
33474                 return;
33475             }
33476             
33477             active = v;
33478             return false;
33479             
33480         });
33481         
33482         return active;
33483     },
33484     
33485     setActiveItem : function(item)
33486     {
33487         var prev = false;
33488         
33489         Roo.each(this.barItems, function(v){
33490             if (v.rid == item.rid) {
33491                 return ;
33492             }
33493             
33494             if (v.isActive()) {
33495                 v.setActive(false);
33496                 prev = v;
33497             }
33498         });
33499
33500         item.setActive(true);
33501         
33502         this.fireEvent('changed', this, item, prev);
33503     },
33504     
33505     getBarItem: function(rid)
33506     {
33507         var ret = false;
33508         
33509         Roo.each(this.barItems, function(e) {
33510             if (e.rid != rid) {
33511                 return;
33512             }
33513             
33514             ret =  e;
33515             return false;
33516         });
33517         
33518         return ret;
33519     },
33520     
33521     indexOfItem : function(item)
33522     {
33523         var index = false;
33524         
33525         Roo.each(this.barItems, function(v, i){
33526             
33527             if (v.rid != item.rid) {
33528                 return;
33529             }
33530             
33531             index = i;
33532             return false
33533         });
33534         
33535         return index;
33536     },
33537     
33538     setActiveNext : function()
33539     {
33540         var i = this.indexOfItem(this.getActive());
33541         
33542         if (i > this.barItems.length) {
33543             return;
33544         }
33545         
33546         this.setActiveItem(this.barItems[i+1]);
33547     },
33548     
33549     setActivePrev : function()
33550     {
33551         var i = this.indexOfItem(this.getActive());
33552         
33553         if (i  < 1) {
33554             return;
33555         }
33556         
33557         this.setActiveItem(this.barItems[i-1]);
33558     },
33559     
33560     format : function()
33561     {
33562         if(!this.barItems.length){
33563             return;
33564         }
33565      
33566         var width = 100 / this.barItems.length;
33567         
33568         Roo.each(this.barItems, function(i){
33569             i.el.setStyle('width', width + '%');
33570             i.topEl.el.setStyle('width', width + '%');
33571             i.bottomEl.el.setStyle('width', width + '%');
33572         }, this);
33573         
33574     }
33575     
33576 });
33577 /*
33578  * - LGPL
33579  *
33580  * Nav Progress Item
33581  * 
33582  */
33583
33584 /**
33585  * @class Roo.bootstrap.NavProgressItem
33586  * @extends Roo.bootstrap.Component
33587  * Bootstrap NavProgressItem class
33588  * @cfg {String} rid the reference id
33589  * @cfg {Boolean} active (true|false) Is item active default false
33590  * @cfg {Boolean} disabled (true|false) Is item active default false
33591  * @cfg {String} html
33592  * @cfg {String} position (top|bottom) text position default bottom
33593  * @cfg {String} icon show icon instead of number
33594  * 
33595  * @constructor
33596  * Create a new NavProgressItem
33597  * @param {Object} config The config object
33598  */
33599 Roo.bootstrap.NavProgressItem = function(config){
33600     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33601     this.addEvents({
33602         // raw events
33603         /**
33604          * @event click
33605          * The raw click event for the entire grid.
33606          * @param {Roo.bootstrap.NavProgressItem} this
33607          * @param {Roo.EventObject} e
33608          */
33609         "click" : true
33610     });
33611    
33612 };
33613
33614 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33615     
33616     rid : '',
33617     active : false,
33618     disabled : false,
33619     html : '',
33620     position : 'bottom',
33621     icon : false,
33622     
33623     getAutoCreate : function()
33624     {
33625         var iconCls = 'roo-navigation-bar-item-icon';
33626         
33627         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33628         
33629         var cfg = {
33630             tag: 'li',
33631             cls: 'roo-navigation-bar-item',
33632             cn : [
33633                 {
33634                     tag : 'i',
33635                     cls : iconCls
33636                 }
33637             ]
33638         };
33639         
33640         if(this.active){
33641             cfg.cls += ' active';
33642         }
33643         if(this.disabled){
33644             cfg.cls += ' disabled';
33645         }
33646         
33647         return cfg;
33648     },
33649     
33650     disable : function()
33651     {
33652         this.setDisabled(true);
33653     },
33654     
33655     enable : function()
33656     {
33657         this.setDisabled(false);
33658     },
33659     
33660     initEvents: function() 
33661     {
33662         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33663         
33664         this.iconEl.on('click', this.onClick, this);
33665     },
33666     
33667     onClick : function(e)
33668     {
33669         e.preventDefault();
33670         
33671         if(this.disabled){
33672             return;
33673         }
33674         
33675         if(this.fireEvent('click', this, e) === false){
33676             return;
33677         };
33678         
33679         this.parent().setActiveItem(this);
33680     },
33681     
33682     isActive: function () 
33683     {
33684         return this.active;
33685     },
33686     
33687     setActive : function(state)
33688     {
33689         if(this.active == state){
33690             return;
33691         }
33692         
33693         this.active = state;
33694         
33695         if (state) {
33696             this.el.addClass('active');
33697             return;
33698         }
33699         
33700         this.el.removeClass('active');
33701         
33702         return;
33703     },
33704     
33705     setDisabled : function(state)
33706     {
33707         if(this.disabled == state){
33708             return;
33709         }
33710         
33711         this.disabled = state;
33712         
33713         if (state) {
33714             this.el.addClass('disabled');
33715             return;
33716         }
33717         
33718         this.el.removeClass('disabled');
33719     },
33720     
33721     tooltipEl : function()
33722     {
33723         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33724     }
33725 });
33726  
33727
33728  /*
33729  * - LGPL
33730  *
33731  * FieldLabel
33732  * 
33733  */
33734
33735 /**
33736  * @class Roo.bootstrap.FieldLabel
33737  * @extends Roo.bootstrap.Component
33738  * Bootstrap FieldLabel class
33739  * @cfg {String} html contents of the element
33740  * @cfg {String} tag tag of the element default label
33741  * @cfg {String} cls class of the element
33742  * @cfg {String} target label target 
33743  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33744  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33745  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33746  * @cfg {String} iconTooltip default "This field is required"
33747  * @cfg {String} indicatorpos (left|right) default left
33748  * 
33749  * @constructor
33750  * Create a new FieldLabel
33751  * @param {Object} config The config object
33752  */
33753
33754 Roo.bootstrap.FieldLabel = function(config){
33755     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33756     
33757     this.addEvents({
33758             /**
33759              * @event invalid
33760              * Fires after the field has been marked as invalid.
33761              * @param {Roo.form.FieldLabel} this
33762              * @param {String} msg The validation message
33763              */
33764             invalid : true,
33765             /**
33766              * @event valid
33767              * Fires after the field has been validated with no errors.
33768              * @param {Roo.form.FieldLabel} this
33769              */
33770             valid : true
33771         });
33772 };
33773
33774 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33775     
33776     tag: 'label',
33777     cls: '',
33778     html: '',
33779     target: '',
33780     allowBlank : true,
33781     invalidClass : 'has-warning',
33782     validClass : 'has-success',
33783     iconTooltip : 'This field is required',
33784     indicatorpos : 'left',
33785     
33786     getAutoCreate : function(){
33787         
33788         var cls = "";
33789         if (!this.allowBlank) {
33790             cls  = "visible";
33791         }
33792         
33793         var cfg = {
33794             tag : this.tag,
33795             cls : 'roo-bootstrap-field-label ' + this.cls,
33796             for : this.target,
33797             cn : [
33798                 {
33799                     tag : 'i',
33800                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33801                     tooltip : this.iconTooltip
33802                 },
33803                 {
33804                     tag : 'span',
33805                     html : this.html
33806                 }
33807             ] 
33808         };
33809         
33810         if(this.indicatorpos == 'right'){
33811             var cfg = {
33812                 tag : this.tag,
33813                 cls : 'roo-bootstrap-field-label ' + this.cls,
33814                 for : this.target,
33815                 cn : [
33816                     {
33817                         tag : 'span',
33818                         html : this.html
33819                     },
33820                     {
33821                         tag : 'i',
33822                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33823                         tooltip : this.iconTooltip
33824                     }
33825                 ] 
33826             };
33827         }
33828         
33829         return cfg;
33830     },
33831     
33832     initEvents: function() 
33833     {
33834         Roo.bootstrap.Element.superclass.initEvents.call(this);
33835         
33836         this.indicator = this.indicatorEl();
33837         
33838         if(this.indicator){
33839             this.indicator.removeClass('visible');
33840             this.indicator.addClass('invisible');
33841         }
33842         
33843         Roo.bootstrap.FieldLabel.register(this);
33844     },
33845     
33846     indicatorEl : function()
33847     {
33848         var indicator = this.el.select('i.roo-required-indicator',true).first();
33849         
33850         if(!indicator){
33851             return false;
33852         }
33853         
33854         return indicator;
33855         
33856     },
33857     
33858     /**
33859      * Mark this field as valid
33860      */
33861     markValid : function()
33862     {
33863         if(this.indicator){
33864             this.indicator.removeClass('visible');
33865             this.indicator.addClass('invisible');
33866         }
33867         if (Roo.bootstrap.version == 3) {
33868             this.el.removeClass(this.invalidClass);
33869             this.el.addClass(this.validClass);
33870         } else {
33871             this.el.removeClass('is-invalid');
33872             this.el.addClass('is-valid');
33873         }
33874         
33875         
33876         this.fireEvent('valid', this);
33877     },
33878     
33879     /**
33880      * Mark this field as invalid
33881      * @param {String} msg The validation message
33882      */
33883     markInvalid : function(msg)
33884     {
33885         if(this.indicator){
33886             this.indicator.removeClass('invisible');
33887             this.indicator.addClass('visible');
33888         }
33889           if (Roo.bootstrap.version == 3) {
33890             this.el.removeClass(this.validClass);
33891             this.el.addClass(this.invalidClass);
33892         } else {
33893             this.el.removeClass('is-valid');
33894             this.el.addClass('is-invalid');
33895         }
33896         
33897         
33898         this.fireEvent('invalid', this, msg);
33899     }
33900     
33901    
33902 });
33903
33904 Roo.apply(Roo.bootstrap.FieldLabel, {
33905     
33906     groups: {},
33907     
33908      /**
33909     * register a FieldLabel Group
33910     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33911     */
33912     register : function(label)
33913     {
33914         if(this.groups.hasOwnProperty(label.target)){
33915             return;
33916         }
33917      
33918         this.groups[label.target] = label;
33919         
33920     },
33921     /**
33922     * fetch a FieldLabel Group based on the target
33923     * @param {string} target
33924     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33925     */
33926     get: function(target) {
33927         if (typeof(this.groups[target]) == 'undefined') {
33928             return false;
33929         }
33930         
33931         return this.groups[target] ;
33932     }
33933 });
33934
33935  
33936
33937  /*
33938  * - LGPL
33939  *
33940  * page DateSplitField.
33941  * 
33942  */
33943
33944
33945 /**
33946  * @class Roo.bootstrap.DateSplitField
33947  * @extends Roo.bootstrap.Component
33948  * Bootstrap DateSplitField class
33949  * @cfg {string} fieldLabel - the label associated
33950  * @cfg {Number} labelWidth set the width of label (0-12)
33951  * @cfg {String} labelAlign (top|left)
33952  * @cfg {Boolean} dayAllowBlank (true|false) default false
33953  * @cfg {Boolean} monthAllowBlank (true|false) default false
33954  * @cfg {Boolean} yearAllowBlank (true|false) default false
33955  * @cfg {string} dayPlaceholder 
33956  * @cfg {string} monthPlaceholder
33957  * @cfg {string} yearPlaceholder
33958  * @cfg {string} dayFormat default 'd'
33959  * @cfg {string} monthFormat default 'm'
33960  * @cfg {string} yearFormat default 'Y'
33961  * @cfg {Number} labellg set the width of label (1-12)
33962  * @cfg {Number} labelmd set the width of label (1-12)
33963  * @cfg {Number} labelsm set the width of label (1-12)
33964  * @cfg {Number} labelxs set the width of label (1-12)
33965
33966  *     
33967  * @constructor
33968  * Create a new DateSplitField
33969  * @param {Object} config The config object
33970  */
33971
33972 Roo.bootstrap.DateSplitField = function(config){
33973     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33974     
33975     this.addEvents({
33976         // raw events
33977          /**
33978          * @event years
33979          * getting the data of years
33980          * @param {Roo.bootstrap.DateSplitField} this
33981          * @param {Object} years
33982          */
33983         "years" : true,
33984         /**
33985          * @event days
33986          * getting the data of days
33987          * @param {Roo.bootstrap.DateSplitField} this
33988          * @param {Object} days
33989          */
33990         "days" : true,
33991         /**
33992          * @event invalid
33993          * Fires after the field has been marked as invalid.
33994          * @param {Roo.form.Field} this
33995          * @param {String} msg The validation message
33996          */
33997         invalid : true,
33998        /**
33999          * @event valid
34000          * Fires after the field has been validated with no errors.
34001          * @param {Roo.form.Field} this
34002          */
34003         valid : true
34004     });
34005 };
34006
34007 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34008     
34009     fieldLabel : '',
34010     labelAlign : 'top',
34011     labelWidth : 3,
34012     dayAllowBlank : false,
34013     monthAllowBlank : false,
34014     yearAllowBlank : false,
34015     dayPlaceholder : '',
34016     monthPlaceholder : '',
34017     yearPlaceholder : '',
34018     dayFormat : 'd',
34019     monthFormat : 'm',
34020     yearFormat : 'Y',
34021     isFormField : true,
34022     labellg : 0,
34023     labelmd : 0,
34024     labelsm : 0,
34025     labelxs : 0,
34026     
34027     getAutoCreate : function()
34028     {
34029         var cfg = {
34030             tag : 'div',
34031             cls : 'row roo-date-split-field-group',
34032             cn : [
34033                 {
34034                     tag : 'input',
34035                     type : 'hidden',
34036                     cls : 'form-hidden-field roo-date-split-field-group-value',
34037                     name : this.name
34038                 }
34039             ]
34040         };
34041         
34042         var labelCls = 'col-md-12';
34043         var contentCls = 'col-md-4';
34044         
34045         if(this.fieldLabel){
34046             
34047             var label = {
34048                 tag : 'div',
34049                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34050                 cn : [
34051                     {
34052                         tag : 'label',
34053                         html : this.fieldLabel
34054                     }
34055                 ]
34056             };
34057             
34058             if(this.labelAlign == 'left'){
34059             
34060                 if(this.labelWidth > 12){
34061                     label.style = "width: " + this.labelWidth + 'px';
34062                 }
34063
34064                 if(this.labelWidth < 13 && this.labelmd == 0){
34065                     this.labelmd = this.labelWidth;
34066                 }
34067
34068                 if(this.labellg > 0){
34069                     labelCls = ' col-lg-' + this.labellg;
34070                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34071                 }
34072
34073                 if(this.labelmd > 0){
34074                     labelCls = ' col-md-' + this.labelmd;
34075                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34076                 }
34077
34078                 if(this.labelsm > 0){
34079                     labelCls = ' col-sm-' + this.labelsm;
34080                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34081                 }
34082
34083                 if(this.labelxs > 0){
34084                     labelCls = ' col-xs-' + this.labelxs;
34085                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34086                 }
34087             }
34088             
34089             label.cls += ' ' + labelCls;
34090             
34091             cfg.cn.push(label);
34092         }
34093         
34094         Roo.each(['day', 'month', 'year'], function(t){
34095             cfg.cn.push({
34096                 tag : 'div',
34097                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34098             });
34099         }, this);
34100         
34101         return cfg;
34102     },
34103     
34104     inputEl: function ()
34105     {
34106         return this.el.select('.roo-date-split-field-group-value', true).first();
34107     },
34108     
34109     onRender : function(ct, position) 
34110     {
34111         var _this = this;
34112         
34113         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34114         
34115         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34116         
34117         this.dayField = new Roo.bootstrap.ComboBox({
34118             allowBlank : this.dayAllowBlank,
34119             alwaysQuery : true,
34120             displayField : 'value',
34121             editable : false,
34122             fieldLabel : '',
34123             forceSelection : true,
34124             mode : 'local',
34125             placeholder : this.dayPlaceholder,
34126             selectOnFocus : true,
34127             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34128             triggerAction : 'all',
34129             typeAhead : true,
34130             valueField : 'value',
34131             store : new Roo.data.SimpleStore({
34132                 data : (function() {    
34133                     var days = [];
34134                     _this.fireEvent('days', _this, days);
34135                     return days;
34136                 })(),
34137                 fields : [ 'value' ]
34138             }),
34139             listeners : {
34140                 select : function (_self, record, index)
34141                 {
34142                     _this.setValue(_this.getValue());
34143                 }
34144             }
34145         });
34146
34147         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34148         
34149         this.monthField = new Roo.bootstrap.MonthField({
34150             after : '<i class=\"fa fa-calendar\"></i>',
34151             allowBlank : this.monthAllowBlank,
34152             placeholder : this.monthPlaceholder,
34153             readOnly : true,
34154             listeners : {
34155                 render : function (_self)
34156                 {
34157                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34158                         e.preventDefault();
34159                         _self.focus();
34160                     });
34161                 },
34162                 select : function (_self, oldvalue, newvalue)
34163                 {
34164                     _this.setValue(_this.getValue());
34165                 }
34166             }
34167         });
34168         
34169         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34170         
34171         this.yearField = new Roo.bootstrap.ComboBox({
34172             allowBlank : this.yearAllowBlank,
34173             alwaysQuery : true,
34174             displayField : 'value',
34175             editable : false,
34176             fieldLabel : '',
34177             forceSelection : true,
34178             mode : 'local',
34179             placeholder : this.yearPlaceholder,
34180             selectOnFocus : true,
34181             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34182             triggerAction : 'all',
34183             typeAhead : true,
34184             valueField : 'value',
34185             store : new Roo.data.SimpleStore({
34186                 data : (function() {
34187                     var years = [];
34188                     _this.fireEvent('years', _this, years);
34189                     return years;
34190                 })(),
34191                 fields : [ 'value' ]
34192             }),
34193             listeners : {
34194                 select : function (_self, record, index)
34195                 {
34196                     _this.setValue(_this.getValue());
34197                 }
34198             }
34199         });
34200
34201         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34202     },
34203     
34204     setValue : function(v, format)
34205     {
34206         this.inputEl.dom.value = v;
34207         
34208         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34209         
34210         var d = Date.parseDate(v, f);
34211         
34212         if(!d){
34213             this.validate();
34214             return;
34215         }
34216         
34217         this.setDay(d.format(this.dayFormat));
34218         this.setMonth(d.format(this.monthFormat));
34219         this.setYear(d.format(this.yearFormat));
34220         
34221         this.validate();
34222         
34223         return;
34224     },
34225     
34226     setDay : function(v)
34227     {
34228         this.dayField.setValue(v);
34229         this.inputEl.dom.value = this.getValue();
34230         this.validate();
34231         return;
34232     },
34233     
34234     setMonth : function(v)
34235     {
34236         this.monthField.setValue(v, true);
34237         this.inputEl.dom.value = this.getValue();
34238         this.validate();
34239         return;
34240     },
34241     
34242     setYear : function(v)
34243     {
34244         this.yearField.setValue(v);
34245         this.inputEl.dom.value = this.getValue();
34246         this.validate();
34247         return;
34248     },
34249     
34250     getDay : function()
34251     {
34252         return this.dayField.getValue();
34253     },
34254     
34255     getMonth : function()
34256     {
34257         return this.monthField.getValue();
34258     },
34259     
34260     getYear : function()
34261     {
34262         return this.yearField.getValue();
34263     },
34264     
34265     getValue : function()
34266     {
34267         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34268         
34269         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34270         
34271         return date;
34272     },
34273     
34274     reset : function()
34275     {
34276         this.setDay('');
34277         this.setMonth('');
34278         this.setYear('');
34279         this.inputEl.dom.value = '';
34280         this.validate();
34281         return;
34282     },
34283     
34284     validate : function()
34285     {
34286         var d = this.dayField.validate();
34287         var m = this.monthField.validate();
34288         var y = this.yearField.validate();
34289         
34290         var valid = true;
34291         
34292         if(
34293                 (!this.dayAllowBlank && !d) ||
34294                 (!this.monthAllowBlank && !m) ||
34295                 (!this.yearAllowBlank && !y)
34296         ){
34297             valid = false;
34298         }
34299         
34300         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34301             return valid;
34302         }
34303         
34304         if(valid){
34305             this.markValid();
34306             return valid;
34307         }
34308         
34309         this.markInvalid();
34310         
34311         return valid;
34312     },
34313     
34314     markValid : function()
34315     {
34316         
34317         var label = this.el.select('label', true).first();
34318         var icon = this.el.select('i.fa-star', true).first();
34319
34320         if(label && icon){
34321             icon.remove();
34322         }
34323         
34324         this.fireEvent('valid', this);
34325     },
34326     
34327      /**
34328      * Mark this field as invalid
34329      * @param {String} msg The validation message
34330      */
34331     markInvalid : function(msg)
34332     {
34333         
34334         var label = this.el.select('label', true).first();
34335         var icon = this.el.select('i.fa-star', true).first();
34336
34337         if(label && !icon){
34338             this.el.select('.roo-date-split-field-label', true).createChild({
34339                 tag : 'i',
34340                 cls : 'text-danger fa fa-lg fa-star',
34341                 tooltip : 'This field is required',
34342                 style : 'margin-right:5px;'
34343             }, label, true);
34344         }
34345         
34346         this.fireEvent('invalid', this, msg);
34347     },
34348     
34349     clearInvalid : function()
34350     {
34351         var label = this.el.select('label', true).first();
34352         var icon = this.el.select('i.fa-star', true).first();
34353
34354         if(label && icon){
34355             icon.remove();
34356         }
34357         
34358         this.fireEvent('valid', this);
34359     },
34360     
34361     getName: function()
34362     {
34363         return this.name;
34364     }
34365     
34366 });
34367
34368  /**
34369  *
34370  * This is based on 
34371  * http://masonry.desandro.com
34372  *
34373  * The idea is to render all the bricks based on vertical width...
34374  *
34375  * The original code extends 'outlayer' - we might need to use that....
34376  * 
34377  */
34378
34379
34380 /**
34381  * @class Roo.bootstrap.LayoutMasonry
34382  * @extends Roo.bootstrap.Component
34383  * Bootstrap Layout Masonry class
34384  * 
34385  * @constructor
34386  * Create a new Element
34387  * @param {Object} config The config object
34388  */
34389
34390 Roo.bootstrap.LayoutMasonry = function(config){
34391     
34392     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34393     
34394     this.bricks = [];
34395     
34396     Roo.bootstrap.LayoutMasonry.register(this);
34397     
34398     this.addEvents({
34399         // raw events
34400         /**
34401          * @event layout
34402          * Fire after layout the items
34403          * @param {Roo.bootstrap.LayoutMasonry} this
34404          * @param {Roo.EventObject} e
34405          */
34406         "layout" : true
34407     });
34408     
34409 };
34410
34411 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34412     
34413     /**
34414      * @cfg {Boolean} isLayoutInstant = no animation?
34415      */   
34416     isLayoutInstant : false, // needed?
34417    
34418     /**
34419      * @cfg {Number} boxWidth  width of the columns
34420      */   
34421     boxWidth : 450,
34422     
34423       /**
34424      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34425      */   
34426     boxHeight : 0,
34427     
34428     /**
34429      * @cfg {Number} padWidth padding below box..
34430      */   
34431     padWidth : 10, 
34432     
34433     /**
34434      * @cfg {Number} gutter gutter width..
34435      */   
34436     gutter : 10,
34437     
34438      /**
34439      * @cfg {Number} maxCols maximum number of columns
34440      */   
34441     
34442     maxCols: 0,
34443     
34444     /**
34445      * @cfg {Boolean} isAutoInitial defalut true
34446      */   
34447     isAutoInitial : true, 
34448     
34449     containerWidth: 0,
34450     
34451     /**
34452      * @cfg {Boolean} isHorizontal defalut false
34453      */   
34454     isHorizontal : false, 
34455
34456     currentSize : null,
34457     
34458     tag: 'div',
34459     
34460     cls: '',
34461     
34462     bricks: null, //CompositeElement
34463     
34464     cols : 1,
34465     
34466     _isLayoutInited : false,
34467     
34468 //    isAlternative : false, // only use for vertical layout...
34469     
34470     /**
34471      * @cfg {Number} alternativePadWidth padding below box..
34472      */   
34473     alternativePadWidth : 50,
34474     
34475     selectedBrick : [],
34476     
34477     getAutoCreate : function(){
34478         
34479         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34480         
34481         var cfg = {
34482             tag: this.tag,
34483             cls: 'blog-masonary-wrapper ' + this.cls,
34484             cn : {
34485                 cls : 'mas-boxes masonary'
34486             }
34487         };
34488         
34489         return cfg;
34490     },
34491     
34492     getChildContainer: function( )
34493     {
34494         if (this.boxesEl) {
34495             return this.boxesEl;
34496         }
34497         
34498         this.boxesEl = this.el.select('.mas-boxes').first();
34499         
34500         return this.boxesEl;
34501     },
34502     
34503     
34504     initEvents : function()
34505     {
34506         var _this = this;
34507         
34508         if(this.isAutoInitial){
34509             Roo.log('hook children rendered');
34510             this.on('childrenrendered', function() {
34511                 Roo.log('children rendered');
34512                 _this.initial();
34513             } ,this);
34514         }
34515     },
34516     
34517     initial : function()
34518     {
34519         this.selectedBrick = [];
34520         
34521         this.currentSize = this.el.getBox(true);
34522         
34523         Roo.EventManager.onWindowResize(this.resize, this); 
34524
34525         if(!this.isAutoInitial){
34526             this.layout();
34527             return;
34528         }
34529         
34530         this.layout();
34531         
34532         return;
34533         //this.layout.defer(500,this);
34534         
34535     },
34536     
34537     resize : function()
34538     {
34539         var cs = this.el.getBox(true);
34540         
34541         if (
34542                 this.currentSize.width == cs.width && 
34543                 this.currentSize.x == cs.x && 
34544                 this.currentSize.height == cs.height && 
34545                 this.currentSize.y == cs.y 
34546         ) {
34547             Roo.log("no change in with or X or Y");
34548             return;
34549         }
34550         
34551         this.currentSize = cs;
34552         
34553         this.layout();
34554         
34555     },
34556     
34557     layout : function()
34558     {   
34559         this._resetLayout();
34560         
34561         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34562         
34563         this.layoutItems( isInstant );
34564       
34565         this._isLayoutInited = true;
34566         
34567         this.fireEvent('layout', this);
34568         
34569     },
34570     
34571     _resetLayout : function()
34572     {
34573         if(this.isHorizontal){
34574             this.horizontalMeasureColumns();
34575             return;
34576         }
34577         
34578         this.verticalMeasureColumns();
34579         
34580     },
34581     
34582     verticalMeasureColumns : function()
34583     {
34584         this.getContainerWidth();
34585         
34586 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34587 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34588 //            return;
34589 //        }
34590         
34591         var boxWidth = this.boxWidth + this.padWidth;
34592         
34593         if(this.containerWidth < this.boxWidth){
34594             boxWidth = this.containerWidth
34595         }
34596         
34597         var containerWidth = this.containerWidth;
34598         
34599         var cols = Math.floor(containerWidth / boxWidth);
34600         
34601         this.cols = Math.max( cols, 1 );
34602         
34603         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34604         
34605         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34606         
34607         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34608         
34609         this.colWidth = boxWidth + avail - this.padWidth;
34610         
34611         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34612         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34613     },
34614     
34615     horizontalMeasureColumns : function()
34616     {
34617         this.getContainerWidth();
34618         
34619         var boxWidth = this.boxWidth;
34620         
34621         if(this.containerWidth < boxWidth){
34622             boxWidth = this.containerWidth;
34623         }
34624         
34625         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34626         
34627         this.el.setHeight(boxWidth);
34628         
34629     },
34630     
34631     getContainerWidth : function()
34632     {
34633         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34634     },
34635     
34636     layoutItems : function( isInstant )
34637     {
34638         Roo.log(this.bricks);
34639         
34640         var items = Roo.apply([], this.bricks);
34641         
34642         if(this.isHorizontal){
34643             this._horizontalLayoutItems( items , isInstant );
34644             return;
34645         }
34646         
34647 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34648 //            this._verticalAlternativeLayoutItems( items , isInstant );
34649 //            return;
34650 //        }
34651         
34652         this._verticalLayoutItems( items , isInstant );
34653         
34654     },
34655     
34656     _verticalLayoutItems : function ( items , isInstant)
34657     {
34658         if ( !items || !items.length ) {
34659             return;
34660         }
34661         
34662         var standard = [
34663             ['xs', 'xs', 'xs', 'tall'],
34664             ['xs', 'xs', 'tall'],
34665             ['xs', 'xs', 'sm'],
34666             ['xs', 'xs', 'xs'],
34667             ['xs', 'tall'],
34668             ['xs', 'sm'],
34669             ['xs', 'xs'],
34670             ['xs'],
34671             
34672             ['sm', 'xs', 'xs'],
34673             ['sm', 'xs'],
34674             ['sm'],
34675             
34676             ['tall', 'xs', 'xs', 'xs'],
34677             ['tall', 'xs', 'xs'],
34678             ['tall', 'xs'],
34679             ['tall']
34680             
34681         ];
34682         
34683         var queue = [];
34684         
34685         var boxes = [];
34686         
34687         var box = [];
34688         
34689         Roo.each(items, function(item, k){
34690             
34691             switch (item.size) {
34692                 // these layouts take up a full box,
34693                 case 'md' :
34694                 case 'md-left' :
34695                 case 'md-right' :
34696                 case 'wide' :
34697                     
34698                     if(box.length){
34699                         boxes.push(box);
34700                         box = [];
34701                     }
34702                     
34703                     boxes.push([item]);
34704                     
34705                     break;
34706                     
34707                 case 'xs' :
34708                 case 'sm' :
34709                 case 'tall' :
34710                     
34711                     box.push(item);
34712                     
34713                     break;
34714                 default :
34715                     break;
34716                     
34717             }
34718             
34719         }, this);
34720         
34721         if(box.length){
34722             boxes.push(box);
34723             box = [];
34724         }
34725         
34726         var filterPattern = function(box, length)
34727         {
34728             if(!box.length){
34729                 return;
34730             }
34731             
34732             var match = false;
34733             
34734             var pattern = box.slice(0, length);
34735             
34736             var format = [];
34737             
34738             Roo.each(pattern, function(i){
34739                 format.push(i.size);
34740             }, this);
34741             
34742             Roo.each(standard, function(s){
34743                 
34744                 if(String(s) != String(format)){
34745                     return;
34746                 }
34747                 
34748                 match = true;
34749                 return false;
34750                 
34751             }, this);
34752             
34753             if(!match && length == 1){
34754                 return;
34755             }
34756             
34757             if(!match){
34758                 filterPattern(box, length - 1);
34759                 return;
34760             }
34761                 
34762             queue.push(pattern);
34763
34764             box = box.slice(length, box.length);
34765
34766             filterPattern(box, 4);
34767
34768             return;
34769             
34770         }
34771         
34772         Roo.each(boxes, function(box, k){
34773             
34774             if(!box.length){
34775                 return;
34776             }
34777             
34778             if(box.length == 1){
34779                 queue.push(box);
34780                 return;
34781             }
34782             
34783             filterPattern(box, 4);
34784             
34785         }, this);
34786         
34787         this._processVerticalLayoutQueue( queue, isInstant );
34788         
34789     },
34790     
34791 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34792 //    {
34793 //        if ( !items || !items.length ) {
34794 //            return;
34795 //        }
34796 //
34797 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34798 //        
34799 //    },
34800     
34801     _horizontalLayoutItems : function ( items , isInstant)
34802     {
34803         if ( !items || !items.length || items.length < 3) {
34804             return;
34805         }
34806         
34807         items.reverse();
34808         
34809         var eItems = items.slice(0, 3);
34810         
34811         items = items.slice(3, items.length);
34812         
34813         var standard = [
34814             ['xs', 'xs', 'xs', 'wide'],
34815             ['xs', 'xs', 'wide'],
34816             ['xs', 'xs', 'sm'],
34817             ['xs', 'xs', 'xs'],
34818             ['xs', 'wide'],
34819             ['xs', 'sm'],
34820             ['xs', 'xs'],
34821             ['xs'],
34822             
34823             ['sm', 'xs', 'xs'],
34824             ['sm', 'xs'],
34825             ['sm'],
34826             
34827             ['wide', 'xs', 'xs', 'xs'],
34828             ['wide', 'xs', 'xs'],
34829             ['wide', 'xs'],
34830             ['wide'],
34831             
34832             ['wide-thin']
34833         ];
34834         
34835         var queue = [];
34836         
34837         var boxes = [];
34838         
34839         var box = [];
34840         
34841         Roo.each(items, function(item, k){
34842             
34843             switch (item.size) {
34844                 case 'md' :
34845                 case 'md-left' :
34846                 case 'md-right' :
34847                 case 'tall' :
34848                     
34849                     if(box.length){
34850                         boxes.push(box);
34851                         box = [];
34852                     }
34853                     
34854                     boxes.push([item]);
34855                     
34856                     break;
34857                     
34858                 case 'xs' :
34859                 case 'sm' :
34860                 case 'wide' :
34861                 case 'wide-thin' :
34862                     
34863                     box.push(item);
34864                     
34865                     break;
34866                 default :
34867                     break;
34868                     
34869             }
34870             
34871         }, this);
34872         
34873         if(box.length){
34874             boxes.push(box);
34875             box = [];
34876         }
34877         
34878         var filterPattern = function(box, length)
34879         {
34880             if(!box.length){
34881                 return;
34882             }
34883             
34884             var match = false;
34885             
34886             var pattern = box.slice(0, length);
34887             
34888             var format = [];
34889             
34890             Roo.each(pattern, function(i){
34891                 format.push(i.size);
34892             }, this);
34893             
34894             Roo.each(standard, function(s){
34895                 
34896                 if(String(s) != String(format)){
34897                     return;
34898                 }
34899                 
34900                 match = true;
34901                 return false;
34902                 
34903             }, this);
34904             
34905             if(!match && length == 1){
34906                 return;
34907             }
34908             
34909             if(!match){
34910                 filterPattern(box, length - 1);
34911                 return;
34912             }
34913                 
34914             queue.push(pattern);
34915
34916             box = box.slice(length, box.length);
34917
34918             filterPattern(box, 4);
34919
34920             return;
34921             
34922         }
34923         
34924         Roo.each(boxes, function(box, k){
34925             
34926             if(!box.length){
34927                 return;
34928             }
34929             
34930             if(box.length == 1){
34931                 queue.push(box);
34932                 return;
34933             }
34934             
34935             filterPattern(box, 4);
34936             
34937         }, this);
34938         
34939         
34940         var prune = [];
34941         
34942         var pos = this.el.getBox(true);
34943         
34944         var minX = pos.x;
34945         
34946         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34947         
34948         var hit_end = false;
34949         
34950         Roo.each(queue, function(box){
34951             
34952             if(hit_end){
34953                 
34954                 Roo.each(box, function(b){
34955                 
34956                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34957                     b.el.hide();
34958
34959                 }, this);
34960
34961                 return;
34962             }
34963             
34964             var mx = 0;
34965             
34966             Roo.each(box, function(b){
34967                 
34968                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34969                 b.el.show();
34970
34971                 mx = Math.max(mx, b.x);
34972                 
34973             }, this);
34974             
34975             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34976             
34977             if(maxX < minX){
34978                 
34979                 Roo.each(box, function(b){
34980                 
34981                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34982                     b.el.hide();
34983                     
34984                 }, this);
34985                 
34986                 hit_end = true;
34987                 
34988                 return;
34989             }
34990             
34991             prune.push(box);
34992             
34993         }, this);
34994         
34995         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34996     },
34997     
34998     /** Sets position of item in DOM
34999     * @param {Element} item
35000     * @param {Number} x - horizontal position
35001     * @param {Number} y - vertical position
35002     * @param {Boolean} isInstant - disables transitions
35003     */
35004     _processVerticalLayoutQueue : function( queue, isInstant )
35005     {
35006         var pos = this.el.getBox(true);
35007         var x = pos.x;
35008         var y = pos.y;
35009         var maxY = [];
35010         
35011         for (var i = 0; i < this.cols; i++){
35012             maxY[i] = pos.y;
35013         }
35014         
35015         Roo.each(queue, function(box, k){
35016             
35017             var col = k % this.cols;
35018             
35019             Roo.each(box, function(b,kk){
35020                 
35021                 b.el.position('absolute');
35022                 
35023                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35024                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35025                 
35026                 if(b.size == 'md-left' || b.size == 'md-right'){
35027                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35028                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35029                 }
35030                 
35031                 b.el.setWidth(width);
35032                 b.el.setHeight(height);
35033                 // iframe?
35034                 b.el.select('iframe',true).setSize(width,height);
35035                 
35036             }, this);
35037             
35038             for (var i = 0; i < this.cols; i++){
35039                 
35040                 if(maxY[i] < maxY[col]){
35041                     col = i;
35042                     continue;
35043                 }
35044                 
35045                 col = Math.min(col, i);
35046                 
35047             }
35048             
35049             x = pos.x + col * (this.colWidth + this.padWidth);
35050             
35051             y = maxY[col];
35052             
35053             var positions = [];
35054             
35055             switch (box.length){
35056                 case 1 :
35057                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35058                     break;
35059                 case 2 :
35060                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35061                     break;
35062                 case 3 :
35063                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35064                     break;
35065                 case 4 :
35066                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35067                     break;
35068                 default :
35069                     break;
35070             }
35071             
35072             Roo.each(box, function(b,kk){
35073                 
35074                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35075                 
35076                 var sz = b.el.getSize();
35077                 
35078                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35079                 
35080             }, this);
35081             
35082         }, this);
35083         
35084         var mY = 0;
35085         
35086         for (var i = 0; i < this.cols; i++){
35087             mY = Math.max(mY, maxY[i]);
35088         }
35089         
35090         this.el.setHeight(mY - pos.y);
35091         
35092     },
35093     
35094 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35095 //    {
35096 //        var pos = this.el.getBox(true);
35097 //        var x = pos.x;
35098 //        var y = pos.y;
35099 //        var maxX = pos.right;
35100 //        
35101 //        var maxHeight = 0;
35102 //        
35103 //        Roo.each(items, function(item, k){
35104 //            
35105 //            var c = k % 2;
35106 //            
35107 //            item.el.position('absolute');
35108 //                
35109 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35110 //
35111 //            item.el.setWidth(width);
35112 //
35113 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35114 //
35115 //            item.el.setHeight(height);
35116 //            
35117 //            if(c == 0){
35118 //                item.el.setXY([x, y], isInstant ? false : true);
35119 //            } else {
35120 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35121 //            }
35122 //            
35123 //            y = y + height + this.alternativePadWidth;
35124 //            
35125 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35126 //            
35127 //        }, this);
35128 //        
35129 //        this.el.setHeight(maxHeight);
35130 //        
35131 //    },
35132     
35133     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35134     {
35135         var pos = this.el.getBox(true);
35136         
35137         var minX = pos.x;
35138         var minY = pos.y;
35139         
35140         var maxX = pos.right;
35141         
35142         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35143         
35144         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35145         
35146         Roo.each(queue, function(box, k){
35147             
35148             Roo.each(box, function(b, kk){
35149                 
35150                 b.el.position('absolute');
35151                 
35152                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35153                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35154                 
35155                 if(b.size == 'md-left' || b.size == 'md-right'){
35156                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35157                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35158                 }
35159                 
35160                 b.el.setWidth(width);
35161                 b.el.setHeight(height);
35162                 
35163             }, this);
35164             
35165             if(!box.length){
35166                 return;
35167             }
35168             
35169             var positions = [];
35170             
35171             switch (box.length){
35172                 case 1 :
35173                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35174                     break;
35175                 case 2 :
35176                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35177                     break;
35178                 case 3 :
35179                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35180                     break;
35181                 case 4 :
35182                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35183                     break;
35184                 default :
35185                     break;
35186             }
35187             
35188             Roo.each(box, function(b,kk){
35189                 
35190                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35191                 
35192                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35193                 
35194             }, this);
35195             
35196         }, this);
35197         
35198     },
35199     
35200     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35201     {
35202         Roo.each(eItems, function(b,k){
35203             
35204             b.size = (k == 0) ? 'sm' : 'xs';
35205             b.x = (k == 0) ? 2 : 1;
35206             b.y = (k == 0) ? 2 : 1;
35207             
35208             b.el.position('absolute');
35209             
35210             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35211                 
35212             b.el.setWidth(width);
35213             
35214             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35215             
35216             b.el.setHeight(height);
35217             
35218         }, this);
35219
35220         var positions = [];
35221         
35222         positions.push({
35223             x : maxX - this.unitWidth * 2 - this.gutter,
35224             y : minY
35225         });
35226         
35227         positions.push({
35228             x : maxX - this.unitWidth,
35229             y : minY + (this.unitWidth + this.gutter) * 2
35230         });
35231         
35232         positions.push({
35233             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35234             y : minY
35235         });
35236         
35237         Roo.each(eItems, function(b,k){
35238             
35239             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35240
35241         }, this);
35242         
35243     },
35244     
35245     getVerticalOneBoxColPositions : function(x, y, box)
35246     {
35247         var pos = [];
35248         
35249         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35250         
35251         if(box[0].size == 'md-left'){
35252             rand = 0;
35253         }
35254         
35255         if(box[0].size == 'md-right'){
35256             rand = 1;
35257         }
35258         
35259         pos.push({
35260             x : x + (this.unitWidth + this.gutter) * rand,
35261             y : y
35262         });
35263         
35264         return pos;
35265     },
35266     
35267     getVerticalTwoBoxColPositions : function(x, y, box)
35268     {
35269         var pos = [];
35270         
35271         if(box[0].size == 'xs'){
35272             
35273             pos.push({
35274                 x : x,
35275                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35276             });
35277
35278             pos.push({
35279                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35280                 y : y
35281             });
35282             
35283             return pos;
35284             
35285         }
35286         
35287         pos.push({
35288             x : x,
35289             y : y
35290         });
35291
35292         pos.push({
35293             x : x + (this.unitWidth + this.gutter) * 2,
35294             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35295         });
35296         
35297         return pos;
35298         
35299     },
35300     
35301     getVerticalThreeBoxColPositions : function(x, y, box)
35302     {
35303         var pos = [];
35304         
35305         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35306             
35307             pos.push({
35308                 x : x,
35309                 y : y
35310             });
35311
35312             pos.push({
35313                 x : x + (this.unitWidth + this.gutter) * 1,
35314                 y : y
35315             });
35316             
35317             pos.push({
35318                 x : x + (this.unitWidth + this.gutter) * 2,
35319                 y : y
35320             });
35321             
35322             return pos;
35323             
35324         }
35325         
35326         if(box[0].size == 'xs' && box[1].size == 'xs'){
35327             
35328             pos.push({
35329                 x : x,
35330                 y : y
35331             });
35332
35333             pos.push({
35334                 x : x,
35335                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35336             });
35337             
35338             pos.push({
35339                 x : x + (this.unitWidth + this.gutter) * 1,
35340                 y : y
35341             });
35342             
35343             return pos;
35344             
35345         }
35346         
35347         pos.push({
35348             x : x,
35349             y : y
35350         });
35351
35352         pos.push({
35353             x : x + (this.unitWidth + this.gutter) * 2,
35354             y : y
35355         });
35356
35357         pos.push({
35358             x : x + (this.unitWidth + this.gutter) * 2,
35359             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35360         });
35361             
35362         return pos;
35363         
35364     },
35365     
35366     getVerticalFourBoxColPositions : function(x, y, box)
35367     {
35368         var pos = [];
35369         
35370         if(box[0].size == 'xs'){
35371             
35372             pos.push({
35373                 x : x,
35374                 y : y
35375             });
35376
35377             pos.push({
35378                 x : x,
35379                 y : y + (this.unitHeight + this.gutter) * 1
35380             });
35381             
35382             pos.push({
35383                 x : x,
35384                 y : y + (this.unitHeight + this.gutter) * 2
35385             });
35386             
35387             pos.push({
35388                 x : x + (this.unitWidth + this.gutter) * 1,
35389                 y : y
35390             });
35391             
35392             return pos;
35393             
35394         }
35395         
35396         pos.push({
35397             x : x,
35398             y : y
35399         });
35400
35401         pos.push({
35402             x : x + (this.unitWidth + this.gutter) * 2,
35403             y : y
35404         });
35405
35406         pos.push({
35407             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35408             y : y + (this.unitHeight + this.gutter) * 1
35409         });
35410
35411         pos.push({
35412             x : x + (this.unitWidth + this.gutter) * 2,
35413             y : y + (this.unitWidth + this.gutter) * 2
35414         });
35415
35416         return pos;
35417         
35418     },
35419     
35420     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35421     {
35422         var pos = [];
35423         
35424         if(box[0].size == 'md-left'){
35425             pos.push({
35426                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35427                 y : minY
35428             });
35429             
35430             return pos;
35431         }
35432         
35433         if(box[0].size == 'md-right'){
35434             pos.push({
35435                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35436                 y : minY + (this.unitWidth + this.gutter) * 1
35437             });
35438             
35439             return pos;
35440         }
35441         
35442         var rand = Math.floor(Math.random() * (4 - box[0].y));
35443         
35444         pos.push({
35445             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35446             y : minY + (this.unitWidth + this.gutter) * rand
35447         });
35448         
35449         return pos;
35450         
35451     },
35452     
35453     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35454     {
35455         var pos = [];
35456         
35457         if(box[0].size == 'xs'){
35458             
35459             pos.push({
35460                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35461                 y : minY
35462             });
35463
35464             pos.push({
35465                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35466                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35467             });
35468             
35469             return pos;
35470             
35471         }
35472         
35473         pos.push({
35474             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35475             y : minY
35476         });
35477
35478         pos.push({
35479             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35480             y : minY + (this.unitWidth + this.gutter) * 2
35481         });
35482         
35483         return pos;
35484         
35485     },
35486     
35487     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35488     {
35489         var pos = [];
35490         
35491         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
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) * 1
35501             });
35502             
35503             pos.push({
35504                 x : maxX - 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         if(box[0].size == 'xs' && box[1].size == 'xs'){
35513             
35514             pos.push({
35515                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35516                 y : minY
35517             });
35518
35519             pos.push({
35520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35521                 y : minY
35522             });
35523             
35524             pos.push({
35525                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35526                 y : minY + (this.unitWidth + this.gutter) * 1
35527             });
35528             
35529             return pos;
35530             
35531         }
35532         
35533         pos.push({
35534             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35535             y : minY
35536         });
35537
35538         pos.push({
35539             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35540             y : minY + (this.unitWidth + this.gutter) * 2
35541         });
35542
35543         pos.push({
35544             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35545             y : minY + (this.unitWidth + this.gutter) * 2
35546         });
35547             
35548         return pos;
35549         
35550     },
35551     
35552     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35553     {
35554         var pos = [];
35555         
35556         if(box[0].size == 'xs'){
35557             
35558             pos.push({
35559                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35560                 y : minY
35561             });
35562
35563             pos.push({
35564                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35565                 y : minY
35566             });
35567             
35568             pos.push({
35569                 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),
35570                 y : minY
35571             });
35572             
35573             pos.push({
35574                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35575                 y : minY + (this.unitWidth + this.gutter) * 1
35576             });
35577             
35578             return pos;
35579             
35580         }
35581         
35582         pos.push({
35583             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35584             y : minY
35585         });
35586         
35587         pos.push({
35588             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35589             y : minY + (this.unitWidth + this.gutter) * 2
35590         });
35591         
35592         pos.push({
35593             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35594             y : minY + (this.unitWidth + this.gutter) * 2
35595         });
35596         
35597         pos.push({
35598             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),
35599             y : minY + (this.unitWidth + this.gutter) * 2
35600         });
35601
35602         return pos;
35603         
35604     },
35605     
35606     /**
35607     * remove a Masonry Brick
35608     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35609     */
35610     removeBrick : function(brick_id)
35611     {
35612         if (!brick_id) {
35613             return;
35614         }
35615         
35616         for (var i = 0; i<this.bricks.length; i++) {
35617             if (this.bricks[i].id == brick_id) {
35618                 this.bricks.splice(i,1);
35619                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35620                 this.initial();
35621             }
35622         }
35623     },
35624     
35625     /**
35626     * adds a Masonry Brick
35627     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35628     */
35629     addBrick : function(cfg)
35630     {
35631         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35632         //this.register(cn);
35633         cn.parentId = this.id;
35634         cn.render(this.el);
35635         return cn;
35636     },
35637     
35638     /**
35639     * register a Masonry Brick
35640     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35641     */
35642     
35643     register : function(brick)
35644     {
35645         this.bricks.push(brick);
35646         brick.masonryId = this.id;
35647     },
35648     
35649     /**
35650     * clear all the Masonry Brick
35651     */
35652     clearAll : function()
35653     {
35654         this.bricks = [];
35655         //this.getChildContainer().dom.innerHTML = "";
35656         this.el.dom.innerHTML = '';
35657     },
35658     
35659     getSelected : function()
35660     {
35661         if (!this.selectedBrick) {
35662             return false;
35663         }
35664         
35665         return this.selectedBrick;
35666     }
35667 });
35668
35669 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35670     
35671     groups: {},
35672      /**
35673     * register a Masonry Layout
35674     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35675     */
35676     
35677     register : function(layout)
35678     {
35679         this.groups[layout.id] = layout;
35680     },
35681     /**
35682     * fetch a  Masonry Layout based on the masonry layout ID
35683     * @param {string} the masonry layout to add
35684     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35685     */
35686     
35687     get: function(layout_id) {
35688         if (typeof(this.groups[layout_id]) == 'undefined') {
35689             return false;
35690         }
35691         return this.groups[layout_id] ;
35692     }
35693     
35694     
35695     
35696 });
35697
35698  
35699
35700  /**
35701  *
35702  * This is based on 
35703  * http://masonry.desandro.com
35704  *
35705  * The idea is to render all the bricks based on vertical width...
35706  *
35707  * The original code extends 'outlayer' - we might need to use that....
35708  * 
35709  */
35710
35711
35712 /**
35713  * @class Roo.bootstrap.LayoutMasonryAuto
35714  * @extends Roo.bootstrap.Component
35715  * Bootstrap Layout Masonry class
35716  * 
35717  * @constructor
35718  * Create a new Element
35719  * @param {Object} config The config object
35720  */
35721
35722 Roo.bootstrap.LayoutMasonryAuto = function(config){
35723     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35724 };
35725
35726 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35727     
35728       /**
35729      * @cfg {Boolean} isFitWidth  - resize the width..
35730      */   
35731     isFitWidth : false,  // options..
35732     /**
35733      * @cfg {Boolean} isOriginLeft = left align?
35734      */   
35735     isOriginLeft : true,
35736     /**
35737      * @cfg {Boolean} isOriginTop = top align?
35738      */   
35739     isOriginTop : false,
35740     /**
35741      * @cfg {Boolean} isLayoutInstant = no animation?
35742      */   
35743     isLayoutInstant : false, // needed?
35744     /**
35745      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35746      */   
35747     isResizingContainer : true,
35748     /**
35749      * @cfg {Number} columnWidth  width of the columns 
35750      */   
35751     
35752     columnWidth : 0,
35753     
35754     /**
35755      * @cfg {Number} maxCols maximum number of columns
35756      */   
35757     
35758     maxCols: 0,
35759     /**
35760      * @cfg {Number} padHeight padding below box..
35761      */   
35762     
35763     padHeight : 10, 
35764     
35765     /**
35766      * @cfg {Boolean} isAutoInitial defalut true
35767      */   
35768     
35769     isAutoInitial : true, 
35770     
35771     // private?
35772     gutter : 0,
35773     
35774     containerWidth: 0,
35775     initialColumnWidth : 0,
35776     currentSize : null,
35777     
35778     colYs : null, // array.
35779     maxY : 0,
35780     padWidth: 10,
35781     
35782     
35783     tag: 'div',
35784     cls: '',
35785     bricks: null, //CompositeElement
35786     cols : 0, // array?
35787     // element : null, // wrapped now this.el
35788     _isLayoutInited : null, 
35789     
35790     
35791     getAutoCreate : function(){
35792         
35793         var cfg = {
35794             tag: this.tag,
35795             cls: 'blog-masonary-wrapper ' + this.cls,
35796             cn : {
35797                 cls : 'mas-boxes masonary'
35798             }
35799         };
35800         
35801         return cfg;
35802     },
35803     
35804     getChildContainer: function( )
35805     {
35806         if (this.boxesEl) {
35807             return this.boxesEl;
35808         }
35809         
35810         this.boxesEl = this.el.select('.mas-boxes').first();
35811         
35812         return this.boxesEl;
35813     },
35814     
35815     
35816     initEvents : function()
35817     {
35818         var _this = this;
35819         
35820         if(this.isAutoInitial){
35821             Roo.log('hook children rendered');
35822             this.on('childrenrendered', function() {
35823                 Roo.log('children rendered');
35824                 _this.initial();
35825             } ,this);
35826         }
35827         
35828     },
35829     
35830     initial : function()
35831     {
35832         this.reloadItems();
35833
35834         this.currentSize = this.el.getBox(true);
35835
35836         /// was window resize... - let's see if this works..
35837         Roo.EventManager.onWindowResize(this.resize, this); 
35838
35839         if(!this.isAutoInitial){
35840             this.layout();
35841             return;
35842         }
35843         
35844         this.layout.defer(500,this);
35845     },
35846     
35847     reloadItems: function()
35848     {
35849         this.bricks = this.el.select('.masonry-brick', true);
35850         
35851         this.bricks.each(function(b) {
35852             //Roo.log(b.getSize());
35853             if (!b.attr('originalwidth')) {
35854                 b.attr('originalwidth',  b.getSize().width);
35855             }
35856             
35857         });
35858         
35859         Roo.log(this.bricks.elements.length);
35860     },
35861     
35862     resize : function()
35863     {
35864         Roo.log('resize');
35865         var cs = this.el.getBox(true);
35866         
35867         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35868             Roo.log("no change in with or X");
35869             return;
35870         }
35871         this.currentSize = cs;
35872         this.layout();
35873     },
35874     
35875     layout : function()
35876     {
35877          Roo.log('layout');
35878         this._resetLayout();
35879         //this._manageStamps();
35880       
35881         // don't animate first layout
35882         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35883         this.layoutItems( isInstant );
35884       
35885         // flag for initalized
35886         this._isLayoutInited = true;
35887     },
35888     
35889     layoutItems : function( isInstant )
35890     {
35891         //var items = this._getItemsForLayout( this.items );
35892         // original code supports filtering layout items.. we just ignore it..
35893         
35894         this._layoutItems( this.bricks , isInstant );
35895       
35896         this._postLayout();
35897     },
35898     _layoutItems : function ( items , isInstant)
35899     {
35900        //this.fireEvent( 'layout', this, items );
35901     
35902
35903         if ( !items || !items.elements.length ) {
35904           // no items, emit event with empty array
35905             return;
35906         }
35907
35908         var queue = [];
35909         items.each(function(item) {
35910             Roo.log("layout item");
35911             Roo.log(item);
35912             // get x/y object from method
35913             var position = this._getItemLayoutPosition( item );
35914             // enqueue
35915             position.item = item;
35916             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35917             queue.push( position );
35918         }, this);
35919       
35920         this._processLayoutQueue( queue );
35921     },
35922     /** Sets position of item in DOM
35923     * @param {Element} item
35924     * @param {Number} x - horizontal position
35925     * @param {Number} y - vertical position
35926     * @param {Boolean} isInstant - disables transitions
35927     */
35928     _processLayoutQueue : function( queue )
35929     {
35930         for ( var i=0, len = queue.length; i < len; i++ ) {
35931             var obj = queue[i];
35932             obj.item.position('absolute');
35933             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35934         }
35935     },
35936       
35937     
35938     /**
35939     * Any logic you want to do after each layout,
35940     * i.e. size the container
35941     */
35942     _postLayout : function()
35943     {
35944         this.resizeContainer();
35945     },
35946     
35947     resizeContainer : function()
35948     {
35949         if ( !this.isResizingContainer ) {
35950             return;
35951         }
35952         var size = this._getContainerSize();
35953         if ( size ) {
35954             this.el.setSize(size.width,size.height);
35955             this.boxesEl.setSize(size.width,size.height);
35956         }
35957     },
35958     
35959     
35960     
35961     _resetLayout : function()
35962     {
35963         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35964         this.colWidth = this.el.getWidth();
35965         //this.gutter = this.el.getWidth(); 
35966         
35967         this.measureColumns();
35968
35969         // reset column Y
35970         var i = this.cols;
35971         this.colYs = [];
35972         while (i--) {
35973             this.colYs.push( 0 );
35974         }
35975     
35976         this.maxY = 0;
35977     },
35978
35979     measureColumns : function()
35980     {
35981         this.getContainerWidth();
35982       // if columnWidth is 0, default to outerWidth of first item
35983         if ( !this.columnWidth ) {
35984             var firstItem = this.bricks.first();
35985             Roo.log(firstItem);
35986             this.columnWidth  = this.containerWidth;
35987             if (firstItem && firstItem.attr('originalwidth') ) {
35988                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35989             }
35990             // columnWidth fall back to item of first element
35991             Roo.log("set column width?");
35992                         this.initialColumnWidth = this.columnWidth  ;
35993
35994             // if first elem has no width, default to size of container
35995             
35996         }
35997         
35998         
35999         if (this.initialColumnWidth) {
36000             this.columnWidth = this.initialColumnWidth;
36001         }
36002         
36003         
36004             
36005         // column width is fixed at the top - however if container width get's smaller we should
36006         // reduce it...
36007         
36008         // this bit calcs how man columns..
36009             
36010         var columnWidth = this.columnWidth += this.gutter;
36011       
36012         // calculate columns
36013         var containerWidth = this.containerWidth + this.gutter;
36014         
36015         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36016         // fix rounding errors, typically with gutters
36017         var excess = columnWidth - containerWidth % columnWidth;
36018         
36019         
36020         // if overshoot is less than a pixel, round up, otherwise floor it
36021         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36022         cols = Math[ mathMethod ]( cols );
36023         this.cols = Math.max( cols, 1 );
36024         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36025         
36026          // padding positioning..
36027         var totalColWidth = this.cols * this.columnWidth;
36028         var padavail = this.containerWidth - totalColWidth;
36029         // so for 2 columns - we need 3 'pads'
36030         
36031         var padNeeded = (1+this.cols) * this.padWidth;
36032         
36033         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36034         
36035         this.columnWidth += padExtra
36036         //this.padWidth = Math.floor(padavail /  ( this.cols));
36037         
36038         // adjust colum width so that padding is fixed??
36039         
36040         // we have 3 columns ... total = width * 3
36041         // we have X left over... that should be used by 
36042         
36043         //if (this.expandC) {
36044             
36045         //}
36046         
36047         
36048         
36049     },
36050     
36051     getContainerWidth : function()
36052     {
36053        /* // container is parent if fit width
36054         var container = this.isFitWidth ? this.element.parentNode : this.element;
36055         // check that this.size and size are there
36056         // IE8 triggers resize on body size change, so they might not be
36057         
36058         var size = getSize( container );  //FIXME
36059         this.containerWidth = size && size.innerWidth; //FIXME
36060         */
36061          
36062         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36063         
36064     },
36065     
36066     _getItemLayoutPosition : function( item )  // what is item?
36067     {
36068         // we resize the item to our columnWidth..
36069       
36070         item.setWidth(this.columnWidth);
36071         item.autoBoxAdjust  = false;
36072         
36073         var sz = item.getSize();
36074  
36075         // how many columns does this brick span
36076         var remainder = this.containerWidth % this.columnWidth;
36077         
36078         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36079         // round if off by 1 pixel, otherwise use ceil
36080         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36081         colSpan = Math.min( colSpan, this.cols );
36082         
36083         // normally this should be '1' as we dont' currently allow multi width columns..
36084         
36085         var colGroup = this._getColGroup( colSpan );
36086         // get the minimum Y value from the columns
36087         var minimumY = Math.min.apply( Math, colGroup );
36088         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36089         
36090         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36091          
36092         // position the brick
36093         var position = {
36094             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36095             y: this.currentSize.y + minimumY + this.padHeight
36096         };
36097         
36098         Roo.log(position);
36099         // apply setHeight to necessary columns
36100         var setHeight = minimumY + sz.height + this.padHeight;
36101         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36102         
36103         var setSpan = this.cols + 1 - colGroup.length;
36104         for ( var i = 0; i < setSpan; i++ ) {
36105           this.colYs[ shortColIndex + i ] = setHeight ;
36106         }
36107       
36108         return position;
36109     },
36110     
36111     /**
36112      * @param {Number} colSpan - number of columns the element spans
36113      * @returns {Array} colGroup
36114      */
36115     _getColGroup : function( colSpan )
36116     {
36117         if ( colSpan < 2 ) {
36118           // if brick spans only one column, use all the column Ys
36119           return this.colYs;
36120         }
36121       
36122         var colGroup = [];
36123         // how many different places could this brick fit horizontally
36124         var groupCount = this.cols + 1 - colSpan;
36125         // for each group potential horizontal position
36126         for ( var i = 0; i < groupCount; i++ ) {
36127           // make an array of colY values for that one group
36128           var groupColYs = this.colYs.slice( i, i + colSpan );
36129           // and get the max value of the array
36130           colGroup[i] = Math.max.apply( Math, groupColYs );
36131         }
36132         return colGroup;
36133     },
36134     /*
36135     _manageStamp : function( stamp )
36136     {
36137         var stampSize =  stamp.getSize();
36138         var offset = stamp.getBox();
36139         // get the columns that this stamp affects
36140         var firstX = this.isOriginLeft ? offset.x : offset.right;
36141         var lastX = firstX + stampSize.width;
36142         var firstCol = Math.floor( firstX / this.columnWidth );
36143         firstCol = Math.max( 0, firstCol );
36144         
36145         var lastCol = Math.floor( lastX / this.columnWidth );
36146         // lastCol should not go over if multiple of columnWidth #425
36147         lastCol -= lastX % this.columnWidth ? 0 : 1;
36148         lastCol = Math.min( this.cols - 1, lastCol );
36149         
36150         // set colYs to bottom of the stamp
36151         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36152             stampSize.height;
36153             
36154         for ( var i = firstCol; i <= lastCol; i++ ) {
36155           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36156         }
36157     },
36158     */
36159     
36160     _getContainerSize : function()
36161     {
36162         this.maxY = Math.max.apply( Math, this.colYs );
36163         var size = {
36164             height: this.maxY
36165         };
36166       
36167         if ( this.isFitWidth ) {
36168             size.width = this._getContainerFitWidth();
36169         }
36170       
36171         return size;
36172     },
36173     
36174     _getContainerFitWidth : function()
36175     {
36176         var unusedCols = 0;
36177         // count unused columns
36178         var i = this.cols;
36179         while ( --i ) {
36180           if ( this.colYs[i] !== 0 ) {
36181             break;
36182           }
36183           unusedCols++;
36184         }
36185         // fit container to columns that have been used
36186         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36187     },
36188     
36189     needsResizeLayout : function()
36190     {
36191         var previousWidth = this.containerWidth;
36192         this.getContainerWidth();
36193         return previousWidth !== this.containerWidth;
36194     }
36195  
36196 });
36197
36198  
36199
36200  /*
36201  * - LGPL
36202  *
36203  * element
36204  * 
36205  */
36206
36207 /**
36208  * @class Roo.bootstrap.MasonryBrick
36209  * @extends Roo.bootstrap.Component
36210  * Bootstrap MasonryBrick class
36211  * 
36212  * @constructor
36213  * Create a new MasonryBrick
36214  * @param {Object} config The config object
36215  */
36216
36217 Roo.bootstrap.MasonryBrick = function(config){
36218     
36219     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36220     
36221     Roo.bootstrap.MasonryBrick.register(this);
36222     
36223     this.addEvents({
36224         // raw events
36225         /**
36226          * @event click
36227          * When a MasonryBrick is clcik
36228          * @param {Roo.bootstrap.MasonryBrick} this
36229          * @param {Roo.EventObject} e
36230          */
36231         "click" : true
36232     });
36233 };
36234
36235 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36236     
36237     /**
36238      * @cfg {String} title
36239      */   
36240     title : '',
36241     /**
36242      * @cfg {String} html
36243      */   
36244     html : '',
36245     /**
36246      * @cfg {String} bgimage
36247      */   
36248     bgimage : '',
36249     /**
36250      * @cfg {String} videourl
36251      */   
36252     videourl : '',
36253     /**
36254      * @cfg {String} cls
36255      */   
36256     cls : '',
36257     /**
36258      * @cfg {String} href
36259      */   
36260     href : '',
36261     /**
36262      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36263      */   
36264     size : 'xs',
36265     
36266     /**
36267      * @cfg {String} placetitle (center|bottom)
36268      */   
36269     placetitle : '',
36270     
36271     /**
36272      * @cfg {Boolean} isFitContainer defalut true
36273      */   
36274     isFitContainer : true, 
36275     
36276     /**
36277      * @cfg {Boolean} preventDefault defalut false
36278      */   
36279     preventDefault : false, 
36280     
36281     /**
36282      * @cfg {Boolean} inverse defalut false
36283      */   
36284     maskInverse : false, 
36285     
36286     getAutoCreate : function()
36287     {
36288         if(!this.isFitContainer){
36289             return this.getSplitAutoCreate();
36290         }
36291         
36292         var cls = 'masonry-brick masonry-brick-full';
36293         
36294         if(this.href.length){
36295             cls += ' masonry-brick-link';
36296         }
36297         
36298         if(this.bgimage.length){
36299             cls += ' masonry-brick-image';
36300         }
36301         
36302         if(this.maskInverse){
36303             cls += ' mask-inverse';
36304         }
36305         
36306         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36307             cls += ' enable-mask';
36308         }
36309         
36310         if(this.size){
36311             cls += ' masonry-' + this.size + '-brick';
36312         }
36313         
36314         if(this.placetitle.length){
36315             
36316             switch (this.placetitle) {
36317                 case 'center' :
36318                     cls += ' masonry-center-title';
36319                     break;
36320                 case 'bottom' :
36321                     cls += ' masonry-bottom-title';
36322                     break;
36323                 default:
36324                     break;
36325             }
36326             
36327         } else {
36328             if(!this.html.length && !this.bgimage.length){
36329                 cls += ' masonry-center-title';
36330             }
36331
36332             if(!this.html.length && this.bgimage.length){
36333                 cls += ' masonry-bottom-title';
36334             }
36335         }
36336         
36337         if(this.cls){
36338             cls += ' ' + this.cls;
36339         }
36340         
36341         var cfg = {
36342             tag: (this.href.length) ? 'a' : 'div',
36343             cls: cls,
36344             cn: [
36345                 {
36346                     tag: 'div',
36347                     cls: 'masonry-brick-mask'
36348                 },
36349                 {
36350                     tag: 'div',
36351                     cls: 'masonry-brick-paragraph',
36352                     cn: []
36353                 }
36354             ]
36355         };
36356         
36357         if(this.href.length){
36358             cfg.href = this.href;
36359         }
36360         
36361         var cn = cfg.cn[1].cn;
36362         
36363         if(this.title.length){
36364             cn.push({
36365                 tag: 'h4',
36366                 cls: 'masonry-brick-title',
36367                 html: this.title
36368             });
36369         }
36370         
36371         if(this.html.length){
36372             cn.push({
36373                 tag: 'p',
36374                 cls: 'masonry-brick-text',
36375                 html: this.html
36376             });
36377         }
36378         
36379         if (!this.title.length && !this.html.length) {
36380             cfg.cn[1].cls += ' hide';
36381         }
36382         
36383         if(this.bgimage.length){
36384             cfg.cn.push({
36385                 tag: 'img',
36386                 cls: 'masonry-brick-image-view',
36387                 src: this.bgimage
36388             });
36389         }
36390         
36391         if(this.videourl.length){
36392             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36393             // youtube support only?
36394             cfg.cn.push({
36395                 tag: 'iframe',
36396                 cls: 'masonry-brick-image-view',
36397                 src: vurl,
36398                 frameborder : 0,
36399                 allowfullscreen : true
36400             });
36401         }
36402         
36403         return cfg;
36404         
36405     },
36406     
36407     getSplitAutoCreate : function()
36408     {
36409         var cls = 'masonry-brick masonry-brick-split';
36410         
36411         if(this.href.length){
36412             cls += ' masonry-brick-link';
36413         }
36414         
36415         if(this.bgimage.length){
36416             cls += ' masonry-brick-image';
36417         }
36418         
36419         if(this.size){
36420             cls += ' masonry-' + this.size + '-brick';
36421         }
36422         
36423         switch (this.placetitle) {
36424             case 'center' :
36425                 cls += ' masonry-center-title';
36426                 break;
36427             case 'bottom' :
36428                 cls += ' masonry-bottom-title';
36429                 break;
36430             default:
36431                 if(!this.bgimage.length){
36432                     cls += ' masonry-center-title';
36433                 }
36434
36435                 if(this.bgimage.length){
36436                     cls += ' masonry-bottom-title';
36437                 }
36438                 break;
36439         }
36440         
36441         if(this.cls){
36442             cls += ' ' + this.cls;
36443         }
36444         
36445         var cfg = {
36446             tag: (this.href.length) ? 'a' : 'div',
36447             cls: cls,
36448             cn: [
36449                 {
36450                     tag: 'div',
36451                     cls: 'masonry-brick-split-head',
36452                     cn: [
36453                         {
36454                             tag: 'div',
36455                             cls: 'masonry-brick-paragraph',
36456                             cn: []
36457                         }
36458                     ]
36459                 },
36460                 {
36461                     tag: 'div',
36462                     cls: 'masonry-brick-split-body',
36463                     cn: []
36464                 }
36465             ]
36466         };
36467         
36468         if(this.href.length){
36469             cfg.href = this.href;
36470         }
36471         
36472         if(this.title.length){
36473             cfg.cn[0].cn[0].cn.push({
36474                 tag: 'h4',
36475                 cls: 'masonry-brick-title',
36476                 html: this.title
36477             });
36478         }
36479         
36480         if(this.html.length){
36481             cfg.cn[1].cn.push({
36482                 tag: 'p',
36483                 cls: 'masonry-brick-text',
36484                 html: this.html
36485             });
36486         }
36487
36488         if(this.bgimage.length){
36489             cfg.cn[0].cn.push({
36490                 tag: 'img',
36491                 cls: 'masonry-brick-image-view',
36492                 src: this.bgimage
36493             });
36494         }
36495         
36496         if(this.videourl.length){
36497             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36498             // youtube support only?
36499             cfg.cn[0].cn.cn.push({
36500                 tag: 'iframe',
36501                 cls: 'masonry-brick-image-view',
36502                 src: vurl,
36503                 frameborder : 0,
36504                 allowfullscreen : true
36505             });
36506         }
36507         
36508         return cfg;
36509     },
36510     
36511     initEvents: function() 
36512     {
36513         switch (this.size) {
36514             case 'xs' :
36515                 this.x = 1;
36516                 this.y = 1;
36517                 break;
36518             case 'sm' :
36519                 this.x = 2;
36520                 this.y = 2;
36521                 break;
36522             case 'md' :
36523             case 'md-left' :
36524             case 'md-right' :
36525                 this.x = 3;
36526                 this.y = 3;
36527                 break;
36528             case 'tall' :
36529                 this.x = 2;
36530                 this.y = 3;
36531                 break;
36532             case 'wide' :
36533                 this.x = 3;
36534                 this.y = 2;
36535                 break;
36536             case 'wide-thin' :
36537                 this.x = 3;
36538                 this.y = 1;
36539                 break;
36540                         
36541             default :
36542                 break;
36543         }
36544         
36545         if(Roo.isTouch){
36546             this.el.on('touchstart', this.onTouchStart, this);
36547             this.el.on('touchmove', this.onTouchMove, this);
36548             this.el.on('touchend', this.onTouchEnd, this);
36549             this.el.on('contextmenu', this.onContextMenu, this);
36550         } else {
36551             this.el.on('mouseenter'  ,this.enter, this);
36552             this.el.on('mouseleave', this.leave, this);
36553             this.el.on('click', this.onClick, this);
36554         }
36555         
36556         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36557             this.parent().bricks.push(this);   
36558         }
36559         
36560     },
36561     
36562     onClick: function(e, el)
36563     {
36564         var time = this.endTimer - this.startTimer;
36565         // Roo.log(e.preventDefault());
36566         if(Roo.isTouch){
36567             if(time > 1000){
36568                 e.preventDefault();
36569                 return;
36570             }
36571         }
36572         
36573         if(!this.preventDefault){
36574             return;
36575         }
36576         
36577         e.preventDefault();
36578         
36579         if (this.activeClass != '') {
36580             this.selectBrick();
36581         }
36582         
36583         this.fireEvent('click', this, e);
36584     },
36585     
36586     enter: function(e, el)
36587     {
36588         e.preventDefault();
36589         
36590         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36591             return;
36592         }
36593         
36594         if(this.bgimage.length && this.html.length){
36595             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36596         }
36597     },
36598     
36599     leave: function(e, el)
36600     {
36601         e.preventDefault();
36602         
36603         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36604             return;
36605         }
36606         
36607         if(this.bgimage.length && this.html.length){
36608             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36609         }
36610     },
36611     
36612     onTouchStart: function(e, el)
36613     {
36614 //        e.preventDefault();
36615         
36616         this.touchmoved = false;
36617         
36618         if(!this.isFitContainer){
36619             return;
36620         }
36621         
36622         if(!this.bgimage.length || !this.html.length){
36623             return;
36624         }
36625         
36626         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36627         
36628         this.timer = new Date().getTime();
36629         
36630     },
36631     
36632     onTouchMove: function(e, el)
36633     {
36634         this.touchmoved = true;
36635     },
36636     
36637     onContextMenu : function(e,el)
36638     {
36639         e.preventDefault();
36640         e.stopPropagation();
36641         return false;
36642     },
36643     
36644     onTouchEnd: function(e, el)
36645     {
36646 //        e.preventDefault();
36647         
36648         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36649         
36650             this.leave(e,el);
36651             
36652             return;
36653         }
36654         
36655         if(!this.bgimage.length || !this.html.length){
36656             
36657             if(this.href.length){
36658                 window.location.href = this.href;
36659             }
36660             
36661             return;
36662         }
36663         
36664         if(!this.isFitContainer){
36665             return;
36666         }
36667         
36668         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36669         
36670         window.location.href = this.href;
36671     },
36672     
36673     //selection on single brick only
36674     selectBrick : function() {
36675         
36676         if (!this.parentId) {
36677             return;
36678         }
36679         
36680         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36681         var index = m.selectedBrick.indexOf(this.id);
36682         
36683         if ( index > -1) {
36684             m.selectedBrick.splice(index,1);
36685             this.el.removeClass(this.activeClass);
36686             return;
36687         }
36688         
36689         for(var i = 0; i < m.selectedBrick.length; i++) {
36690             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36691             b.el.removeClass(b.activeClass);
36692         }
36693         
36694         m.selectedBrick = [];
36695         
36696         m.selectedBrick.push(this.id);
36697         this.el.addClass(this.activeClass);
36698         return;
36699     },
36700     
36701     isSelected : function(){
36702         return this.el.hasClass(this.activeClass);
36703         
36704     }
36705 });
36706
36707 Roo.apply(Roo.bootstrap.MasonryBrick, {
36708     
36709     //groups: {},
36710     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36711      /**
36712     * register a Masonry Brick
36713     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36714     */
36715     
36716     register : function(brick)
36717     {
36718         //this.groups[brick.id] = brick;
36719         this.groups.add(brick.id, brick);
36720     },
36721     /**
36722     * fetch a  masonry brick based on the masonry brick ID
36723     * @param {string} the masonry brick to add
36724     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36725     */
36726     
36727     get: function(brick_id) 
36728     {
36729         // if (typeof(this.groups[brick_id]) == 'undefined') {
36730         //     return false;
36731         // }
36732         // return this.groups[brick_id] ;
36733         
36734         if(this.groups.key(brick_id)) {
36735             return this.groups.key(brick_id);
36736         }
36737         
36738         return false;
36739     }
36740     
36741     
36742     
36743 });
36744
36745  /*
36746  * - LGPL
36747  *
36748  * element
36749  * 
36750  */
36751
36752 /**
36753  * @class Roo.bootstrap.Brick
36754  * @extends Roo.bootstrap.Component
36755  * Bootstrap Brick class
36756  * 
36757  * @constructor
36758  * Create a new Brick
36759  * @param {Object} config The config object
36760  */
36761
36762 Roo.bootstrap.Brick = function(config){
36763     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36764     
36765     this.addEvents({
36766         // raw events
36767         /**
36768          * @event click
36769          * When a Brick is click
36770          * @param {Roo.bootstrap.Brick} this
36771          * @param {Roo.EventObject} e
36772          */
36773         "click" : true
36774     });
36775 };
36776
36777 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36778     
36779     /**
36780      * @cfg {String} title
36781      */   
36782     title : '',
36783     /**
36784      * @cfg {String} html
36785      */   
36786     html : '',
36787     /**
36788      * @cfg {String} bgimage
36789      */   
36790     bgimage : '',
36791     /**
36792      * @cfg {String} cls
36793      */   
36794     cls : '',
36795     /**
36796      * @cfg {String} href
36797      */   
36798     href : '',
36799     /**
36800      * @cfg {String} video
36801      */   
36802     video : '',
36803     /**
36804      * @cfg {Boolean} square
36805      */   
36806     square : true,
36807     
36808     getAutoCreate : function()
36809     {
36810         var cls = 'roo-brick';
36811         
36812         if(this.href.length){
36813             cls += ' roo-brick-link';
36814         }
36815         
36816         if(this.bgimage.length){
36817             cls += ' roo-brick-image';
36818         }
36819         
36820         if(!this.html.length && !this.bgimage.length){
36821             cls += ' roo-brick-center-title';
36822         }
36823         
36824         if(!this.html.length && this.bgimage.length){
36825             cls += ' roo-brick-bottom-title';
36826         }
36827         
36828         if(this.cls){
36829             cls += ' ' + this.cls;
36830         }
36831         
36832         var cfg = {
36833             tag: (this.href.length) ? 'a' : 'div',
36834             cls: cls,
36835             cn: [
36836                 {
36837                     tag: 'div',
36838                     cls: 'roo-brick-paragraph',
36839                     cn: []
36840                 }
36841             ]
36842         };
36843         
36844         if(this.href.length){
36845             cfg.href = this.href;
36846         }
36847         
36848         var cn = cfg.cn[0].cn;
36849         
36850         if(this.title.length){
36851             cn.push({
36852                 tag: 'h4',
36853                 cls: 'roo-brick-title',
36854                 html: this.title
36855             });
36856         }
36857         
36858         if(this.html.length){
36859             cn.push({
36860                 tag: 'p',
36861                 cls: 'roo-brick-text',
36862                 html: this.html
36863             });
36864         } else {
36865             cn.cls += ' hide';
36866         }
36867         
36868         if(this.bgimage.length){
36869             cfg.cn.push({
36870                 tag: 'img',
36871                 cls: 'roo-brick-image-view',
36872                 src: this.bgimage
36873             });
36874         }
36875         
36876         return cfg;
36877     },
36878     
36879     initEvents: function() 
36880     {
36881         if(this.title.length || this.html.length){
36882             this.el.on('mouseenter'  ,this.enter, this);
36883             this.el.on('mouseleave', this.leave, this);
36884         }
36885         
36886         Roo.EventManager.onWindowResize(this.resize, this); 
36887         
36888         if(this.bgimage.length){
36889             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36890             this.imageEl.on('load', this.onImageLoad, this);
36891             return;
36892         }
36893         
36894         this.resize();
36895     },
36896     
36897     onImageLoad : function()
36898     {
36899         this.resize();
36900     },
36901     
36902     resize : function()
36903     {
36904         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36905         
36906         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36907         
36908         if(this.bgimage.length){
36909             var image = this.el.select('.roo-brick-image-view', true).first();
36910             
36911             image.setWidth(paragraph.getWidth());
36912             
36913             if(this.square){
36914                 image.setHeight(paragraph.getWidth());
36915             }
36916             
36917             this.el.setHeight(image.getHeight());
36918             paragraph.setHeight(image.getHeight());
36919             
36920         }
36921         
36922     },
36923     
36924     enter: function(e, el)
36925     {
36926         e.preventDefault();
36927         
36928         if(this.bgimage.length){
36929             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36930             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36931         }
36932     },
36933     
36934     leave: function(e, el)
36935     {
36936         e.preventDefault();
36937         
36938         if(this.bgimage.length){
36939             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36940             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36941         }
36942     }
36943     
36944 });
36945
36946  
36947
36948  /*
36949  * - LGPL
36950  *
36951  * Number field 
36952  */
36953
36954 /**
36955  * @class Roo.bootstrap.NumberField
36956  * @extends Roo.bootstrap.Input
36957  * Bootstrap NumberField class
36958  * 
36959  * 
36960  * 
36961  * 
36962  * @constructor
36963  * Create a new NumberField
36964  * @param {Object} config The config object
36965  */
36966
36967 Roo.bootstrap.NumberField = function(config){
36968     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36969 };
36970
36971 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36972     
36973     /**
36974      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36975      */
36976     allowDecimals : true,
36977     /**
36978      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36979      */
36980     decimalSeparator : ".",
36981     /**
36982      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36983      */
36984     decimalPrecision : 2,
36985     /**
36986      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36987      */
36988     allowNegative : true,
36989     
36990     /**
36991      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36992      */
36993     allowZero: true,
36994     /**
36995      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36996      */
36997     minValue : Number.NEGATIVE_INFINITY,
36998     /**
36999      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37000      */
37001     maxValue : Number.MAX_VALUE,
37002     /**
37003      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37004      */
37005     minText : "The minimum value for this field is {0}",
37006     /**
37007      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37008      */
37009     maxText : "The maximum value for this field is {0}",
37010     /**
37011      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37012      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37013      */
37014     nanText : "{0} is not a valid number",
37015     /**
37016      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37017      */
37018     thousandsDelimiter : false,
37019     /**
37020      * @cfg {String} valueAlign alignment of value
37021      */
37022     valueAlign : "left",
37023
37024     getAutoCreate : function()
37025     {
37026         var hiddenInput = {
37027             tag: 'input',
37028             type: 'hidden',
37029             id: Roo.id(),
37030             cls: 'hidden-number-input'
37031         };
37032         
37033         if (this.name) {
37034             hiddenInput.name = this.name;
37035         }
37036         
37037         this.name = '';
37038         
37039         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37040         
37041         this.name = hiddenInput.name;
37042         
37043         if(cfg.cn.length > 0) {
37044             cfg.cn.push(hiddenInput);
37045         }
37046         
37047         return cfg;
37048     },
37049
37050     // private
37051     initEvents : function()
37052     {   
37053         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37054         
37055         var allowed = "0123456789";
37056         
37057         if(this.allowDecimals){
37058             allowed += this.decimalSeparator;
37059         }
37060         
37061         if(this.allowNegative){
37062             allowed += "-";
37063         }
37064         
37065         if(this.thousandsDelimiter) {
37066             allowed += ",";
37067         }
37068         
37069         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37070         
37071         var keyPress = function(e){
37072             
37073             var k = e.getKey();
37074             
37075             var c = e.getCharCode();
37076             
37077             if(
37078                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37079                     allowed.indexOf(String.fromCharCode(c)) === -1
37080             ){
37081                 e.stopEvent();
37082                 return;
37083             }
37084             
37085             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37086                 return;
37087             }
37088             
37089             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37090                 e.stopEvent();
37091             }
37092         };
37093         
37094         this.el.on("keypress", keyPress, this);
37095     },
37096     
37097     validateValue : function(value)
37098     {
37099         
37100         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37101             return false;
37102         }
37103         
37104         var num = this.parseValue(value);
37105         
37106         if(isNaN(num)){
37107             this.markInvalid(String.format(this.nanText, value));
37108             return false;
37109         }
37110         
37111         if(num < this.minValue){
37112             this.markInvalid(String.format(this.minText, this.minValue));
37113             return false;
37114         }
37115         
37116         if(num > this.maxValue){
37117             this.markInvalid(String.format(this.maxText, this.maxValue));
37118             return false;
37119         }
37120         
37121         return true;
37122     },
37123
37124     getValue : function()
37125     {
37126         var v = this.hiddenEl().getValue();
37127         
37128         return this.fixPrecision(this.parseValue(v));
37129     },
37130
37131     parseValue : function(value)
37132     {
37133         if(this.thousandsDelimiter) {
37134             value += "";
37135             r = new RegExp(",", "g");
37136             value = value.replace(r, "");
37137         }
37138         
37139         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37140         return isNaN(value) ? '' : value;
37141     },
37142
37143     fixPrecision : function(value)
37144     {
37145         if(this.thousandsDelimiter) {
37146             value += "";
37147             r = new RegExp(",", "g");
37148             value = value.replace(r, "");
37149         }
37150         
37151         var nan = isNaN(value);
37152         
37153         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37154             return nan ? '' : value;
37155         }
37156         return parseFloat(value).toFixed(this.decimalPrecision);
37157     },
37158
37159     setValue : function(v)
37160     {
37161         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37162         
37163         this.value = v;
37164         
37165         if(this.rendered){
37166             
37167             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37168             
37169             this.inputEl().dom.value = (v == '') ? '' :
37170                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37171             
37172             if(!this.allowZero && v === '0') {
37173                 this.hiddenEl().dom.value = '';
37174                 this.inputEl().dom.value = '';
37175             }
37176             
37177             this.validate();
37178         }
37179     },
37180
37181     decimalPrecisionFcn : function(v)
37182     {
37183         return Math.floor(v);
37184     },
37185
37186     beforeBlur : function()
37187     {
37188         var v = this.parseValue(this.getRawValue());
37189         
37190         if(v || v === 0 || v === ''){
37191             this.setValue(v);
37192         }
37193     },
37194     
37195     hiddenEl : function()
37196     {
37197         return this.el.select('input.hidden-number-input',true).first();
37198     }
37199     
37200 });
37201
37202  
37203
37204 /*
37205 * Licence: LGPL
37206 */
37207
37208 /**
37209  * @class Roo.bootstrap.DocumentSlider
37210  * @extends Roo.bootstrap.Component
37211  * Bootstrap DocumentSlider class
37212  * 
37213  * @constructor
37214  * Create a new DocumentViewer
37215  * @param {Object} config The config object
37216  */
37217
37218 Roo.bootstrap.DocumentSlider = function(config){
37219     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37220     
37221     this.files = [];
37222     
37223     this.addEvents({
37224         /**
37225          * @event initial
37226          * Fire after initEvent
37227          * @param {Roo.bootstrap.DocumentSlider} this
37228          */
37229         "initial" : true,
37230         /**
37231          * @event update
37232          * Fire after update
37233          * @param {Roo.bootstrap.DocumentSlider} this
37234          */
37235         "update" : true,
37236         /**
37237          * @event click
37238          * Fire after click
37239          * @param {Roo.bootstrap.DocumentSlider} this
37240          */
37241         "click" : true
37242     });
37243 };
37244
37245 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37246     
37247     files : false,
37248     
37249     indicator : 0,
37250     
37251     getAutoCreate : function()
37252     {
37253         var cfg = {
37254             tag : 'div',
37255             cls : 'roo-document-slider',
37256             cn : [
37257                 {
37258                     tag : 'div',
37259                     cls : 'roo-document-slider-header',
37260                     cn : [
37261                         {
37262                             tag : 'div',
37263                             cls : 'roo-document-slider-header-title'
37264                         }
37265                     ]
37266                 },
37267                 {
37268                     tag : 'div',
37269                     cls : 'roo-document-slider-body',
37270                     cn : [
37271                         {
37272                             tag : 'div',
37273                             cls : 'roo-document-slider-prev',
37274                             cn : [
37275                                 {
37276                                     tag : 'i',
37277                                     cls : 'fa fa-chevron-left'
37278                                 }
37279                             ]
37280                         },
37281                         {
37282                             tag : 'div',
37283                             cls : 'roo-document-slider-thumb',
37284                             cn : [
37285                                 {
37286                                     tag : 'img',
37287                                     cls : 'roo-document-slider-image'
37288                                 }
37289                             ]
37290                         },
37291                         {
37292                             tag : 'div',
37293                             cls : 'roo-document-slider-next',
37294                             cn : [
37295                                 {
37296                                     tag : 'i',
37297                                     cls : 'fa fa-chevron-right'
37298                                 }
37299                             ]
37300                         }
37301                     ]
37302                 }
37303             ]
37304         };
37305         
37306         return cfg;
37307     },
37308     
37309     initEvents : function()
37310     {
37311         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37312         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37313         
37314         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37315         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37316         
37317         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37318         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37319         
37320         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37321         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37322         
37323         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37324         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37325         
37326         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37327         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37328         
37329         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37330         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37331         
37332         this.thumbEl.on('click', this.onClick, this);
37333         
37334         this.prevIndicator.on('click', this.prev, this);
37335         
37336         this.nextIndicator.on('click', this.next, this);
37337         
37338     },
37339     
37340     initial : function()
37341     {
37342         if(this.files.length){
37343             this.indicator = 1;
37344             this.update()
37345         }
37346         
37347         this.fireEvent('initial', this);
37348     },
37349     
37350     update : function()
37351     {
37352         this.imageEl.attr('src', this.files[this.indicator - 1]);
37353         
37354         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37355         
37356         this.prevIndicator.show();
37357         
37358         if(this.indicator == 1){
37359             this.prevIndicator.hide();
37360         }
37361         
37362         this.nextIndicator.show();
37363         
37364         if(this.indicator == this.files.length){
37365             this.nextIndicator.hide();
37366         }
37367         
37368         this.thumbEl.scrollTo('top');
37369         
37370         this.fireEvent('update', this);
37371     },
37372     
37373     onClick : function(e)
37374     {
37375         e.preventDefault();
37376         
37377         this.fireEvent('click', this);
37378     },
37379     
37380     prev : function(e)
37381     {
37382         e.preventDefault();
37383         
37384         this.indicator = Math.max(1, this.indicator - 1);
37385         
37386         this.update();
37387     },
37388     
37389     next : function(e)
37390     {
37391         e.preventDefault();
37392         
37393         this.indicator = Math.min(this.files.length, this.indicator + 1);
37394         
37395         this.update();
37396     }
37397 });
37398 /*
37399  * - LGPL
37400  *
37401  * RadioSet
37402  *
37403  *
37404  */
37405
37406 /**
37407  * @class Roo.bootstrap.RadioSet
37408  * @extends Roo.bootstrap.Input
37409  * Bootstrap RadioSet class
37410  * @cfg {String} indicatorpos (left|right) default left
37411  * @cfg {Boolean} inline (true|false) inline the element (default true)
37412  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37413  * @constructor
37414  * Create a new RadioSet
37415  * @param {Object} config The config object
37416  */
37417
37418 Roo.bootstrap.RadioSet = function(config){
37419     
37420     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37421     
37422     this.radioes = [];
37423     
37424     Roo.bootstrap.RadioSet.register(this);
37425     
37426     this.addEvents({
37427         /**
37428         * @event check
37429         * Fires when the element is checked or unchecked.
37430         * @param {Roo.bootstrap.RadioSet} this This radio
37431         * @param {Roo.bootstrap.Radio} item The checked item
37432         */
37433        check : true,
37434        /**
37435         * @event click
37436         * Fires when the element is click.
37437         * @param {Roo.bootstrap.RadioSet} this This radio set
37438         * @param {Roo.bootstrap.Radio} item The checked item
37439         * @param {Roo.EventObject} e The event object
37440         */
37441        click : true
37442     });
37443     
37444 };
37445
37446 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37447
37448     radioes : false,
37449     
37450     inline : true,
37451     
37452     weight : '',
37453     
37454     indicatorpos : 'left',
37455     
37456     getAutoCreate : function()
37457     {
37458         var label = {
37459             tag : 'label',
37460             cls : 'roo-radio-set-label',
37461             cn : [
37462                 {
37463                     tag : 'span',
37464                     html : this.fieldLabel
37465                 }
37466             ]
37467         };
37468         if (Roo.bootstrap.version == 3) {
37469             
37470             
37471             if(this.indicatorpos == 'left'){
37472                 label.cn.unshift({
37473                     tag : 'i',
37474                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37475                     tooltip : 'This field is required'
37476                 });
37477             } else {
37478                 label.cn.push({
37479                     tag : 'i',
37480                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37481                     tooltip : 'This field is required'
37482                 });
37483             }
37484         }
37485         var items = {
37486             tag : 'div',
37487             cls : 'roo-radio-set-items'
37488         };
37489         
37490         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37491         
37492         if (align === 'left' && this.fieldLabel.length) {
37493             
37494             items = {
37495                 cls : "roo-radio-set-right", 
37496                 cn: [
37497                     items
37498                 ]
37499             };
37500             
37501             if(this.labelWidth > 12){
37502                 label.style = "width: " + this.labelWidth + 'px';
37503             }
37504             
37505             if(this.labelWidth < 13 && this.labelmd == 0){
37506                 this.labelmd = this.labelWidth;
37507             }
37508             
37509             if(this.labellg > 0){
37510                 label.cls += ' col-lg-' + this.labellg;
37511                 items.cls += ' col-lg-' + (12 - this.labellg);
37512             }
37513             
37514             if(this.labelmd > 0){
37515                 label.cls += ' col-md-' + this.labelmd;
37516                 items.cls += ' col-md-' + (12 - this.labelmd);
37517             }
37518             
37519             if(this.labelsm > 0){
37520                 label.cls += ' col-sm-' + this.labelsm;
37521                 items.cls += ' col-sm-' + (12 - this.labelsm);
37522             }
37523             
37524             if(this.labelxs > 0){
37525                 label.cls += ' col-xs-' + this.labelxs;
37526                 items.cls += ' col-xs-' + (12 - this.labelxs);
37527             }
37528         }
37529         
37530         var cfg = {
37531             tag : 'div',
37532             cls : 'roo-radio-set',
37533             cn : [
37534                 {
37535                     tag : 'input',
37536                     cls : 'roo-radio-set-input',
37537                     type : 'hidden',
37538                     name : this.name,
37539                     value : this.value ? this.value :  ''
37540                 },
37541                 label,
37542                 items
37543             ]
37544         };
37545         
37546         if(this.weight.length){
37547             cfg.cls += ' roo-radio-' + this.weight;
37548         }
37549         
37550         if(this.inline) {
37551             cfg.cls += ' roo-radio-set-inline';
37552         }
37553         
37554         var settings=this;
37555         ['xs','sm','md','lg'].map(function(size){
37556             if (settings[size]) {
37557                 cfg.cls += ' col-' + size + '-' + settings[size];
37558             }
37559         });
37560         
37561         return cfg;
37562         
37563     },
37564
37565     initEvents : function()
37566     {
37567         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37568         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37569         
37570         if(!this.fieldLabel.length){
37571             this.labelEl.hide();
37572         }
37573         
37574         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37575         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37576         
37577         this.indicator = this.indicatorEl();
37578         
37579         if(this.indicator){
37580             this.indicator.addClass('invisible');
37581         }
37582         
37583         this.originalValue = this.getValue();
37584         
37585     },
37586     
37587     inputEl: function ()
37588     {
37589         return this.el.select('.roo-radio-set-input', true).first();
37590     },
37591     
37592     getChildContainer : function()
37593     {
37594         return this.itemsEl;
37595     },
37596     
37597     register : function(item)
37598     {
37599         this.radioes.push(item);
37600         
37601     },
37602     
37603     validate : function()
37604     {   
37605         if(this.getVisibilityEl().hasClass('hidden')){
37606             return true;
37607         }
37608         
37609         var valid = false;
37610         
37611         Roo.each(this.radioes, function(i){
37612             if(!i.checked){
37613                 return;
37614             }
37615             
37616             valid = true;
37617             return false;
37618         });
37619         
37620         if(this.allowBlank) {
37621             return true;
37622         }
37623         
37624         if(this.disabled || valid){
37625             this.markValid();
37626             return true;
37627         }
37628         
37629         this.markInvalid();
37630         return false;
37631         
37632     },
37633     
37634     markValid : function()
37635     {
37636         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37637             this.indicatorEl().removeClass('visible');
37638             this.indicatorEl().addClass('invisible');
37639         }
37640         
37641         
37642         if (Roo.bootstrap.version == 3) {
37643             this.el.removeClass([this.invalidClass, this.validClass]);
37644             this.el.addClass(this.validClass);
37645         } else {
37646             this.el.removeClass(['is-invalid','is-valid']);
37647             this.el.addClass(['is-valid']);
37648         }
37649         this.fireEvent('valid', this);
37650     },
37651     
37652     markInvalid : function(msg)
37653     {
37654         if(this.allowBlank || this.disabled){
37655             return;
37656         }
37657         
37658         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37659             this.indicatorEl().removeClass('invisible');
37660             this.indicatorEl().addClass('visible');
37661         }
37662         if (Roo.bootstrap.version == 3) {
37663             this.el.removeClass([this.invalidClass, this.validClass]);
37664             this.el.addClass(this.invalidClass);
37665         } else {
37666             this.el.removeClass(['is-invalid','is-valid']);
37667             this.el.addClass(['is-invalid']);
37668         }
37669         
37670         this.fireEvent('invalid', this, msg);
37671         
37672     },
37673     
37674     setValue : function(v, suppressEvent)
37675     {   
37676         if(this.value === v){
37677             return;
37678         }
37679         
37680         this.value = v;
37681         
37682         if(this.rendered){
37683             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37684         }
37685         
37686         Roo.each(this.radioes, function(i){
37687             i.checked = false;
37688             i.el.removeClass('checked');
37689         });
37690         
37691         Roo.each(this.radioes, function(i){
37692             
37693             if(i.value === v || i.value.toString() === v.toString()){
37694                 i.checked = true;
37695                 i.el.addClass('checked');
37696                 
37697                 if(suppressEvent !== true){
37698                     this.fireEvent('check', this, i);
37699                 }
37700                 
37701                 return false;
37702             }
37703             
37704         }, this);
37705         
37706         this.validate();
37707     },
37708     
37709     clearInvalid : function(){
37710         
37711         if(!this.el || this.preventMark){
37712             return;
37713         }
37714         
37715         this.el.removeClass([this.invalidClass]);
37716         
37717         this.fireEvent('valid', this);
37718     }
37719     
37720 });
37721
37722 Roo.apply(Roo.bootstrap.RadioSet, {
37723     
37724     groups: {},
37725     
37726     register : function(set)
37727     {
37728         this.groups[set.name] = set;
37729     },
37730     
37731     get: function(name) 
37732     {
37733         if (typeof(this.groups[name]) == 'undefined') {
37734             return false;
37735         }
37736         
37737         return this.groups[name] ;
37738     }
37739     
37740 });
37741 /*
37742  * Based on:
37743  * Ext JS Library 1.1.1
37744  * Copyright(c) 2006-2007, Ext JS, LLC.
37745  *
37746  * Originally Released Under LGPL - original licence link has changed is not relivant.
37747  *
37748  * Fork - LGPL
37749  * <script type="text/javascript">
37750  */
37751
37752
37753 /**
37754  * @class Roo.bootstrap.SplitBar
37755  * @extends Roo.util.Observable
37756  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37757  * <br><br>
37758  * Usage:
37759  * <pre><code>
37760 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37761                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37762 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37763 split.minSize = 100;
37764 split.maxSize = 600;
37765 split.animate = true;
37766 split.on('moved', splitterMoved);
37767 </code></pre>
37768  * @constructor
37769  * Create a new SplitBar
37770  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37771  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37772  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37773  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37774                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37775                         position of the SplitBar).
37776  */
37777 Roo.bootstrap.SplitBar = function(cfg){
37778     
37779     /** @private */
37780     
37781     //{
37782     //  dragElement : elm
37783     //  resizingElement: el,
37784         // optional..
37785     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37786     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37787         // existingProxy ???
37788     //}
37789     
37790     this.el = Roo.get(cfg.dragElement, true);
37791     this.el.dom.unselectable = "on";
37792     /** @private */
37793     this.resizingEl = Roo.get(cfg.resizingElement, true);
37794
37795     /**
37796      * @private
37797      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37798      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37799      * @type Number
37800      */
37801     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37802     
37803     /**
37804      * The minimum size of the resizing element. (Defaults to 0)
37805      * @type Number
37806      */
37807     this.minSize = 0;
37808     
37809     /**
37810      * The maximum size of the resizing element. (Defaults to 2000)
37811      * @type Number
37812      */
37813     this.maxSize = 2000;
37814     
37815     /**
37816      * Whether to animate the transition to the new size
37817      * @type Boolean
37818      */
37819     this.animate = false;
37820     
37821     /**
37822      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37823      * @type Boolean
37824      */
37825     this.useShim = false;
37826     
37827     /** @private */
37828     this.shim = null;
37829     
37830     if(!cfg.existingProxy){
37831         /** @private */
37832         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37833     }else{
37834         this.proxy = Roo.get(cfg.existingProxy).dom;
37835     }
37836     /** @private */
37837     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37838     
37839     /** @private */
37840     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37841     
37842     /** @private */
37843     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37844     
37845     /** @private */
37846     this.dragSpecs = {};
37847     
37848     /**
37849      * @private The adapter to use to positon and resize elements
37850      */
37851     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37852     this.adapter.init(this);
37853     
37854     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37855         /** @private */
37856         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37857         this.el.addClass("roo-splitbar-h");
37858     }else{
37859         /** @private */
37860         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37861         this.el.addClass("roo-splitbar-v");
37862     }
37863     
37864     this.addEvents({
37865         /**
37866          * @event resize
37867          * Fires when the splitter is moved (alias for {@link #event-moved})
37868          * @param {Roo.bootstrap.SplitBar} this
37869          * @param {Number} newSize the new width or height
37870          */
37871         "resize" : true,
37872         /**
37873          * @event moved
37874          * Fires when the splitter is moved
37875          * @param {Roo.bootstrap.SplitBar} this
37876          * @param {Number} newSize the new width or height
37877          */
37878         "moved" : true,
37879         /**
37880          * @event beforeresize
37881          * Fires before the splitter is dragged
37882          * @param {Roo.bootstrap.SplitBar} this
37883          */
37884         "beforeresize" : true,
37885
37886         "beforeapply" : true
37887     });
37888
37889     Roo.util.Observable.call(this);
37890 };
37891
37892 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37893     onStartProxyDrag : function(x, y){
37894         this.fireEvent("beforeresize", this);
37895         if(!this.overlay){
37896             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37897             o.unselectable();
37898             o.enableDisplayMode("block");
37899             // all splitbars share the same overlay
37900             Roo.bootstrap.SplitBar.prototype.overlay = o;
37901         }
37902         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37903         this.overlay.show();
37904         Roo.get(this.proxy).setDisplayed("block");
37905         var size = this.adapter.getElementSize(this);
37906         this.activeMinSize = this.getMinimumSize();;
37907         this.activeMaxSize = this.getMaximumSize();;
37908         var c1 = size - this.activeMinSize;
37909         var c2 = Math.max(this.activeMaxSize - size, 0);
37910         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37911             this.dd.resetConstraints();
37912             this.dd.setXConstraint(
37913                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37914                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37915             );
37916             this.dd.setYConstraint(0, 0);
37917         }else{
37918             this.dd.resetConstraints();
37919             this.dd.setXConstraint(0, 0);
37920             this.dd.setYConstraint(
37921                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37922                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37923             );
37924          }
37925         this.dragSpecs.startSize = size;
37926         this.dragSpecs.startPoint = [x, y];
37927         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37928     },
37929     
37930     /** 
37931      * @private Called after the drag operation by the DDProxy
37932      */
37933     onEndProxyDrag : function(e){
37934         Roo.get(this.proxy).setDisplayed(false);
37935         var endPoint = Roo.lib.Event.getXY(e);
37936         if(this.overlay){
37937             this.overlay.hide();
37938         }
37939         var newSize;
37940         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37941             newSize = this.dragSpecs.startSize + 
37942                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37943                     endPoint[0] - this.dragSpecs.startPoint[0] :
37944                     this.dragSpecs.startPoint[0] - endPoint[0]
37945                 );
37946         }else{
37947             newSize = this.dragSpecs.startSize + 
37948                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37949                     endPoint[1] - this.dragSpecs.startPoint[1] :
37950                     this.dragSpecs.startPoint[1] - endPoint[1]
37951                 );
37952         }
37953         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37954         if(newSize != this.dragSpecs.startSize){
37955             if(this.fireEvent('beforeapply', this, newSize) !== false){
37956                 this.adapter.setElementSize(this, newSize);
37957                 this.fireEvent("moved", this, newSize);
37958                 this.fireEvent("resize", this, newSize);
37959             }
37960         }
37961     },
37962     
37963     /**
37964      * Get the adapter this SplitBar uses
37965      * @return The adapter object
37966      */
37967     getAdapter : function(){
37968         return this.adapter;
37969     },
37970     
37971     /**
37972      * Set the adapter this SplitBar uses
37973      * @param {Object} adapter A SplitBar adapter object
37974      */
37975     setAdapter : function(adapter){
37976         this.adapter = adapter;
37977         this.adapter.init(this);
37978     },
37979     
37980     /**
37981      * Gets the minimum size for the resizing element
37982      * @return {Number} The minimum size
37983      */
37984     getMinimumSize : function(){
37985         return this.minSize;
37986     },
37987     
37988     /**
37989      * Sets the minimum size for the resizing element
37990      * @param {Number} minSize The minimum size
37991      */
37992     setMinimumSize : function(minSize){
37993         this.minSize = minSize;
37994     },
37995     
37996     /**
37997      * Gets the maximum size for the resizing element
37998      * @return {Number} The maximum size
37999      */
38000     getMaximumSize : function(){
38001         return this.maxSize;
38002     },
38003     
38004     /**
38005      * Sets the maximum size for the resizing element
38006      * @param {Number} maxSize The maximum size
38007      */
38008     setMaximumSize : function(maxSize){
38009         this.maxSize = maxSize;
38010     },
38011     
38012     /**
38013      * Sets the initialize size for the resizing element
38014      * @param {Number} size The initial size
38015      */
38016     setCurrentSize : function(size){
38017         var oldAnimate = this.animate;
38018         this.animate = false;
38019         this.adapter.setElementSize(this, size);
38020         this.animate = oldAnimate;
38021     },
38022     
38023     /**
38024      * Destroy this splitbar. 
38025      * @param {Boolean} removeEl True to remove the element
38026      */
38027     destroy : function(removeEl){
38028         if(this.shim){
38029             this.shim.remove();
38030         }
38031         this.dd.unreg();
38032         this.proxy.parentNode.removeChild(this.proxy);
38033         if(removeEl){
38034             this.el.remove();
38035         }
38036     }
38037 });
38038
38039 /**
38040  * @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.
38041  */
38042 Roo.bootstrap.SplitBar.createProxy = function(dir){
38043     var proxy = new Roo.Element(document.createElement("div"));
38044     proxy.unselectable();
38045     var cls = 'roo-splitbar-proxy';
38046     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38047     document.body.appendChild(proxy.dom);
38048     return proxy.dom;
38049 };
38050
38051 /** 
38052  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38053  * Default Adapter. It assumes the splitter and resizing element are not positioned
38054  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38055  */
38056 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38057 };
38058
38059 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38060     // do nothing for now
38061     init : function(s){
38062     
38063     },
38064     /**
38065      * Called before drag operations to get the current size of the resizing element. 
38066      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38067      */
38068      getElementSize : function(s){
38069         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38070             return s.resizingEl.getWidth();
38071         }else{
38072             return s.resizingEl.getHeight();
38073         }
38074     },
38075     
38076     /**
38077      * Called after drag operations to set the size of the resizing element.
38078      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38079      * @param {Number} newSize The new size to set
38080      * @param {Function} onComplete A function to be invoked when resizing is complete
38081      */
38082     setElementSize : function(s, newSize, onComplete){
38083         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38084             if(!s.animate){
38085                 s.resizingEl.setWidth(newSize);
38086                 if(onComplete){
38087                     onComplete(s, newSize);
38088                 }
38089             }else{
38090                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38091             }
38092         }else{
38093             
38094             if(!s.animate){
38095                 s.resizingEl.setHeight(newSize);
38096                 if(onComplete){
38097                     onComplete(s, newSize);
38098                 }
38099             }else{
38100                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38101             }
38102         }
38103     }
38104 };
38105
38106 /** 
38107  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38108  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38109  * Adapter that  moves the splitter element to align with the resized sizing element. 
38110  * Used with an absolute positioned SplitBar.
38111  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38112  * document.body, make sure you assign an id to the body element.
38113  */
38114 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38115     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38116     this.container = Roo.get(container);
38117 };
38118
38119 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38120     init : function(s){
38121         this.basic.init(s);
38122     },
38123     
38124     getElementSize : function(s){
38125         return this.basic.getElementSize(s);
38126     },
38127     
38128     setElementSize : function(s, newSize, onComplete){
38129         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38130     },
38131     
38132     moveSplitter : function(s){
38133         var yes = Roo.bootstrap.SplitBar;
38134         switch(s.placement){
38135             case yes.LEFT:
38136                 s.el.setX(s.resizingEl.getRight());
38137                 break;
38138             case yes.RIGHT:
38139                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38140                 break;
38141             case yes.TOP:
38142                 s.el.setY(s.resizingEl.getBottom());
38143                 break;
38144             case yes.BOTTOM:
38145                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38146                 break;
38147         }
38148     }
38149 };
38150
38151 /**
38152  * Orientation constant - Create a vertical SplitBar
38153  * @static
38154  * @type Number
38155  */
38156 Roo.bootstrap.SplitBar.VERTICAL = 1;
38157
38158 /**
38159  * Orientation constant - Create a horizontal SplitBar
38160  * @static
38161  * @type Number
38162  */
38163 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38164
38165 /**
38166  * Placement constant - The resizing element is to the left of the splitter element
38167  * @static
38168  * @type Number
38169  */
38170 Roo.bootstrap.SplitBar.LEFT = 1;
38171
38172 /**
38173  * Placement constant - The resizing element is to the right of the splitter element
38174  * @static
38175  * @type Number
38176  */
38177 Roo.bootstrap.SplitBar.RIGHT = 2;
38178
38179 /**
38180  * Placement constant - The resizing element is positioned above the splitter element
38181  * @static
38182  * @type Number
38183  */
38184 Roo.bootstrap.SplitBar.TOP = 3;
38185
38186 /**
38187  * Placement constant - The resizing element is positioned under splitter element
38188  * @static
38189  * @type Number
38190  */
38191 Roo.bootstrap.SplitBar.BOTTOM = 4;
38192 Roo.namespace("Roo.bootstrap.layout");/*
38193  * Based on:
38194  * Ext JS Library 1.1.1
38195  * Copyright(c) 2006-2007, Ext JS, LLC.
38196  *
38197  * Originally Released Under LGPL - original licence link has changed is not relivant.
38198  *
38199  * Fork - LGPL
38200  * <script type="text/javascript">
38201  */
38202
38203 /**
38204  * @class Roo.bootstrap.layout.Manager
38205  * @extends Roo.bootstrap.Component
38206  * Base class for layout managers.
38207  */
38208 Roo.bootstrap.layout.Manager = function(config)
38209 {
38210     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38211
38212
38213
38214
38215
38216     /** false to disable window resize monitoring @type Boolean */
38217     this.monitorWindowResize = true;
38218     this.regions = {};
38219     this.addEvents({
38220         /**
38221          * @event layout
38222          * Fires when a layout is performed.
38223          * @param {Roo.LayoutManager} this
38224          */
38225         "layout" : true,
38226         /**
38227          * @event regionresized
38228          * Fires when the user resizes a region.
38229          * @param {Roo.LayoutRegion} region The resized region
38230          * @param {Number} newSize The new size (width for east/west, height for north/south)
38231          */
38232         "regionresized" : true,
38233         /**
38234          * @event regioncollapsed
38235          * Fires when a region is collapsed.
38236          * @param {Roo.LayoutRegion} region The collapsed region
38237          */
38238         "regioncollapsed" : true,
38239         /**
38240          * @event regionexpanded
38241          * Fires when a region is expanded.
38242          * @param {Roo.LayoutRegion} region The expanded region
38243          */
38244         "regionexpanded" : true
38245     });
38246     this.updating = false;
38247
38248     if (config.el) {
38249         this.el = Roo.get(config.el);
38250         this.initEvents();
38251     }
38252
38253 };
38254
38255 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38256
38257
38258     regions : null,
38259
38260     monitorWindowResize : true,
38261
38262
38263     updating : false,
38264
38265
38266     onRender : function(ct, position)
38267     {
38268         if(!this.el){
38269             this.el = Roo.get(ct);
38270             this.initEvents();
38271         }
38272         //this.fireEvent('render',this);
38273     },
38274
38275
38276     initEvents: function()
38277     {
38278
38279
38280         // ie scrollbar fix
38281         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38282             document.body.scroll = "no";
38283         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38284             this.el.position('relative');
38285         }
38286         this.id = this.el.id;
38287         this.el.addClass("roo-layout-container");
38288         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38289         if(this.el.dom != document.body ) {
38290             this.el.on('resize', this.layout,this);
38291             this.el.on('show', this.layout,this);
38292         }
38293
38294     },
38295
38296     /**
38297      * Returns true if this layout is currently being updated
38298      * @return {Boolean}
38299      */
38300     isUpdating : function(){
38301         return this.updating;
38302     },
38303
38304     /**
38305      * Suspend the LayoutManager from doing auto-layouts while
38306      * making multiple add or remove calls
38307      */
38308     beginUpdate : function(){
38309         this.updating = true;
38310     },
38311
38312     /**
38313      * Restore auto-layouts and optionally disable the manager from performing a layout
38314      * @param {Boolean} noLayout true to disable a layout update
38315      */
38316     endUpdate : function(noLayout){
38317         this.updating = false;
38318         if(!noLayout){
38319             this.layout();
38320         }
38321     },
38322
38323     layout: function(){
38324         // abstract...
38325     },
38326
38327     onRegionResized : function(region, newSize){
38328         this.fireEvent("regionresized", region, newSize);
38329         this.layout();
38330     },
38331
38332     onRegionCollapsed : function(region){
38333         this.fireEvent("regioncollapsed", region);
38334     },
38335
38336     onRegionExpanded : function(region){
38337         this.fireEvent("regionexpanded", region);
38338     },
38339
38340     /**
38341      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38342      * performs box-model adjustments.
38343      * @return {Object} The size as an object {width: (the width), height: (the height)}
38344      */
38345     getViewSize : function()
38346     {
38347         var size;
38348         if(this.el.dom != document.body){
38349             size = this.el.getSize();
38350         }else{
38351             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38352         }
38353         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38354         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38355         return size;
38356     },
38357
38358     /**
38359      * Returns the Element this layout is bound to.
38360      * @return {Roo.Element}
38361      */
38362     getEl : function(){
38363         return this.el;
38364     },
38365
38366     /**
38367      * Returns the specified region.
38368      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38369      * @return {Roo.LayoutRegion}
38370      */
38371     getRegion : function(target){
38372         return this.regions[target.toLowerCase()];
38373     },
38374
38375     onWindowResize : function(){
38376         if(this.monitorWindowResize){
38377             this.layout();
38378         }
38379     }
38380 });
38381 /*
38382  * Based on:
38383  * Ext JS Library 1.1.1
38384  * Copyright(c) 2006-2007, Ext JS, LLC.
38385  *
38386  * Originally Released Under LGPL - original licence link has changed is not relivant.
38387  *
38388  * Fork - LGPL
38389  * <script type="text/javascript">
38390  */
38391 /**
38392  * @class Roo.bootstrap.layout.Border
38393  * @extends Roo.bootstrap.layout.Manager
38394  * @builder-top
38395  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38396  * please see: examples/bootstrap/nested.html<br><br>
38397  
38398 <b>The container the layout is rendered into can be either the body element or any other element.
38399 If it is not the body element, the container needs to either be an absolute positioned element,
38400 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38401 the container size if it is not the body element.</b>
38402
38403 * @constructor
38404 * Create a new Border
38405 * @param {Object} config Configuration options
38406  */
38407 Roo.bootstrap.layout.Border = function(config){
38408     config = config || {};
38409     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38410     
38411     
38412     
38413     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38414         if(config[region]){
38415             config[region].region = region;
38416             this.addRegion(config[region]);
38417         }
38418     },this);
38419     
38420 };
38421
38422 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38423
38424 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38425     
38426     parent : false, // this might point to a 'nest' or a ???
38427     
38428     /**
38429      * Creates and adds a new region if it doesn't already exist.
38430      * @param {String} target The target region key (north, south, east, west or center).
38431      * @param {Object} config The regions config object
38432      * @return {BorderLayoutRegion} The new region
38433      */
38434     addRegion : function(config)
38435     {
38436         if(!this.regions[config.region]){
38437             var r = this.factory(config);
38438             this.bindRegion(r);
38439         }
38440         return this.regions[config.region];
38441     },
38442
38443     // private (kinda)
38444     bindRegion : function(r){
38445         this.regions[r.config.region] = r;
38446         
38447         r.on("visibilitychange",    this.layout, this);
38448         r.on("paneladded",          this.layout, this);
38449         r.on("panelremoved",        this.layout, this);
38450         r.on("invalidated",         this.layout, this);
38451         r.on("resized",             this.onRegionResized, this);
38452         r.on("collapsed",           this.onRegionCollapsed, this);
38453         r.on("expanded",            this.onRegionExpanded, this);
38454     },
38455
38456     /**
38457      * Performs a layout update.
38458      */
38459     layout : function()
38460     {
38461         if(this.updating) {
38462             return;
38463         }
38464         
38465         // render all the rebions if they have not been done alreayd?
38466         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38467             if(this.regions[region] && !this.regions[region].bodyEl){
38468                 this.regions[region].onRender(this.el)
38469             }
38470         },this);
38471         
38472         var size = this.getViewSize();
38473         var w = size.width;
38474         var h = size.height;
38475         var centerW = w;
38476         var centerH = h;
38477         var centerY = 0;
38478         var centerX = 0;
38479         //var x = 0, y = 0;
38480
38481         var rs = this.regions;
38482         var north = rs["north"];
38483         var south = rs["south"]; 
38484         var west = rs["west"];
38485         var east = rs["east"];
38486         var center = rs["center"];
38487         //if(this.hideOnLayout){ // not supported anymore
38488             //c.el.setStyle("display", "none");
38489         //}
38490         if(north && north.isVisible()){
38491             var b = north.getBox();
38492             var m = north.getMargins();
38493             b.width = w - (m.left+m.right);
38494             b.x = m.left;
38495             b.y = m.top;
38496             centerY = b.height + b.y + m.bottom;
38497             centerH -= centerY;
38498             north.updateBox(this.safeBox(b));
38499         }
38500         if(south && south.isVisible()){
38501             var b = south.getBox();
38502             var m = south.getMargins();
38503             b.width = w - (m.left+m.right);
38504             b.x = m.left;
38505             var totalHeight = (b.height + m.top + m.bottom);
38506             b.y = h - totalHeight + m.top;
38507             centerH -= totalHeight;
38508             south.updateBox(this.safeBox(b));
38509         }
38510         if(west && west.isVisible()){
38511             var b = west.getBox();
38512             var m = west.getMargins();
38513             b.height = centerH - (m.top+m.bottom);
38514             b.x = m.left;
38515             b.y = centerY + m.top;
38516             var totalWidth = (b.width + m.left + m.right);
38517             centerX += totalWidth;
38518             centerW -= totalWidth;
38519             west.updateBox(this.safeBox(b));
38520         }
38521         if(east && east.isVisible()){
38522             var b = east.getBox();
38523             var m = east.getMargins();
38524             b.height = centerH - (m.top+m.bottom);
38525             var totalWidth = (b.width + m.left + m.right);
38526             b.x = w - totalWidth + m.left;
38527             b.y = centerY + m.top;
38528             centerW -= totalWidth;
38529             east.updateBox(this.safeBox(b));
38530         }
38531         if(center){
38532             var m = center.getMargins();
38533             var centerBox = {
38534                 x: centerX + m.left,
38535                 y: centerY + m.top,
38536                 width: centerW - (m.left+m.right),
38537                 height: centerH - (m.top+m.bottom)
38538             };
38539             //if(this.hideOnLayout){
38540                 //center.el.setStyle("display", "block");
38541             //}
38542             center.updateBox(this.safeBox(centerBox));
38543         }
38544         this.el.repaint();
38545         this.fireEvent("layout", this);
38546     },
38547
38548     // private
38549     safeBox : function(box){
38550         box.width = Math.max(0, box.width);
38551         box.height = Math.max(0, box.height);
38552         return box;
38553     },
38554
38555     /**
38556      * Adds a ContentPanel (or subclass) to this layout.
38557      * @param {String} target The target region key (north, south, east, west or center).
38558      * @param {Roo.ContentPanel} panel The panel to add
38559      * @return {Roo.ContentPanel} The added panel
38560      */
38561     add : function(target, panel){
38562          
38563         target = target.toLowerCase();
38564         return this.regions[target].add(panel);
38565     },
38566
38567     /**
38568      * Remove a ContentPanel (or subclass) to this layout.
38569      * @param {String} target The target region key (north, south, east, west or center).
38570      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38571      * @return {Roo.ContentPanel} The removed panel
38572      */
38573     remove : function(target, panel){
38574         target = target.toLowerCase();
38575         return this.regions[target].remove(panel);
38576     },
38577
38578     /**
38579      * Searches all regions for a panel with the specified id
38580      * @param {String} panelId
38581      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38582      */
38583     findPanel : function(panelId){
38584         var rs = this.regions;
38585         for(var target in rs){
38586             if(typeof rs[target] != "function"){
38587                 var p = rs[target].getPanel(panelId);
38588                 if(p){
38589                     return p;
38590                 }
38591             }
38592         }
38593         return null;
38594     },
38595
38596     /**
38597      * Searches all regions for a panel with the specified id and activates (shows) it.
38598      * @param {String/ContentPanel} panelId The panels id or the panel itself
38599      * @return {Roo.ContentPanel} The shown panel or null
38600      */
38601     showPanel : function(panelId) {
38602       var rs = this.regions;
38603       for(var target in rs){
38604          var r = rs[target];
38605          if(typeof r != "function"){
38606             if(r.hasPanel(panelId)){
38607                return r.showPanel(panelId);
38608             }
38609          }
38610       }
38611       return null;
38612    },
38613
38614    /**
38615      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38616      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38617      */
38618    /*
38619     restoreState : function(provider){
38620         if(!provider){
38621             provider = Roo.state.Manager;
38622         }
38623         var sm = new Roo.LayoutStateManager();
38624         sm.init(this, provider);
38625     },
38626 */
38627  
38628  
38629     /**
38630      * Adds a xtype elements to the layout.
38631      * <pre><code>
38632
38633 layout.addxtype({
38634        xtype : 'ContentPanel',
38635        region: 'west',
38636        items: [ .... ]
38637    }
38638 );
38639
38640 layout.addxtype({
38641         xtype : 'NestedLayoutPanel',
38642         region: 'west',
38643         layout: {
38644            center: { },
38645            west: { }   
38646         },
38647         items : [ ... list of content panels or nested layout panels.. ]
38648    }
38649 );
38650 </code></pre>
38651      * @param {Object} cfg Xtype definition of item to add.
38652      */
38653     addxtype : function(cfg)
38654     {
38655         // basically accepts a pannel...
38656         // can accept a layout region..!?!?
38657         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38658         
38659         
38660         // theory?  children can only be panels??
38661         
38662         //if (!cfg.xtype.match(/Panel$/)) {
38663         //    return false;
38664         //}
38665         var ret = false;
38666         
38667         if (typeof(cfg.region) == 'undefined') {
38668             Roo.log("Failed to add Panel, region was not set");
38669             Roo.log(cfg);
38670             return false;
38671         }
38672         var region = cfg.region;
38673         delete cfg.region;
38674         
38675           
38676         var xitems = [];
38677         if (cfg.items) {
38678             xitems = cfg.items;
38679             delete cfg.items;
38680         }
38681         var nb = false;
38682         
38683         if ( region == 'center') {
38684             Roo.log("Center: " + cfg.title);
38685         }
38686         
38687         
38688         switch(cfg.xtype) 
38689         {
38690             case 'Content':  // ContentPanel (el, cfg)
38691             case 'Scroll':  // ContentPanel (el, cfg)
38692             case 'View': 
38693                 cfg.autoCreate = cfg.autoCreate || true;
38694                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38695                 //} else {
38696                 //    var el = this.el.createChild();
38697                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38698                 //}
38699                 
38700                 this.add(region, ret);
38701                 break;
38702             
38703             /*
38704             case 'TreePanel': // our new panel!
38705                 cfg.el = this.el.createChild();
38706                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38707                 this.add(region, ret);
38708                 break;
38709             */
38710             
38711             case 'Nest': 
38712                 // create a new Layout (which is  a Border Layout...
38713                 
38714                 var clayout = cfg.layout;
38715                 clayout.el  = this.el.createChild();
38716                 clayout.items   = clayout.items  || [];
38717                 
38718                 delete cfg.layout;
38719                 
38720                 // replace this exitems with the clayout ones..
38721                 xitems = clayout.items;
38722                  
38723                 // force background off if it's in center...
38724                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38725                     cfg.background = false;
38726                 }
38727                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38728                 
38729                 
38730                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38731                 //console.log('adding nested layout panel '  + cfg.toSource());
38732                 this.add(region, ret);
38733                 nb = {}; /// find first...
38734                 break;
38735             
38736             case 'Grid':
38737                 
38738                 // needs grid and region
38739                 
38740                 //var el = this.getRegion(region).el.createChild();
38741                 /*
38742                  *var el = this.el.createChild();
38743                 // create the grid first...
38744                 cfg.grid.container = el;
38745                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38746                 */
38747                 
38748                 if (region == 'center' && this.active ) {
38749                     cfg.background = false;
38750                 }
38751                 
38752                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38753                 
38754                 this.add(region, ret);
38755                 /*
38756                 if (cfg.background) {
38757                     // render grid on panel activation (if panel background)
38758                     ret.on('activate', function(gp) {
38759                         if (!gp.grid.rendered) {
38760                     //        gp.grid.render(el);
38761                         }
38762                     });
38763                 } else {
38764                   //  cfg.grid.render(el);
38765                 }
38766                 */
38767                 break;
38768            
38769            
38770             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38771                 // it was the old xcomponent building that caused this before.
38772                 // espeically if border is the top element in the tree.
38773                 ret = this;
38774                 break; 
38775                 
38776                     
38777                 
38778                 
38779                 
38780             default:
38781                 /*
38782                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38783                     
38784                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38785                     this.add(region, ret);
38786                 } else {
38787                 */
38788                     Roo.log(cfg);
38789                     throw "Can not add '" + cfg.xtype + "' to Border";
38790                     return null;
38791              
38792                                 
38793              
38794         }
38795         this.beginUpdate();
38796         // add children..
38797         var region = '';
38798         var abn = {};
38799         Roo.each(xitems, function(i)  {
38800             region = nb && i.region ? i.region : false;
38801             
38802             var add = ret.addxtype(i);
38803            
38804             if (region) {
38805                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38806                 if (!i.background) {
38807                     abn[region] = nb[region] ;
38808                 }
38809             }
38810             
38811         });
38812         this.endUpdate();
38813
38814         // make the last non-background panel active..
38815         //if (nb) { Roo.log(abn); }
38816         if (nb) {
38817             
38818             for(var r in abn) {
38819                 region = this.getRegion(r);
38820                 if (region) {
38821                     // tried using nb[r], but it does not work..
38822                      
38823                     region.showPanel(abn[r]);
38824                    
38825                 }
38826             }
38827         }
38828         return ret;
38829         
38830     },
38831     
38832     
38833 // private
38834     factory : function(cfg)
38835     {
38836         
38837         var validRegions = Roo.bootstrap.layout.Border.regions;
38838
38839         var target = cfg.region;
38840         cfg.mgr = this;
38841         
38842         var r = Roo.bootstrap.layout;
38843         Roo.log(target);
38844         switch(target){
38845             case "north":
38846                 return new r.North(cfg);
38847             case "south":
38848                 return new r.South(cfg);
38849             case "east":
38850                 return new r.East(cfg);
38851             case "west":
38852                 return new r.West(cfg);
38853             case "center":
38854                 return new r.Center(cfg);
38855         }
38856         throw 'Layout region "'+target+'" not supported.';
38857     }
38858     
38859     
38860 });
38861  /*
38862  * Based on:
38863  * Ext JS Library 1.1.1
38864  * Copyright(c) 2006-2007, Ext JS, LLC.
38865  *
38866  * Originally Released Under LGPL - original licence link has changed is not relivant.
38867  *
38868  * Fork - LGPL
38869  * <script type="text/javascript">
38870  */
38871  
38872 /**
38873  * @class Roo.bootstrap.layout.Basic
38874  * @extends Roo.util.Observable
38875  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38876  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38877  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38878  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38879  * @cfg {string}   region  the region that it inhabits..
38880  * @cfg {bool}   skipConfig skip config?
38881  * 
38882
38883  */
38884 Roo.bootstrap.layout.Basic = function(config){
38885     
38886     this.mgr = config.mgr;
38887     
38888     this.position = config.region;
38889     
38890     var skipConfig = config.skipConfig;
38891     
38892     this.events = {
38893         /**
38894          * @scope Roo.BasicLayoutRegion
38895          */
38896         
38897         /**
38898          * @event beforeremove
38899          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38900          * @param {Roo.LayoutRegion} this
38901          * @param {Roo.ContentPanel} panel The panel
38902          * @param {Object} e The cancel event object
38903          */
38904         "beforeremove" : true,
38905         /**
38906          * @event invalidated
38907          * Fires when the layout for this region is changed.
38908          * @param {Roo.LayoutRegion} this
38909          */
38910         "invalidated" : true,
38911         /**
38912          * @event visibilitychange
38913          * Fires when this region is shown or hidden 
38914          * @param {Roo.LayoutRegion} this
38915          * @param {Boolean} visibility true or false
38916          */
38917         "visibilitychange" : true,
38918         /**
38919          * @event paneladded
38920          * Fires when a panel is added. 
38921          * @param {Roo.LayoutRegion} this
38922          * @param {Roo.ContentPanel} panel The panel
38923          */
38924         "paneladded" : true,
38925         /**
38926          * @event panelremoved
38927          * Fires when a panel is removed. 
38928          * @param {Roo.LayoutRegion} this
38929          * @param {Roo.ContentPanel} panel The panel
38930          */
38931         "panelremoved" : true,
38932         /**
38933          * @event beforecollapse
38934          * Fires when this region before collapse.
38935          * @param {Roo.LayoutRegion} this
38936          */
38937         "beforecollapse" : true,
38938         /**
38939          * @event collapsed
38940          * Fires when this region is collapsed.
38941          * @param {Roo.LayoutRegion} this
38942          */
38943         "collapsed" : true,
38944         /**
38945          * @event expanded
38946          * Fires when this region is expanded.
38947          * @param {Roo.LayoutRegion} this
38948          */
38949         "expanded" : true,
38950         /**
38951          * @event slideshow
38952          * Fires when this region is slid into view.
38953          * @param {Roo.LayoutRegion} this
38954          */
38955         "slideshow" : true,
38956         /**
38957          * @event slidehide
38958          * Fires when this region slides out of view. 
38959          * @param {Roo.LayoutRegion} this
38960          */
38961         "slidehide" : true,
38962         /**
38963          * @event panelactivated
38964          * Fires when a panel is activated. 
38965          * @param {Roo.LayoutRegion} this
38966          * @param {Roo.ContentPanel} panel The activated panel
38967          */
38968         "panelactivated" : true,
38969         /**
38970          * @event resized
38971          * Fires when the user resizes this region. 
38972          * @param {Roo.LayoutRegion} this
38973          * @param {Number} newSize The new size (width for east/west, height for north/south)
38974          */
38975         "resized" : true
38976     };
38977     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38978     this.panels = new Roo.util.MixedCollection();
38979     this.panels.getKey = this.getPanelId.createDelegate(this);
38980     this.box = null;
38981     this.activePanel = null;
38982     // ensure listeners are added...
38983     
38984     if (config.listeners || config.events) {
38985         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38986             listeners : config.listeners || {},
38987             events : config.events || {}
38988         });
38989     }
38990     
38991     if(skipConfig !== true){
38992         this.applyConfig(config);
38993     }
38994 };
38995
38996 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38997 {
38998     getPanelId : function(p){
38999         return p.getId();
39000     },
39001     
39002     applyConfig : function(config){
39003         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39004         this.config = config;
39005         
39006     },
39007     
39008     /**
39009      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39010      * the width, for horizontal (north, south) the height.
39011      * @param {Number} newSize The new width or height
39012      */
39013     resizeTo : function(newSize){
39014         var el = this.el ? this.el :
39015                  (this.activePanel ? this.activePanel.getEl() : null);
39016         if(el){
39017             switch(this.position){
39018                 case "east":
39019                 case "west":
39020                     el.setWidth(newSize);
39021                     this.fireEvent("resized", this, newSize);
39022                 break;
39023                 case "north":
39024                 case "south":
39025                     el.setHeight(newSize);
39026                     this.fireEvent("resized", this, newSize);
39027                 break;                
39028             }
39029         }
39030     },
39031     
39032     getBox : function(){
39033         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39034     },
39035     
39036     getMargins : function(){
39037         return this.margins;
39038     },
39039     
39040     updateBox : function(box){
39041         this.box = box;
39042         var el = this.activePanel.getEl();
39043         el.dom.style.left = box.x + "px";
39044         el.dom.style.top = box.y + "px";
39045         this.activePanel.setSize(box.width, box.height);
39046     },
39047     
39048     /**
39049      * Returns the container element for this region.
39050      * @return {Roo.Element}
39051      */
39052     getEl : function(){
39053         return this.activePanel;
39054     },
39055     
39056     /**
39057      * Returns true if this region is currently visible.
39058      * @return {Boolean}
39059      */
39060     isVisible : function(){
39061         return this.activePanel ? true : false;
39062     },
39063     
39064     setActivePanel : function(panel){
39065         panel = this.getPanel(panel);
39066         if(this.activePanel && this.activePanel != panel){
39067             this.activePanel.setActiveState(false);
39068             this.activePanel.getEl().setLeftTop(-10000,-10000);
39069         }
39070         this.activePanel = panel;
39071         panel.setActiveState(true);
39072         if(this.box){
39073             panel.setSize(this.box.width, this.box.height);
39074         }
39075         this.fireEvent("panelactivated", this, panel);
39076         this.fireEvent("invalidated");
39077     },
39078     
39079     /**
39080      * Show the specified panel.
39081      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39082      * @return {Roo.ContentPanel} The shown panel or null
39083      */
39084     showPanel : function(panel){
39085         panel = this.getPanel(panel);
39086         if(panel){
39087             this.setActivePanel(panel);
39088         }
39089         return panel;
39090     },
39091     
39092     /**
39093      * Get the active panel for this region.
39094      * @return {Roo.ContentPanel} The active panel or null
39095      */
39096     getActivePanel : function(){
39097         return this.activePanel;
39098     },
39099     
39100     /**
39101      * Add the passed ContentPanel(s)
39102      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39103      * @return {Roo.ContentPanel} The panel added (if only one was added)
39104      */
39105     add : function(panel){
39106         if(arguments.length > 1){
39107             for(var i = 0, len = arguments.length; i < len; i++) {
39108                 this.add(arguments[i]);
39109             }
39110             return null;
39111         }
39112         if(this.hasPanel(panel)){
39113             this.showPanel(panel);
39114             return panel;
39115         }
39116         var el = panel.getEl();
39117         if(el.dom.parentNode != this.mgr.el.dom){
39118             this.mgr.el.dom.appendChild(el.dom);
39119         }
39120         if(panel.setRegion){
39121             panel.setRegion(this);
39122         }
39123         this.panels.add(panel);
39124         el.setStyle("position", "absolute");
39125         if(!panel.background){
39126             this.setActivePanel(panel);
39127             if(this.config.initialSize && this.panels.getCount()==1){
39128                 this.resizeTo(this.config.initialSize);
39129             }
39130         }
39131         this.fireEvent("paneladded", this, panel);
39132         return panel;
39133     },
39134     
39135     /**
39136      * Returns true if the panel is in this region.
39137      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39138      * @return {Boolean}
39139      */
39140     hasPanel : function(panel){
39141         if(typeof panel == "object"){ // must be panel obj
39142             panel = panel.getId();
39143         }
39144         return this.getPanel(panel) ? true : false;
39145     },
39146     
39147     /**
39148      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39149      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39150      * @param {Boolean} preservePanel Overrides the config preservePanel option
39151      * @return {Roo.ContentPanel} The panel that was removed
39152      */
39153     remove : function(panel, preservePanel){
39154         panel = this.getPanel(panel);
39155         if(!panel){
39156             return null;
39157         }
39158         var e = {};
39159         this.fireEvent("beforeremove", this, panel, e);
39160         if(e.cancel === true){
39161             return null;
39162         }
39163         var panelId = panel.getId();
39164         this.panels.removeKey(panelId);
39165         return panel;
39166     },
39167     
39168     /**
39169      * Returns the panel specified or null if it's not in this region.
39170      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39171      * @return {Roo.ContentPanel}
39172      */
39173     getPanel : function(id){
39174         if(typeof id == "object"){ // must be panel obj
39175             return id;
39176         }
39177         return this.panels.get(id);
39178     },
39179     
39180     /**
39181      * Returns this regions position (north/south/east/west/center).
39182      * @return {String} 
39183      */
39184     getPosition: function(){
39185         return this.position;    
39186     }
39187 });/*
39188  * Based on:
39189  * Ext JS Library 1.1.1
39190  * Copyright(c) 2006-2007, Ext JS, LLC.
39191  *
39192  * Originally Released Under LGPL - original licence link has changed is not relivant.
39193  *
39194  * Fork - LGPL
39195  * <script type="text/javascript">
39196  */
39197  
39198 /**
39199  * @class Roo.bootstrap.layout.Region
39200  * @extends Roo.bootstrap.layout.Basic
39201  * This class represents a region in a layout manager.
39202  
39203  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39204  * @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})
39205  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39206  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39207  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39208  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39209  * @cfg {String}    title           The title for the region (overrides panel titles)
39210  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39211  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39212  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39213  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39214  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39215  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39216  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39217  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39218  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39219  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39220
39221  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39222  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39223  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39224  * @cfg {Number}    width           For East/West panels
39225  * @cfg {Number}    height          For North/South panels
39226  * @cfg {Boolean}   split           To show the splitter
39227  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39228  * 
39229  * @cfg {string}   cls             Extra CSS classes to add to region
39230  * 
39231  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39232  * @cfg {string}   region  the region that it inhabits..
39233  *
39234
39235  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39236  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39237
39238  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39239  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39240  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39241  */
39242 Roo.bootstrap.layout.Region = function(config)
39243 {
39244     this.applyConfig(config);
39245
39246     var mgr = config.mgr;
39247     var pos = config.region;
39248     config.skipConfig = true;
39249     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39250     
39251     if (mgr.el) {
39252         this.onRender(mgr.el);   
39253     }
39254      
39255     this.visible = true;
39256     this.collapsed = false;
39257     this.unrendered_panels = [];
39258 };
39259
39260 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39261
39262     position: '', // set by wrapper (eg. north/south etc..)
39263     unrendered_panels : null,  // unrendered panels.
39264     
39265     tabPosition : false,
39266     
39267     mgr: false, // points to 'Border'
39268     
39269     
39270     createBody : function(){
39271         /** This region's body element 
39272         * @type Roo.Element */
39273         this.bodyEl = this.el.createChild({
39274                 tag: "div",
39275                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39276         });
39277     },
39278
39279     onRender: function(ctr, pos)
39280     {
39281         var dh = Roo.DomHelper;
39282         /** This region's container element 
39283         * @type Roo.Element */
39284         this.el = dh.append(ctr.dom, {
39285                 tag: "div",
39286                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39287             }, true);
39288         /** This region's title element 
39289         * @type Roo.Element */
39290     
39291         this.titleEl = dh.append(this.el.dom,  {
39292                 tag: "div",
39293                 unselectable: "on",
39294                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39295                 children:[
39296                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39297                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39298                 ]
39299             }, true);
39300         
39301         this.titleEl.enableDisplayMode();
39302         /** This region's title text element 
39303         * @type HTMLElement */
39304         this.titleTextEl = this.titleEl.dom.firstChild;
39305         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39306         /*
39307         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39308         this.closeBtn.enableDisplayMode();
39309         this.closeBtn.on("click", this.closeClicked, this);
39310         this.closeBtn.hide();
39311     */
39312         this.createBody(this.config);
39313         if(this.config.hideWhenEmpty){
39314             this.hide();
39315             this.on("paneladded", this.validateVisibility, this);
39316             this.on("panelremoved", this.validateVisibility, this);
39317         }
39318         if(this.autoScroll){
39319             this.bodyEl.setStyle("overflow", "auto");
39320         }else{
39321             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39322         }
39323         //if(c.titlebar !== false){
39324             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39325                 this.titleEl.hide();
39326             }else{
39327                 this.titleEl.show();
39328                 if(this.config.title){
39329                     this.titleTextEl.innerHTML = this.config.title;
39330                 }
39331             }
39332         //}
39333         if(this.config.collapsed){
39334             this.collapse(true);
39335         }
39336         if(this.config.hidden){
39337             this.hide();
39338         }
39339         
39340         if (this.unrendered_panels && this.unrendered_panels.length) {
39341             for (var i =0;i< this.unrendered_panels.length; i++) {
39342                 this.add(this.unrendered_panels[i]);
39343             }
39344             this.unrendered_panels = null;
39345             
39346         }
39347         
39348     },
39349     
39350     applyConfig : function(c)
39351     {
39352         /*
39353          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39354             var dh = Roo.DomHelper;
39355             if(c.titlebar !== false){
39356                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39357                 this.collapseBtn.on("click", this.collapse, this);
39358                 this.collapseBtn.enableDisplayMode();
39359                 /*
39360                 if(c.showPin === true || this.showPin){
39361                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39362                     this.stickBtn.enableDisplayMode();
39363                     this.stickBtn.on("click", this.expand, this);
39364                     this.stickBtn.hide();
39365                 }
39366                 
39367             }
39368             */
39369             /** This region's collapsed element
39370             * @type Roo.Element */
39371             /*
39372              *
39373             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39374                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39375             ]}, true);
39376             
39377             if(c.floatable !== false){
39378                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39379                this.collapsedEl.on("click", this.collapseClick, this);
39380             }
39381
39382             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39383                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39384                    id: "message", unselectable: "on", style:{"float":"left"}});
39385                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39386              }
39387             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39388             this.expandBtn.on("click", this.expand, this);
39389             
39390         }
39391         
39392         if(this.collapseBtn){
39393             this.collapseBtn.setVisible(c.collapsible == true);
39394         }
39395         
39396         this.cmargins = c.cmargins || this.cmargins ||
39397                          (this.position == "west" || this.position == "east" ?
39398                              {top: 0, left: 2, right:2, bottom: 0} :
39399                              {top: 2, left: 0, right:0, bottom: 2});
39400         */
39401         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39402         
39403         
39404         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39405         
39406         this.autoScroll = c.autoScroll || false;
39407         
39408         
39409        
39410         
39411         this.duration = c.duration || .30;
39412         this.slideDuration = c.slideDuration || .45;
39413         this.config = c;
39414        
39415     },
39416     /**
39417      * Returns true if this region is currently visible.
39418      * @return {Boolean}
39419      */
39420     isVisible : function(){
39421         return this.visible;
39422     },
39423
39424     /**
39425      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39426      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39427      */
39428     //setCollapsedTitle : function(title){
39429     //    title = title || "&#160;";
39430      //   if(this.collapsedTitleTextEl){
39431       //      this.collapsedTitleTextEl.innerHTML = title;
39432        // }
39433     //},
39434
39435     getBox : function(){
39436         var b;
39437       //  if(!this.collapsed){
39438             b = this.el.getBox(false, true);
39439        // }else{
39440           //  b = this.collapsedEl.getBox(false, true);
39441         //}
39442         return b;
39443     },
39444
39445     getMargins : function(){
39446         return this.margins;
39447         //return this.collapsed ? this.cmargins : this.margins;
39448     },
39449 /*
39450     highlight : function(){
39451         this.el.addClass("x-layout-panel-dragover");
39452     },
39453
39454     unhighlight : function(){
39455         this.el.removeClass("x-layout-panel-dragover");
39456     },
39457 */
39458     updateBox : function(box)
39459     {
39460         if (!this.bodyEl) {
39461             return; // not rendered yet..
39462         }
39463         
39464         this.box = box;
39465         if(!this.collapsed){
39466             this.el.dom.style.left = box.x + "px";
39467             this.el.dom.style.top = box.y + "px";
39468             this.updateBody(box.width, box.height);
39469         }else{
39470             this.collapsedEl.dom.style.left = box.x + "px";
39471             this.collapsedEl.dom.style.top = box.y + "px";
39472             this.collapsedEl.setSize(box.width, box.height);
39473         }
39474         if(this.tabs){
39475             this.tabs.autoSizeTabs();
39476         }
39477     },
39478
39479     updateBody : function(w, h)
39480     {
39481         if(w !== null){
39482             this.el.setWidth(w);
39483             w -= this.el.getBorderWidth("rl");
39484             if(this.config.adjustments){
39485                 w += this.config.adjustments[0];
39486             }
39487         }
39488         if(h !== null && h > 0){
39489             this.el.setHeight(h);
39490             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39491             h -= this.el.getBorderWidth("tb");
39492             if(this.config.adjustments){
39493                 h += this.config.adjustments[1];
39494             }
39495             this.bodyEl.setHeight(h);
39496             if(this.tabs){
39497                 h = this.tabs.syncHeight(h);
39498             }
39499         }
39500         if(this.panelSize){
39501             w = w !== null ? w : this.panelSize.width;
39502             h = h !== null ? h : this.panelSize.height;
39503         }
39504         if(this.activePanel){
39505             var el = this.activePanel.getEl();
39506             w = w !== null ? w : el.getWidth();
39507             h = h !== null ? h : el.getHeight();
39508             this.panelSize = {width: w, height: h};
39509             this.activePanel.setSize(w, h);
39510         }
39511         if(Roo.isIE && this.tabs){
39512             this.tabs.el.repaint();
39513         }
39514     },
39515
39516     /**
39517      * Returns the container element for this region.
39518      * @return {Roo.Element}
39519      */
39520     getEl : function(){
39521         return this.el;
39522     },
39523
39524     /**
39525      * Hides this region.
39526      */
39527     hide : function(){
39528         //if(!this.collapsed){
39529             this.el.dom.style.left = "-2000px";
39530             this.el.hide();
39531         //}else{
39532          //   this.collapsedEl.dom.style.left = "-2000px";
39533          //   this.collapsedEl.hide();
39534        // }
39535         this.visible = false;
39536         this.fireEvent("visibilitychange", this, false);
39537     },
39538
39539     /**
39540      * Shows this region if it was previously hidden.
39541      */
39542     show : function(){
39543         //if(!this.collapsed){
39544             this.el.show();
39545         //}else{
39546         //    this.collapsedEl.show();
39547        // }
39548         this.visible = true;
39549         this.fireEvent("visibilitychange", this, true);
39550     },
39551 /*
39552     closeClicked : function(){
39553         if(this.activePanel){
39554             this.remove(this.activePanel);
39555         }
39556     },
39557
39558     collapseClick : function(e){
39559         if(this.isSlid){
39560            e.stopPropagation();
39561            this.slideIn();
39562         }else{
39563            e.stopPropagation();
39564            this.slideOut();
39565         }
39566     },
39567 */
39568     /**
39569      * Collapses this region.
39570      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39571      */
39572     /*
39573     collapse : function(skipAnim, skipCheck = false){
39574         if(this.collapsed) {
39575             return;
39576         }
39577         
39578         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39579             
39580             this.collapsed = true;
39581             if(this.split){
39582                 this.split.el.hide();
39583             }
39584             if(this.config.animate && skipAnim !== true){
39585                 this.fireEvent("invalidated", this);
39586                 this.animateCollapse();
39587             }else{
39588                 this.el.setLocation(-20000,-20000);
39589                 this.el.hide();
39590                 this.collapsedEl.show();
39591                 this.fireEvent("collapsed", this);
39592                 this.fireEvent("invalidated", this);
39593             }
39594         }
39595         
39596     },
39597 */
39598     animateCollapse : function(){
39599         // overridden
39600     },
39601
39602     /**
39603      * Expands this region if it was previously collapsed.
39604      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39605      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39606      */
39607     /*
39608     expand : function(e, skipAnim){
39609         if(e) {
39610             e.stopPropagation();
39611         }
39612         if(!this.collapsed || this.el.hasActiveFx()) {
39613             return;
39614         }
39615         if(this.isSlid){
39616             this.afterSlideIn();
39617             skipAnim = true;
39618         }
39619         this.collapsed = false;
39620         if(this.config.animate && skipAnim !== true){
39621             this.animateExpand();
39622         }else{
39623             this.el.show();
39624             if(this.split){
39625                 this.split.el.show();
39626             }
39627             this.collapsedEl.setLocation(-2000,-2000);
39628             this.collapsedEl.hide();
39629             this.fireEvent("invalidated", this);
39630             this.fireEvent("expanded", this);
39631         }
39632     },
39633 */
39634     animateExpand : function(){
39635         // overridden
39636     },
39637
39638     initTabs : function()
39639     {
39640         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39641         
39642         var ts = new Roo.bootstrap.panel.Tabs({
39643             el: this.bodyEl.dom,
39644             region : this,
39645             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39646             disableTooltips: this.config.disableTabTips,
39647             toolbar : this.config.toolbar
39648         });
39649         
39650         if(this.config.hideTabs){
39651             ts.stripWrap.setDisplayed(false);
39652         }
39653         this.tabs = ts;
39654         ts.resizeTabs = this.config.resizeTabs === true;
39655         ts.minTabWidth = this.config.minTabWidth || 40;
39656         ts.maxTabWidth = this.config.maxTabWidth || 250;
39657         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39658         ts.monitorResize = false;
39659         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39660         ts.bodyEl.addClass('roo-layout-tabs-body');
39661         this.panels.each(this.initPanelAsTab, this);
39662     },
39663
39664     initPanelAsTab : function(panel){
39665         var ti = this.tabs.addTab(
39666             panel.getEl().id,
39667             panel.getTitle(),
39668             null,
39669             this.config.closeOnTab && panel.isClosable(),
39670             panel.tpl
39671         );
39672         if(panel.tabTip !== undefined){
39673             ti.setTooltip(panel.tabTip);
39674         }
39675         ti.on("activate", function(){
39676               this.setActivePanel(panel);
39677         }, this);
39678         
39679         if(this.config.closeOnTab){
39680             ti.on("beforeclose", function(t, e){
39681                 e.cancel = true;
39682                 this.remove(panel);
39683             }, this);
39684         }
39685         
39686         panel.tabItem = ti;
39687         
39688         return ti;
39689     },
39690
39691     updatePanelTitle : function(panel, title)
39692     {
39693         if(this.activePanel == panel){
39694             this.updateTitle(title);
39695         }
39696         if(this.tabs){
39697             var ti = this.tabs.getTab(panel.getEl().id);
39698             ti.setText(title);
39699             if(panel.tabTip !== undefined){
39700                 ti.setTooltip(panel.tabTip);
39701             }
39702         }
39703     },
39704
39705     updateTitle : function(title){
39706         if(this.titleTextEl && !this.config.title){
39707             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39708         }
39709     },
39710
39711     setActivePanel : function(panel)
39712     {
39713         panel = this.getPanel(panel);
39714         if(this.activePanel && this.activePanel != panel){
39715             if(this.activePanel.setActiveState(false) === false){
39716                 return;
39717             }
39718         }
39719         this.activePanel = panel;
39720         panel.setActiveState(true);
39721         if(this.panelSize){
39722             panel.setSize(this.panelSize.width, this.panelSize.height);
39723         }
39724         if(this.closeBtn){
39725             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39726         }
39727         this.updateTitle(panel.getTitle());
39728         if(this.tabs){
39729             this.fireEvent("invalidated", this);
39730         }
39731         this.fireEvent("panelactivated", this, panel);
39732     },
39733
39734     /**
39735      * Shows the specified panel.
39736      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39737      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39738      */
39739     showPanel : function(panel)
39740     {
39741         panel = this.getPanel(panel);
39742         if(panel){
39743             if(this.tabs){
39744                 var tab = this.tabs.getTab(panel.getEl().id);
39745                 if(tab.isHidden()){
39746                     this.tabs.unhideTab(tab.id);
39747                 }
39748                 tab.activate();
39749             }else{
39750                 this.setActivePanel(panel);
39751             }
39752         }
39753         return panel;
39754     },
39755
39756     /**
39757      * Get the active panel for this region.
39758      * @return {Roo.ContentPanel} The active panel or null
39759      */
39760     getActivePanel : function(){
39761         return this.activePanel;
39762     },
39763
39764     validateVisibility : function(){
39765         if(this.panels.getCount() < 1){
39766             this.updateTitle("&#160;");
39767             this.closeBtn.hide();
39768             this.hide();
39769         }else{
39770             if(!this.isVisible()){
39771                 this.show();
39772             }
39773         }
39774     },
39775
39776     /**
39777      * Adds the passed ContentPanel(s) to this region.
39778      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39779      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39780      */
39781     add : function(panel)
39782     {
39783         if(arguments.length > 1){
39784             for(var i = 0, len = arguments.length; i < len; i++) {
39785                 this.add(arguments[i]);
39786             }
39787             return null;
39788         }
39789         
39790         // if we have not been rendered yet, then we can not really do much of this..
39791         if (!this.bodyEl) {
39792             this.unrendered_panels.push(panel);
39793             return panel;
39794         }
39795         
39796         
39797         
39798         
39799         if(this.hasPanel(panel)){
39800             this.showPanel(panel);
39801             return panel;
39802         }
39803         panel.setRegion(this);
39804         this.panels.add(panel);
39805        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39806             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39807             // and hide them... ???
39808             this.bodyEl.dom.appendChild(panel.getEl().dom);
39809             if(panel.background !== true){
39810                 this.setActivePanel(panel);
39811             }
39812             this.fireEvent("paneladded", this, panel);
39813             return panel;
39814         }
39815         */
39816         if(!this.tabs){
39817             this.initTabs();
39818         }else{
39819             this.initPanelAsTab(panel);
39820         }
39821         
39822         
39823         if(panel.background !== true){
39824             this.tabs.activate(panel.getEl().id);
39825         }
39826         this.fireEvent("paneladded", this, panel);
39827         return panel;
39828     },
39829
39830     /**
39831      * Hides the tab for the specified panel.
39832      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39833      */
39834     hidePanel : function(panel){
39835         if(this.tabs && (panel = this.getPanel(panel))){
39836             this.tabs.hideTab(panel.getEl().id);
39837         }
39838     },
39839
39840     /**
39841      * Unhides the tab for a previously hidden panel.
39842      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39843      */
39844     unhidePanel : function(panel){
39845         if(this.tabs && (panel = this.getPanel(panel))){
39846             this.tabs.unhideTab(panel.getEl().id);
39847         }
39848     },
39849
39850     clearPanels : function(){
39851         while(this.panels.getCount() > 0){
39852              this.remove(this.panels.first());
39853         }
39854     },
39855
39856     /**
39857      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39858      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39859      * @param {Boolean} preservePanel Overrides the config preservePanel option
39860      * @return {Roo.ContentPanel} The panel that was removed
39861      */
39862     remove : function(panel, preservePanel)
39863     {
39864         panel = this.getPanel(panel);
39865         if(!panel){
39866             return null;
39867         }
39868         var e = {};
39869         this.fireEvent("beforeremove", this, panel, e);
39870         if(e.cancel === true){
39871             return null;
39872         }
39873         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39874         var panelId = panel.getId();
39875         this.panels.removeKey(panelId);
39876         if(preservePanel){
39877             document.body.appendChild(panel.getEl().dom);
39878         }
39879         if(this.tabs){
39880             this.tabs.removeTab(panel.getEl().id);
39881         }else if (!preservePanel){
39882             this.bodyEl.dom.removeChild(panel.getEl().dom);
39883         }
39884         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39885             var p = this.panels.first();
39886             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39887             tempEl.appendChild(p.getEl().dom);
39888             this.bodyEl.update("");
39889             this.bodyEl.dom.appendChild(p.getEl().dom);
39890             tempEl = null;
39891             this.updateTitle(p.getTitle());
39892             this.tabs = null;
39893             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39894             this.setActivePanel(p);
39895         }
39896         panel.setRegion(null);
39897         if(this.activePanel == panel){
39898             this.activePanel = null;
39899         }
39900         if(this.config.autoDestroy !== false && preservePanel !== true){
39901             try{panel.destroy();}catch(e){}
39902         }
39903         this.fireEvent("panelremoved", this, panel);
39904         return panel;
39905     },
39906
39907     /**
39908      * Returns the TabPanel component used by this region
39909      * @return {Roo.TabPanel}
39910      */
39911     getTabs : function(){
39912         return this.tabs;
39913     },
39914
39915     createTool : function(parentEl, className){
39916         var btn = Roo.DomHelper.append(parentEl, {
39917             tag: "div",
39918             cls: "x-layout-tools-button",
39919             children: [ {
39920                 tag: "div",
39921                 cls: "roo-layout-tools-button-inner " + className,
39922                 html: "&#160;"
39923             }]
39924         }, true);
39925         btn.addClassOnOver("roo-layout-tools-button-over");
39926         return btn;
39927     }
39928 });/*
39929  * Based on:
39930  * Ext JS Library 1.1.1
39931  * Copyright(c) 2006-2007, Ext JS, LLC.
39932  *
39933  * Originally Released Under LGPL - original licence link has changed is not relivant.
39934  *
39935  * Fork - LGPL
39936  * <script type="text/javascript">
39937  */
39938  
39939
39940
39941 /**
39942  * @class Roo.SplitLayoutRegion
39943  * @extends Roo.LayoutRegion
39944  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39945  */
39946 Roo.bootstrap.layout.Split = function(config){
39947     this.cursor = config.cursor;
39948     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39949 };
39950
39951 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39952 {
39953     splitTip : "Drag to resize.",
39954     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39955     useSplitTips : false,
39956
39957     applyConfig : function(config){
39958         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39959     },
39960     
39961     onRender : function(ctr,pos) {
39962         
39963         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39964         if(!this.config.split){
39965             return;
39966         }
39967         if(!this.split){
39968             
39969             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39970                             tag: "div",
39971                             id: this.el.id + "-split",
39972                             cls: "roo-layout-split roo-layout-split-"+this.position,
39973                             html: "&#160;"
39974             });
39975             /** The SplitBar for this region 
39976             * @type Roo.SplitBar */
39977             // does not exist yet...
39978             Roo.log([this.position, this.orientation]);
39979             
39980             this.split = new Roo.bootstrap.SplitBar({
39981                 dragElement : splitEl,
39982                 resizingElement: this.el,
39983                 orientation : this.orientation
39984             });
39985             
39986             this.split.on("moved", this.onSplitMove, this);
39987             this.split.useShim = this.config.useShim === true;
39988             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39989             if(this.useSplitTips){
39990                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39991             }
39992             //if(config.collapsible){
39993             //    this.split.el.on("dblclick", this.collapse,  this);
39994             //}
39995         }
39996         if(typeof this.config.minSize != "undefined"){
39997             this.split.minSize = this.config.minSize;
39998         }
39999         if(typeof this.config.maxSize != "undefined"){
40000             this.split.maxSize = this.config.maxSize;
40001         }
40002         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
40003             this.hideSplitter();
40004         }
40005         
40006     },
40007
40008     getHMaxSize : function(){
40009          var cmax = this.config.maxSize || 10000;
40010          var center = this.mgr.getRegion("center");
40011          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40012     },
40013
40014     getVMaxSize : function(){
40015          var cmax = this.config.maxSize || 10000;
40016          var center = this.mgr.getRegion("center");
40017          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40018     },
40019
40020     onSplitMove : function(split, newSize){
40021         this.fireEvent("resized", this, newSize);
40022     },
40023     
40024     /** 
40025      * Returns the {@link Roo.SplitBar} for this region.
40026      * @return {Roo.SplitBar}
40027      */
40028     getSplitBar : function(){
40029         return this.split;
40030     },
40031     
40032     hide : function(){
40033         this.hideSplitter();
40034         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40035     },
40036
40037     hideSplitter : function(){
40038         if(this.split){
40039             this.split.el.setLocation(-2000,-2000);
40040             this.split.el.hide();
40041         }
40042     },
40043
40044     show : function(){
40045         if(this.split){
40046             this.split.el.show();
40047         }
40048         Roo.bootstrap.layout.Split.superclass.show.call(this);
40049     },
40050     
40051     beforeSlide: function(){
40052         if(Roo.isGecko){// firefox overflow auto bug workaround
40053             this.bodyEl.clip();
40054             if(this.tabs) {
40055                 this.tabs.bodyEl.clip();
40056             }
40057             if(this.activePanel){
40058                 this.activePanel.getEl().clip();
40059                 
40060                 if(this.activePanel.beforeSlide){
40061                     this.activePanel.beforeSlide();
40062                 }
40063             }
40064         }
40065     },
40066     
40067     afterSlide : function(){
40068         if(Roo.isGecko){// firefox overflow auto bug workaround
40069             this.bodyEl.unclip();
40070             if(this.tabs) {
40071                 this.tabs.bodyEl.unclip();
40072             }
40073             if(this.activePanel){
40074                 this.activePanel.getEl().unclip();
40075                 if(this.activePanel.afterSlide){
40076                     this.activePanel.afterSlide();
40077                 }
40078             }
40079         }
40080     },
40081
40082     initAutoHide : function(){
40083         if(this.autoHide !== false){
40084             if(!this.autoHideHd){
40085                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40086                 this.autoHideHd = {
40087                     "mouseout": function(e){
40088                         if(!e.within(this.el, true)){
40089                             st.delay(500);
40090                         }
40091                     },
40092                     "mouseover" : function(e){
40093                         st.cancel();
40094                     },
40095                     scope : this
40096                 };
40097             }
40098             this.el.on(this.autoHideHd);
40099         }
40100     },
40101
40102     clearAutoHide : function(){
40103         if(this.autoHide !== false){
40104             this.el.un("mouseout", this.autoHideHd.mouseout);
40105             this.el.un("mouseover", this.autoHideHd.mouseover);
40106         }
40107     },
40108
40109     clearMonitor : function(){
40110         Roo.get(document).un("click", this.slideInIf, this);
40111     },
40112
40113     // these names are backwards but not changed for compat
40114     slideOut : function(){
40115         if(this.isSlid || this.el.hasActiveFx()){
40116             return;
40117         }
40118         this.isSlid = true;
40119         if(this.collapseBtn){
40120             this.collapseBtn.hide();
40121         }
40122         this.closeBtnState = this.closeBtn.getStyle('display');
40123         this.closeBtn.hide();
40124         if(this.stickBtn){
40125             this.stickBtn.show();
40126         }
40127         this.el.show();
40128         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40129         this.beforeSlide();
40130         this.el.setStyle("z-index", 10001);
40131         this.el.slideIn(this.getSlideAnchor(), {
40132             callback: function(){
40133                 this.afterSlide();
40134                 this.initAutoHide();
40135                 Roo.get(document).on("click", this.slideInIf, this);
40136                 this.fireEvent("slideshow", this);
40137             },
40138             scope: this,
40139             block: true
40140         });
40141     },
40142
40143     afterSlideIn : function(){
40144         this.clearAutoHide();
40145         this.isSlid = false;
40146         this.clearMonitor();
40147         this.el.setStyle("z-index", "");
40148         if(this.collapseBtn){
40149             this.collapseBtn.show();
40150         }
40151         this.closeBtn.setStyle('display', this.closeBtnState);
40152         if(this.stickBtn){
40153             this.stickBtn.hide();
40154         }
40155         this.fireEvent("slidehide", this);
40156     },
40157
40158     slideIn : function(cb){
40159         if(!this.isSlid || this.el.hasActiveFx()){
40160             Roo.callback(cb);
40161             return;
40162         }
40163         this.isSlid = false;
40164         this.beforeSlide();
40165         this.el.slideOut(this.getSlideAnchor(), {
40166             callback: function(){
40167                 this.el.setLeftTop(-10000, -10000);
40168                 this.afterSlide();
40169                 this.afterSlideIn();
40170                 Roo.callback(cb);
40171             },
40172             scope: this,
40173             block: true
40174         });
40175     },
40176     
40177     slideInIf : function(e){
40178         if(!e.within(this.el)){
40179             this.slideIn();
40180         }
40181     },
40182
40183     animateCollapse : function(){
40184         this.beforeSlide();
40185         this.el.setStyle("z-index", 20000);
40186         var anchor = this.getSlideAnchor();
40187         this.el.slideOut(anchor, {
40188             callback : function(){
40189                 this.el.setStyle("z-index", "");
40190                 this.collapsedEl.slideIn(anchor, {duration:.3});
40191                 this.afterSlide();
40192                 this.el.setLocation(-10000,-10000);
40193                 this.el.hide();
40194                 this.fireEvent("collapsed", this);
40195             },
40196             scope: this,
40197             block: true
40198         });
40199     },
40200
40201     animateExpand : function(){
40202         this.beforeSlide();
40203         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40204         this.el.setStyle("z-index", 20000);
40205         this.collapsedEl.hide({
40206             duration:.1
40207         });
40208         this.el.slideIn(this.getSlideAnchor(), {
40209             callback : function(){
40210                 this.el.setStyle("z-index", "");
40211                 this.afterSlide();
40212                 if(this.split){
40213                     this.split.el.show();
40214                 }
40215                 this.fireEvent("invalidated", this);
40216                 this.fireEvent("expanded", this);
40217             },
40218             scope: this,
40219             block: true
40220         });
40221     },
40222
40223     anchors : {
40224         "west" : "left",
40225         "east" : "right",
40226         "north" : "top",
40227         "south" : "bottom"
40228     },
40229
40230     sanchors : {
40231         "west" : "l",
40232         "east" : "r",
40233         "north" : "t",
40234         "south" : "b"
40235     },
40236
40237     canchors : {
40238         "west" : "tl-tr",
40239         "east" : "tr-tl",
40240         "north" : "tl-bl",
40241         "south" : "bl-tl"
40242     },
40243
40244     getAnchor : function(){
40245         return this.anchors[this.position];
40246     },
40247
40248     getCollapseAnchor : function(){
40249         return this.canchors[this.position];
40250     },
40251
40252     getSlideAnchor : function(){
40253         return this.sanchors[this.position];
40254     },
40255
40256     getAlignAdj : function(){
40257         var cm = this.cmargins;
40258         switch(this.position){
40259             case "west":
40260                 return [0, 0];
40261             break;
40262             case "east":
40263                 return [0, 0];
40264             break;
40265             case "north":
40266                 return [0, 0];
40267             break;
40268             case "south":
40269                 return [0, 0];
40270             break;
40271         }
40272     },
40273
40274     getExpandAdj : function(){
40275         var c = this.collapsedEl, cm = this.cmargins;
40276         switch(this.position){
40277             case "west":
40278                 return [-(cm.right+c.getWidth()+cm.left), 0];
40279             break;
40280             case "east":
40281                 return [cm.right+c.getWidth()+cm.left, 0];
40282             break;
40283             case "north":
40284                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40285             break;
40286             case "south":
40287                 return [0, cm.top+cm.bottom+c.getHeight()];
40288             break;
40289         }
40290     }
40291 });/*
40292  * Based on:
40293  * Ext JS Library 1.1.1
40294  * Copyright(c) 2006-2007, Ext JS, LLC.
40295  *
40296  * Originally Released Under LGPL - original licence link has changed is not relivant.
40297  *
40298  * Fork - LGPL
40299  * <script type="text/javascript">
40300  */
40301 /*
40302  * These classes are private internal classes
40303  */
40304 Roo.bootstrap.layout.Center = function(config){
40305     config.region = "center";
40306     Roo.bootstrap.layout.Region.call(this, config);
40307     this.visible = true;
40308     this.minWidth = config.minWidth || 20;
40309     this.minHeight = config.minHeight || 20;
40310 };
40311
40312 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40313     hide : function(){
40314         // center panel can't be hidden
40315     },
40316     
40317     show : function(){
40318         // center panel can't be hidden
40319     },
40320     
40321     getMinWidth: function(){
40322         return this.minWidth;
40323     },
40324     
40325     getMinHeight: function(){
40326         return this.minHeight;
40327     }
40328 });
40329
40330
40331
40332
40333  
40334
40335
40336
40337
40338
40339
40340 Roo.bootstrap.layout.North = function(config)
40341 {
40342     config.region = 'north';
40343     config.cursor = 'n-resize';
40344     
40345     Roo.bootstrap.layout.Split.call(this, config);
40346     
40347     
40348     if(this.split){
40349         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40350         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40351         this.split.el.addClass("roo-layout-split-v");
40352     }
40353     //var size = config.initialSize || config.height;
40354     //if(this.el && typeof size != "undefined"){
40355     //    this.el.setHeight(size);
40356     //}
40357 };
40358 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40359 {
40360     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40361      
40362      
40363     onRender : function(ctr, pos)
40364     {
40365         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40366         var size = this.config.initialSize || this.config.height;
40367         if(this.el && typeof size != "undefined"){
40368             this.el.setHeight(size);
40369         }
40370     
40371     },
40372     
40373     getBox : function(){
40374         if(this.collapsed){
40375             return this.collapsedEl.getBox();
40376         }
40377         var box = this.el.getBox();
40378         if(this.split){
40379             box.height += this.split.el.getHeight();
40380         }
40381         return box;
40382     },
40383     
40384     updateBox : function(box){
40385         if(this.split && !this.collapsed){
40386             box.height -= this.split.el.getHeight();
40387             this.split.el.setLeft(box.x);
40388             this.split.el.setTop(box.y+box.height);
40389             this.split.el.setWidth(box.width);
40390         }
40391         if(this.collapsed){
40392             this.updateBody(box.width, null);
40393         }
40394         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40395     }
40396 });
40397
40398
40399
40400
40401
40402 Roo.bootstrap.layout.South = function(config){
40403     config.region = 'south';
40404     config.cursor = 's-resize';
40405     Roo.bootstrap.layout.Split.call(this, config);
40406     if(this.split){
40407         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40408         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40409         this.split.el.addClass("roo-layout-split-v");
40410     }
40411     
40412 };
40413
40414 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40415     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40416     
40417     onRender : function(ctr, pos)
40418     {
40419         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40420         var size = this.config.initialSize || this.config.height;
40421         if(this.el && typeof size != "undefined"){
40422             this.el.setHeight(size);
40423         }
40424     
40425     },
40426     
40427     getBox : function(){
40428         if(this.collapsed){
40429             return this.collapsedEl.getBox();
40430         }
40431         var box = this.el.getBox();
40432         if(this.split){
40433             var sh = this.split.el.getHeight();
40434             box.height += sh;
40435             box.y -= sh;
40436         }
40437         return box;
40438     },
40439     
40440     updateBox : function(box){
40441         if(this.split && !this.collapsed){
40442             var sh = this.split.el.getHeight();
40443             box.height -= sh;
40444             box.y += sh;
40445             this.split.el.setLeft(box.x);
40446             this.split.el.setTop(box.y-sh);
40447             this.split.el.setWidth(box.width);
40448         }
40449         if(this.collapsed){
40450             this.updateBody(box.width, null);
40451         }
40452         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40453     }
40454 });
40455
40456 Roo.bootstrap.layout.East = function(config){
40457     config.region = "east";
40458     config.cursor = "e-resize";
40459     Roo.bootstrap.layout.Split.call(this, config);
40460     if(this.split){
40461         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40462         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40463         this.split.el.addClass("roo-layout-split-h");
40464     }
40465     
40466 };
40467 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40468     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40469     
40470     onRender : function(ctr, pos)
40471     {
40472         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40473         var size = this.config.initialSize || this.config.width;
40474         if(this.el && typeof size != "undefined"){
40475             this.el.setWidth(size);
40476         }
40477     
40478     },
40479     
40480     getBox : function(){
40481         if(this.collapsed){
40482             return this.collapsedEl.getBox();
40483         }
40484         var box = this.el.getBox();
40485         if(this.split){
40486             var sw = this.split.el.getWidth();
40487             box.width += sw;
40488             box.x -= sw;
40489         }
40490         return box;
40491     },
40492
40493     updateBox : function(box){
40494         if(this.split && !this.collapsed){
40495             var sw = this.split.el.getWidth();
40496             box.width -= sw;
40497             this.split.el.setLeft(box.x);
40498             this.split.el.setTop(box.y);
40499             this.split.el.setHeight(box.height);
40500             box.x += sw;
40501         }
40502         if(this.collapsed){
40503             this.updateBody(null, box.height);
40504         }
40505         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40506     }
40507 });
40508
40509 Roo.bootstrap.layout.West = function(config){
40510     config.region = "west";
40511     config.cursor = "w-resize";
40512     
40513     Roo.bootstrap.layout.Split.call(this, config);
40514     if(this.split){
40515         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40516         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40517         this.split.el.addClass("roo-layout-split-h");
40518     }
40519     
40520 };
40521 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40522     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40523     
40524     onRender: function(ctr, pos)
40525     {
40526         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40527         var size = this.config.initialSize || this.config.width;
40528         if(typeof size != "undefined"){
40529             this.el.setWidth(size);
40530         }
40531     },
40532     
40533     getBox : function(){
40534         if(this.collapsed){
40535             return this.collapsedEl.getBox();
40536         }
40537         var box = this.el.getBox();
40538         if (box.width == 0) {
40539             box.width = this.config.width; // kludge?
40540         }
40541         if(this.split){
40542             box.width += this.split.el.getWidth();
40543         }
40544         return box;
40545     },
40546     
40547     updateBox : function(box){
40548         if(this.split && !this.collapsed){
40549             var sw = this.split.el.getWidth();
40550             box.width -= sw;
40551             this.split.el.setLeft(box.x+box.width);
40552             this.split.el.setTop(box.y);
40553             this.split.el.setHeight(box.height);
40554         }
40555         if(this.collapsed){
40556             this.updateBody(null, box.height);
40557         }
40558         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40559     }
40560 });Roo.namespace("Roo.bootstrap.panel");/*
40561  * Based on:
40562  * Ext JS Library 1.1.1
40563  * Copyright(c) 2006-2007, Ext JS, LLC.
40564  *
40565  * Originally Released Under LGPL - original licence link has changed is not relivant.
40566  *
40567  * Fork - LGPL
40568  * <script type="text/javascript">
40569  */
40570 /**
40571  * @class Roo.bootstrap.paenl.Content
40572  * @extends Roo.util.Observable
40573  * @builder-top
40574  * @children Roo.bootstrap.Component
40575  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40576  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40577  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40578  * @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
40579  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40580  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40581  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40582  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40583  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40584  * @cfg {String} title          The title for this panel
40585  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40586  * @cfg {String} url            Calls {@link #setUrl} with this value
40587  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40588  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40589  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40590  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40591  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40592  * @cfg {Boolean} badges render the badges
40593  * @cfg {String} cls  extra classes to use  
40594  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40595  
40596  * @constructor
40597  * Create a new ContentPanel.
40598  * @param {String/Object} config A string to set only the title or a config object
40599  
40600  */
40601 Roo.bootstrap.panel.Content = function( config){
40602     
40603     this.tpl = config.tpl || false;
40604     
40605     var el = config.el;
40606     var content = config.content;
40607
40608     if(config.autoCreate){ // xtype is available if this is called from factory
40609         el = Roo.id();
40610     }
40611     this.el = Roo.get(el);
40612     if(!this.el && config && config.autoCreate){
40613         if(typeof config.autoCreate == "object"){
40614             if(!config.autoCreate.id){
40615                 config.autoCreate.id = config.id||el;
40616             }
40617             this.el = Roo.DomHelper.append(document.body,
40618                         config.autoCreate, true);
40619         }else{
40620             var elcfg =  {
40621                 tag: "div",
40622                 cls: (config.cls || '') +
40623                     (config.background ? ' bg-' + config.background : '') +
40624                     " roo-layout-inactive-content",
40625                 id: config.id||el
40626             };
40627             if (config.iframe) {
40628                 elcfg.cn = [
40629                     {
40630                         tag : 'iframe',
40631                         style : 'border: 0px',
40632                         src : 'about:blank'
40633                     }
40634                 ];
40635             }
40636               
40637             if (config.html) {
40638                 elcfg.html = config.html;
40639                 
40640             }
40641                         
40642             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40643             if (config.iframe) {
40644                 this.iframeEl = this.el.select('iframe',true).first();
40645             }
40646             
40647         }
40648     } 
40649     this.closable = false;
40650     this.loaded = false;
40651     this.active = false;
40652    
40653       
40654     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40655         
40656         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40657         
40658         this.wrapEl = this.el; //this.el.wrap();
40659         var ti = [];
40660         if (config.toolbar.items) {
40661             ti = config.toolbar.items ;
40662             delete config.toolbar.items ;
40663         }
40664         
40665         var nitems = [];
40666         this.toolbar.render(this.wrapEl, 'before');
40667         for(var i =0;i < ti.length;i++) {
40668           //  Roo.log(['add child', items[i]]);
40669             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40670         }
40671         this.toolbar.items = nitems;
40672         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40673         delete config.toolbar;
40674         
40675     }
40676     /*
40677     // xtype created footer. - not sure if will work as we normally have to render first..
40678     if (this.footer && !this.footer.el && this.footer.xtype) {
40679         if (!this.wrapEl) {
40680             this.wrapEl = this.el.wrap();
40681         }
40682     
40683         this.footer.container = this.wrapEl.createChild();
40684          
40685         this.footer = Roo.factory(this.footer, Roo);
40686         
40687     }
40688     */
40689     
40690      if(typeof config == "string"){
40691         this.title = config;
40692     }else{
40693         Roo.apply(this, config);
40694     }
40695     
40696     if(this.resizeEl){
40697         this.resizeEl = Roo.get(this.resizeEl, true);
40698     }else{
40699         this.resizeEl = this.el;
40700     }
40701     // handle view.xtype
40702     
40703  
40704     
40705     
40706     this.addEvents({
40707         /**
40708          * @event activate
40709          * Fires when this panel is activated. 
40710          * @param {Roo.ContentPanel} this
40711          */
40712         "activate" : true,
40713         /**
40714          * @event deactivate
40715          * Fires when this panel is activated. 
40716          * @param {Roo.ContentPanel} this
40717          */
40718         "deactivate" : true,
40719
40720         /**
40721          * @event resize
40722          * Fires when this panel is resized if fitToFrame is true.
40723          * @param {Roo.ContentPanel} this
40724          * @param {Number} width The width after any component adjustments
40725          * @param {Number} height The height after any component adjustments
40726          */
40727         "resize" : true,
40728         
40729          /**
40730          * @event render
40731          * Fires when this tab is created
40732          * @param {Roo.ContentPanel} this
40733          */
40734         "render" : true,
40735         
40736           /**
40737          * @event scroll
40738          * Fires when this content is scrolled
40739          * @param {Roo.ContentPanel} this
40740          * @param {Event} scrollEvent
40741          */
40742         "scroll" : true
40743         
40744         
40745         
40746     });
40747     
40748
40749     
40750     
40751     if(this.autoScroll && !this.iframe){
40752         this.resizeEl.setStyle("overflow", "auto");
40753         this.resizeEl.on('scroll', this.onScroll, this);
40754     } else {
40755         // fix randome scrolling
40756         //this.el.on('scroll', function() {
40757         //    Roo.log('fix random scolling');
40758         //    this.scrollTo('top',0); 
40759         //});
40760     }
40761     content = content || this.content;
40762     if(content){
40763         this.setContent(content);
40764     }
40765     if(config && config.url){
40766         this.setUrl(this.url, this.params, this.loadOnce);
40767     }
40768     
40769     
40770     
40771     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40772     
40773     if (this.view && typeof(this.view.xtype) != 'undefined') {
40774         this.view.el = this.el.appendChild(document.createElement("div"));
40775         this.view = Roo.factory(this.view); 
40776         this.view.render  &&  this.view.render(false, '');  
40777     }
40778     
40779     
40780     this.fireEvent('render', this);
40781 };
40782
40783 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40784     
40785     cls : '',
40786     background : '',
40787     
40788     tabTip : '',
40789     
40790     iframe : false,
40791     iframeEl : false,
40792     
40793     /* Resize Element - use this to work out scroll etc. */
40794     resizeEl : false,
40795     
40796     setRegion : function(region){
40797         this.region = region;
40798         this.setActiveClass(region && !this.background);
40799     },
40800     
40801     
40802     setActiveClass: function(state)
40803     {
40804         if(state){
40805            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40806            this.el.setStyle('position','relative');
40807         }else{
40808            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40809            this.el.setStyle('position', 'absolute');
40810         } 
40811     },
40812     
40813     /**
40814      * Returns the toolbar for this Panel if one was configured. 
40815      * @return {Roo.Toolbar} 
40816      */
40817     getToolbar : function(){
40818         return this.toolbar;
40819     },
40820     
40821     setActiveState : function(active)
40822     {
40823         this.active = active;
40824         this.setActiveClass(active);
40825         if(!active){
40826             if(this.fireEvent("deactivate", this) === false){
40827                 return false;
40828             }
40829             return true;
40830         }
40831         this.fireEvent("activate", this);
40832         return true;
40833     },
40834     /**
40835      * Updates this panel's element (not for iframe)
40836      * @param {String} content The new content
40837      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40838     */
40839     setContent : function(content, loadScripts){
40840         if (this.iframe) {
40841             return;
40842         }
40843         
40844         this.el.update(content, loadScripts);
40845     },
40846
40847     ignoreResize : function(w, h){
40848         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40849             return true;
40850         }else{
40851             this.lastSize = {width: w, height: h};
40852             return false;
40853         }
40854     },
40855     /**
40856      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40857      * @return {Roo.UpdateManager} The UpdateManager
40858      */
40859     getUpdateManager : function(){
40860         if (this.iframe) {
40861             return false;
40862         }
40863         return this.el.getUpdateManager();
40864     },
40865      /**
40866      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40867      * Does not work with IFRAME contents
40868      * @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:
40869 <pre><code>
40870 panel.load({
40871     url: "your-url.php",
40872     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40873     callback: yourFunction,
40874     scope: yourObject, //(optional scope)
40875     discardUrl: false,
40876     nocache: false,
40877     text: "Loading...",
40878     timeout: 30,
40879     scripts: false
40880 });
40881 </code></pre>
40882      
40883      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40884      * 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.
40885      * @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}
40886      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40887      * @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.
40888      * @return {Roo.ContentPanel} this
40889      */
40890     load : function(){
40891         
40892         if (this.iframe) {
40893             return this;
40894         }
40895         
40896         var um = this.el.getUpdateManager();
40897         um.update.apply(um, arguments);
40898         return this;
40899     },
40900
40901
40902     /**
40903      * 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.
40904      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40905      * @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)
40906      * @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)
40907      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40908      */
40909     setUrl : function(url, params, loadOnce){
40910         if (this.iframe) {
40911             this.iframeEl.dom.src = url;
40912             return false;
40913         }
40914         
40915         if(this.refreshDelegate){
40916             this.removeListener("activate", this.refreshDelegate);
40917         }
40918         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40919         this.on("activate", this.refreshDelegate);
40920         return this.el.getUpdateManager();
40921     },
40922     
40923     _handleRefresh : function(url, params, loadOnce){
40924         if(!loadOnce || !this.loaded){
40925             var updater = this.el.getUpdateManager();
40926             updater.update(url, params, this._setLoaded.createDelegate(this));
40927         }
40928     },
40929     
40930     _setLoaded : function(){
40931         this.loaded = true;
40932     }, 
40933     
40934     /**
40935      * Returns this panel's id
40936      * @return {String} 
40937      */
40938     getId : function(){
40939         return this.el.id;
40940     },
40941     
40942     /** 
40943      * Returns this panel's element - used by regiosn to add.
40944      * @return {Roo.Element} 
40945      */
40946     getEl : function(){
40947         return this.wrapEl || this.el;
40948     },
40949     
40950    
40951     
40952     adjustForComponents : function(width, height)
40953     {
40954         //Roo.log('adjustForComponents ');
40955         if(this.resizeEl != this.el){
40956             width -= this.el.getFrameWidth('lr');
40957             height -= this.el.getFrameWidth('tb');
40958         }
40959         if(this.toolbar){
40960             var te = this.toolbar.getEl();
40961             te.setWidth(width);
40962             height -= te.getHeight();
40963         }
40964         if(this.footer){
40965             var te = this.footer.getEl();
40966             te.setWidth(width);
40967             height -= te.getHeight();
40968         }
40969         
40970         
40971         if(this.adjustments){
40972             width += this.adjustments[0];
40973             height += this.adjustments[1];
40974         }
40975         return {"width": width, "height": height};
40976     },
40977     
40978     setSize : function(width, height){
40979         if(this.fitToFrame && !this.ignoreResize(width, height)){
40980             if(this.fitContainer && this.resizeEl != this.el){
40981                 this.el.setSize(width, height);
40982             }
40983             var size = this.adjustForComponents(width, height);
40984             if (this.iframe) {
40985                 this.iframeEl.setSize(width,height);
40986             }
40987             
40988             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40989             this.fireEvent('resize', this, size.width, size.height);
40990             
40991             
40992         }
40993     },
40994     
40995     /**
40996      * Returns this panel's title
40997      * @return {String} 
40998      */
40999     getTitle : function(){
41000         
41001         if (typeof(this.title) != 'object') {
41002             return this.title;
41003         }
41004         
41005         var t = '';
41006         for (var k in this.title) {
41007             if (!this.title.hasOwnProperty(k)) {
41008                 continue;
41009             }
41010             
41011             if (k.indexOf('-') >= 0) {
41012                 var s = k.split('-');
41013                 for (var i = 0; i<s.length; i++) {
41014                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41015                 }
41016             } else {
41017                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41018             }
41019         }
41020         return t;
41021     },
41022     
41023     /**
41024      * Set this panel's title
41025      * @param {String} title
41026      */
41027     setTitle : function(title){
41028         this.title = title;
41029         if(this.region){
41030             this.region.updatePanelTitle(this, title);
41031         }
41032     },
41033     
41034     /**
41035      * Returns true is this panel was configured to be closable
41036      * @return {Boolean} 
41037      */
41038     isClosable : function(){
41039         return this.closable;
41040     },
41041     
41042     beforeSlide : function(){
41043         this.el.clip();
41044         this.resizeEl.clip();
41045     },
41046     
41047     afterSlide : function(){
41048         this.el.unclip();
41049         this.resizeEl.unclip();
41050     },
41051     
41052     /**
41053      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41054      *   Will fail silently if the {@link #setUrl} method has not been called.
41055      *   This does not activate the panel, just updates its content.
41056      */
41057     refresh : function(){
41058         if(this.refreshDelegate){
41059            this.loaded = false;
41060            this.refreshDelegate();
41061         }
41062     },
41063     
41064     /**
41065      * Destroys this panel
41066      */
41067     destroy : function(){
41068         this.el.removeAllListeners();
41069         var tempEl = document.createElement("span");
41070         tempEl.appendChild(this.el.dom);
41071         tempEl.innerHTML = "";
41072         this.el.remove();
41073         this.el = null;
41074     },
41075     
41076     /**
41077      * form - if the content panel contains a form - this is a reference to it.
41078      * @type {Roo.form.Form}
41079      */
41080     form : false,
41081     /**
41082      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41083      *    This contains a reference to it.
41084      * @type {Roo.View}
41085      */
41086     view : false,
41087     
41088       /**
41089      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41090      * <pre><code>
41091
41092 layout.addxtype({
41093        xtype : 'Form',
41094        items: [ .... ]
41095    }
41096 );
41097
41098 </code></pre>
41099      * @param {Object} cfg Xtype definition of item to add.
41100      */
41101     
41102     
41103     getChildContainer: function () {
41104         return this.getEl();
41105     },
41106     
41107     
41108     onScroll : function(e)
41109     {
41110         this.fireEvent('scroll', this, e);
41111     }
41112     
41113     
41114     /*
41115         var  ret = new Roo.factory(cfg);
41116         return ret;
41117         
41118         
41119         // add form..
41120         if (cfg.xtype.match(/^Form$/)) {
41121             
41122             var el;
41123             //if (this.footer) {
41124             //    el = this.footer.container.insertSibling(false, 'before');
41125             //} else {
41126                 el = this.el.createChild();
41127             //}
41128
41129             this.form = new  Roo.form.Form(cfg);
41130             
41131             
41132             if ( this.form.allItems.length) {
41133                 this.form.render(el.dom);
41134             }
41135             return this.form;
41136         }
41137         // should only have one of theses..
41138         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41139             // views.. should not be just added - used named prop 'view''
41140             
41141             cfg.el = this.el.appendChild(document.createElement("div"));
41142             // factory?
41143             
41144             var ret = new Roo.factory(cfg);
41145              
41146              ret.render && ret.render(false, ''); // render blank..
41147             this.view = ret;
41148             return ret;
41149         }
41150         return false;
41151     }
41152     \*/
41153 });
41154  
41155 /**
41156  * @class Roo.bootstrap.panel.Grid
41157  * @extends Roo.bootstrap.panel.Content
41158  * @constructor
41159  * Create a new GridPanel.
41160  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41161  * @param {Object} config A the config object
41162   
41163  */
41164
41165
41166
41167 Roo.bootstrap.panel.Grid = function(config)
41168 {
41169     
41170       
41171     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41172         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41173
41174     config.el = this.wrapper;
41175     //this.el = this.wrapper;
41176     
41177       if (config.container) {
41178         // ctor'ed from a Border/panel.grid
41179         
41180         
41181         this.wrapper.setStyle("overflow", "hidden");
41182         this.wrapper.addClass('roo-grid-container');
41183
41184     }
41185     
41186     
41187     if(config.toolbar){
41188         var tool_el = this.wrapper.createChild();    
41189         this.toolbar = Roo.factory(config.toolbar);
41190         var ti = [];
41191         if (config.toolbar.items) {
41192             ti = config.toolbar.items ;
41193             delete config.toolbar.items ;
41194         }
41195         
41196         var nitems = [];
41197         this.toolbar.render(tool_el);
41198         for(var i =0;i < ti.length;i++) {
41199           //  Roo.log(['add child', items[i]]);
41200             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41201         }
41202         this.toolbar.items = nitems;
41203         
41204         delete config.toolbar;
41205     }
41206     
41207     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41208     config.grid.scrollBody = true;;
41209     config.grid.monitorWindowResize = false; // turn off autosizing
41210     config.grid.autoHeight = false;
41211     config.grid.autoWidth = false;
41212     
41213     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41214     
41215     if (config.background) {
41216         // render grid on panel activation (if panel background)
41217         this.on('activate', function(gp) {
41218             if (!gp.grid.rendered) {
41219                 gp.grid.render(this.wrapper);
41220                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41221             }
41222         });
41223             
41224     } else {
41225         this.grid.render(this.wrapper);
41226         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41227
41228     }
41229     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41230     // ??? needed ??? config.el = this.wrapper;
41231     
41232     
41233     
41234   
41235     // xtype created footer. - not sure if will work as we normally have to render first..
41236     if (this.footer && !this.footer.el && this.footer.xtype) {
41237         
41238         var ctr = this.grid.getView().getFooterPanel(true);
41239         this.footer.dataSource = this.grid.dataSource;
41240         this.footer = Roo.factory(this.footer, Roo);
41241         this.footer.render(ctr);
41242         
41243     }
41244     
41245     
41246     
41247     
41248      
41249 };
41250
41251 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41252     getId : function(){
41253         return this.grid.id;
41254     },
41255     
41256     /**
41257      * Returns the grid for this panel
41258      * @return {Roo.bootstrap.Table} 
41259      */
41260     getGrid : function(){
41261         return this.grid;    
41262     },
41263     
41264     setSize : function(width, height){
41265         if(!this.ignoreResize(width, height)){
41266             var grid = this.grid;
41267             var size = this.adjustForComponents(width, height);
41268             // tfoot is not a footer?
41269           
41270             
41271             var gridel = grid.getGridEl();
41272             gridel.setSize(size.width, size.height);
41273             
41274             var tbd = grid.getGridEl().select('tbody', true).first();
41275             var thd = grid.getGridEl().select('thead',true).first();
41276             var tbf= grid.getGridEl().select('tfoot', true).first();
41277
41278             if (tbf) {
41279                 size.height -= tbf.getHeight();
41280             }
41281             if (thd) {
41282                 size.height -= thd.getHeight();
41283             }
41284             
41285             tbd.setSize(size.width, size.height );
41286             // this is for the account management tab -seems to work there.
41287             var thd = grid.getGridEl().select('thead',true).first();
41288             //if (tbd) {
41289             //    tbd.setSize(size.width, size.height - thd.getHeight());
41290             //}
41291              
41292             grid.autoSize();
41293         }
41294     },
41295      
41296     
41297     
41298     beforeSlide : function(){
41299         this.grid.getView().scroller.clip();
41300     },
41301     
41302     afterSlide : function(){
41303         this.grid.getView().scroller.unclip();
41304     },
41305     
41306     destroy : function(){
41307         this.grid.destroy();
41308         delete this.grid;
41309         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41310     }
41311 });
41312
41313 /**
41314  * @class Roo.bootstrap.panel.Nest
41315  * @extends Roo.bootstrap.panel.Content
41316  * @constructor
41317  * Create a new Panel, that can contain a layout.Border.
41318  * 
41319  * 
41320  * @param {Roo.BorderLayout} layout The layout for this panel
41321  * @param {String/Object} config A string to set only the title or a config object
41322  */
41323 Roo.bootstrap.panel.Nest = function(config)
41324 {
41325     // construct with only one argument..
41326     /* FIXME - implement nicer consturctors
41327     if (layout.layout) {
41328         config = layout;
41329         layout = config.layout;
41330         delete config.layout;
41331     }
41332     if (layout.xtype && !layout.getEl) {
41333         // then layout needs constructing..
41334         layout = Roo.factory(layout, Roo);
41335     }
41336     */
41337     
41338     config.el =  config.layout.getEl();
41339     
41340     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41341     
41342     config.layout.monitorWindowResize = false; // turn off autosizing
41343     this.layout = config.layout;
41344     this.layout.getEl().addClass("roo-layout-nested-layout");
41345     this.layout.parent = this;
41346     
41347     
41348     
41349     
41350 };
41351
41352 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41353
41354     setSize : function(width, height){
41355         if(!this.ignoreResize(width, height)){
41356             var size = this.adjustForComponents(width, height);
41357             var el = this.layout.getEl();
41358             if (size.height < 1) {
41359                 el.setWidth(size.width);   
41360             } else {
41361                 el.setSize(size.width, size.height);
41362             }
41363             var touch = el.dom.offsetWidth;
41364             this.layout.layout();
41365             // ie requires a double layout on the first pass
41366             if(Roo.isIE && !this.initialized){
41367                 this.initialized = true;
41368                 this.layout.layout();
41369             }
41370         }
41371     },
41372     
41373     // activate all subpanels if not currently active..
41374     
41375     setActiveState : function(active){
41376         this.active = active;
41377         this.setActiveClass(active);
41378         
41379         if(!active){
41380             this.fireEvent("deactivate", this);
41381             return;
41382         }
41383         
41384         this.fireEvent("activate", this);
41385         // not sure if this should happen before or after..
41386         if (!this.layout) {
41387             return; // should not happen..
41388         }
41389         var reg = false;
41390         for (var r in this.layout.regions) {
41391             reg = this.layout.getRegion(r);
41392             if (reg.getActivePanel()) {
41393                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41394                 reg.setActivePanel(reg.getActivePanel());
41395                 continue;
41396             }
41397             if (!reg.panels.length) {
41398                 continue;
41399             }
41400             reg.showPanel(reg.getPanel(0));
41401         }
41402         
41403         
41404         
41405         
41406     },
41407     
41408     /**
41409      * Returns the nested BorderLayout for this panel
41410      * @return {Roo.BorderLayout} 
41411      */
41412     getLayout : function(){
41413         return this.layout;
41414     },
41415     
41416      /**
41417      * Adds a xtype elements to the layout of the nested panel
41418      * <pre><code>
41419
41420 panel.addxtype({
41421        xtype : 'ContentPanel',
41422        region: 'west',
41423        items: [ .... ]
41424    }
41425 );
41426
41427 panel.addxtype({
41428         xtype : 'NestedLayoutPanel',
41429         region: 'west',
41430         layout: {
41431            center: { },
41432            west: { }   
41433         },
41434         items : [ ... list of content panels or nested layout panels.. ]
41435    }
41436 );
41437 </code></pre>
41438      * @param {Object} cfg Xtype definition of item to add.
41439      */
41440     addxtype : function(cfg) {
41441         return this.layout.addxtype(cfg);
41442     
41443     }
41444 });/*
41445  * Based on:
41446  * Ext JS Library 1.1.1
41447  * Copyright(c) 2006-2007, Ext JS, LLC.
41448  *
41449  * Originally Released Under LGPL - original licence link has changed is not relivant.
41450  *
41451  * Fork - LGPL
41452  * <script type="text/javascript">
41453  */
41454 /**
41455  * @class Roo.TabPanel
41456  * @extends Roo.util.Observable
41457  * A lightweight tab container.
41458  * <br><br>
41459  * Usage:
41460  * <pre><code>
41461 // basic tabs 1, built from existing content
41462 var tabs = new Roo.TabPanel("tabs1");
41463 tabs.addTab("script", "View Script");
41464 tabs.addTab("markup", "View Markup");
41465 tabs.activate("script");
41466
41467 // more advanced tabs, built from javascript
41468 var jtabs = new Roo.TabPanel("jtabs");
41469 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41470
41471 // set up the UpdateManager
41472 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41473 var updater = tab2.getUpdateManager();
41474 updater.setDefaultUrl("ajax1.htm");
41475 tab2.on('activate', updater.refresh, updater, true);
41476
41477 // Use setUrl for Ajax loading
41478 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41479 tab3.setUrl("ajax2.htm", null, true);
41480
41481 // Disabled tab
41482 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41483 tab4.disable();
41484
41485 jtabs.activate("jtabs-1");
41486  * </code></pre>
41487  * @constructor
41488  * Create a new TabPanel.
41489  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41490  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41491  */
41492 Roo.bootstrap.panel.Tabs = function(config){
41493     /**
41494     * The container element for this TabPanel.
41495     * @type Roo.Element
41496     */
41497     this.el = Roo.get(config.el);
41498     delete config.el;
41499     if(config){
41500         if(typeof config == "boolean"){
41501             this.tabPosition = config ? "bottom" : "top";
41502         }else{
41503             Roo.apply(this, config);
41504         }
41505     }
41506     
41507     if(this.tabPosition == "bottom"){
41508         // if tabs are at the bottom = create the body first.
41509         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41510         this.el.addClass("roo-tabs-bottom");
41511     }
41512     // next create the tabs holders
41513     
41514     if (this.tabPosition == "west"){
41515         
41516         var reg = this.region; // fake it..
41517         while (reg) {
41518             if (!reg.mgr.parent) {
41519                 break;
41520             }
41521             reg = reg.mgr.parent.region;
41522         }
41523         Roo.log("got nest?");
41524         Roo.log(reg);
41525         if (reg.mgr.getRegion('west')) {
41526             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41527             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41528             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41529             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41530             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41531         
41532             
41533         }
41534         
41535         
41536     } else {
41537      
41538         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41539         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41540         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41541         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41542     }
41543     
41544     
41545     if(Roo.isIE){
41546         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41547     }
41548     
41549     // finally - if tabs are at the top, then create the body last..
41550     if(this.tabPosition != "bottom"){
41551         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41552          * @type Roo.Element
41553          */
41554         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41555         this.el.addClass("roo-tabs-top");
41556     }
41557     this.items = [];
41558
41559     this.bodyEl.setStyle("position", "relative");
41560
41561     this.active = null;
41562     this.activateDelegate = this.activate.createDelegate(this);
41563
41564     this.addEvents({
41565         /**
41566          * @event tabchange
41567          * Fires when the active tab changes
41568          * @param {Roo.TabPanel} this
41569          * @param {Roo.TabPanelItem} activePanel The new active tab
41570          */
41571         "tabchange": true,
41572         /**
41573          * @event beforetabchange
41574          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41575          * @param {Roo.TabPanel} this
41576          * @param {Object} e Set cancel to true on this object to cancel the tab change
41577          * @param {Roo.TabPanelItem} tab The tab being changed to
41578          */
41579         "beforetabchange" : true
41580     });
41581
41582     Roo.EventManager.onWindowResize(this.onResize, this);
41583     this.cpad = this.el.getPadding("lr");
41584     this.hiddenCount = 0;
41585
41586
41587     // toolbar on the tabbar support...
41588     if (this.toolbar) {
41589         alert("no toolbar support yet");
41590         this.toolbar  = false;
41591         /*
41592         var tcfg = this.toolbar;
41593         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41594         this.toolbar = new Roo.Toolbar(tcfg);
41595         if (Roo.isSafari) {
41596             var tbl = tcfg.container.child('table', true);
41597             tbl.setAttribute('width', '100%');
41598         }
41599         */
41600         
41601     }
41602    
41603
41604
41605     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41606 };
41607
41608 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41609     /*
41610      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41611      */
41612     tabPosition : "top",
41613     /*
41614      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41615      */
41616     currentTabWidth : 0,
41617     /*
41618      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41619      */
41620     minTabWidth : 40,
41621     /*
41622      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41623      */
41624     maxTabWidth : 250,
41625     /*
41626      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41627      */
41628     preferredTabWidth : 175,
41629     /*
41630      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41631      */
41632     resizeTabs : false,
41633     /*
41634      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41635      */
41636     monitorResize : true,
41637     /*
41638      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41639      */
41640     toolbar : false,  // set by caller..
41641     
41642     region : false, /// set by caller
41643     
41644     disableTooltips : true, // not used yet...
41645
41646     /**
41647      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41648      * @param {String} id The id of the div to use <b>or create</b>
41649      * @param {String} text The text for the tab
41650      * @param {String} content (optional) Content to put in the TabPanelItem body
41651      * @param {Boolean} closable (optional) True to create a close icon on the tab
41652      * @return {Roo.TabPanelItem} The created TabPanelItem
41653      */
41654     addTab : function(id, text, content, closable, tpl)
41655     {
41656         var item = new Roo.bootstrap.panel.TabItem({
41657             panel: this,
41658             id : id,
41659             text : text,
41660             closable : closable,
41661             tpl : tpl
41662         });
41663         this.addTabItem(item);
41664         if(content){
41665             item.setContent(content);
41666         }
41667         return item;
41668     },
41669
41670     /**
41671      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41672      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41673      * @return {Roo.TabPanelItem}
41674      */
41675     getTab : function(id){
41676         return this.items[id];
41677     },
41678
41679     /**
41680      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41681      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41682      */
41683     hideTab : function(id){
41684         var t = this.items[id];
41685         if(!t.isHidden()){
41686            t.setHidden(true);
41687            this.hiddenCount++;
41688            this.autoSizeTabs();
41689         }
41690     },
41691
41692     /**
41693      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41694      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41695      */
41696     unhideTab : function(id){
41697         var t = this.items[id];
41698         if(t.isHidden()){
41699            t.setHidden(false);
41700            this.hiddenCount--;
41701            this.autoSizeTabs();
41702         }
41703     },
41704
41705     /**
41706      * Adds an existing {@link Roo.TabPanelItem}.
41707      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41708      */
41709     addTabItem : function(item)
41710     {
41711         this.items[item.id] = item;
41712         this.items.push(item);
41713         this.autoSizeTabs();
41714       //  if(this.resizeTabs){
41715     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41716   //         this.autoSizeTabs();
41717 //        }else{
41718 //            item.autoSize();
41719        // }
41720     },
41721
41722     /**
41723      * Removes a {@link Roo.TabPanelItem}.
41724      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41725      */
41726     removeTab : function(id){
41727         var items = this.items;
41728         var tab = items[id];
41729         if(!tab) { return; }
41730         var index = items.indexOf(tab);
41731         if(this.active == tab && items.length > 1){
41732             var newTab = this.getNextAvailable(index);
41733             if(newTab) {
41734                 newTab.activate();
41735             }
41736         }
41737         this.stripEl.dom.removeChild(tab.pnode.dom);
41738         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41739             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41740         }
41741         items.splice(index, 1);
41742         delete this.items[tab.id];
41743         tab.fireEvent("close", tab);
41744         tab.purgeListeners();
41745         this.autoSizeTabs();
41746     },
41747
41748     getNextAvailable : function(start){
41749         var items = this.items;
41750         var index = start;
41751         // look for a next tab that will slide over to
41752         // replace the one being removed
41753         while(index < items.length){
41754             var item = items[++index];
41755             if(item && !item.isHidden()){
41756                 return item;
41757             }
41758         }
41759         // if one isn't found select the previous tab (on the left)
41760         index = start;
41761         while(index >= 0){
41762             var item = items[--index];
41763             if(item && !item.isHidden()){
41764                 return item;
41765             }
41766         }
41767         return null;
41768     },
41769
41770     /**
41771      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41772      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41773      */
41774     disableTab : function(id){
41775         var tab = this.items[id];
41776         if(tab && this.active != tab){
41777             tab.disable();
41778         }
41779     },
41780
41781     /**
41782      * Enables a {@link Roo.TabPanelItem} that is disabled.
41783      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41784      */
41785     enableTab : function(id){
41786         var tab = this.items[id];
41787         tab.enable();
41788     },
41789
41790     /**
41791      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41792      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41793      * @return {Roo.TabPanelItem} The TabPanelItem.
41794      */
41795     activate : function(id)
41796     {
41797         //Roo.log('activite:'  + id);
41798         
41799         var tab = this.items[id];
41800         if(!tab){
41801             return null;
41802         }
41803         if(tab == this.active || tab.disabled){
41804             return tab;
41805         }
41806         var e = {};
41807         this.fireEvent("beforetabchange", this, e, tab);
41808         if(e.cancel !== true && !tab.disabled){
41809             if(this.active){
41810                 this.active.hide();
41811             }
41812             this.active = this.items[id];
41813             this.active.show();
41814             this.fireEvent("tabchange", this, this.active);
41815         }
41816         return tab;
41817     },
41818
41819     /**
41820      * Gets the active {@link Roo.TabPanelItem}.
41821      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41822      */
41823     getActiveTab : function(){
41824         return this.active;
41825     },
41826
41827     /**
41828      * Updates the tab body element to fit the height of the container element
41829      * for overflow scrolling
41830      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41831      */
41832     syncHeight : function(targetHeight){
41833         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41834         var bm = this.bodyEl.getMargins();
41835         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41836         this.bodyEl.setHeight(newHeight);
41837         return newHeight;
41838     },
41839
41840     onResize : function(){
41841         if(this.monitorResize){
41842             this.autoSizeTabs();
41843         }
41844     },
41845
41846     /**
41847      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41848      */
41849     beginUpdate : function(){
41850         this.updating = true;
41851     },
41852
41853     /**
41854      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41855      */
41856     endUpdate : function(){
41857         this.updating = false;
41858         this.autoSizeTabs();
41859     },
41860
41861     /**
41862      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41863      */
41864     autoSizeTabs : function()
41865     {
41866         var count = this.items.length;
41867         var vcount = count - this.hiddenCount;
41868         
41869         if (vcount < 2) {
41870             this.stripEl.hide();
41871         } else {
41872             this.stripEl.show();
41873         }
41874         
41875         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41876             return;
41877         }
41878         
41879         
41880         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41881         var availWidth = Math.floor(w / vcount);
41882         var b = this.stripBody;
41883         if(b.getWidth() > w){
41884             var tabs = this.items;
41885             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41886             if(availWidth < this.minTabWidth){
41887                 /*if(!this.sleft){    // incomplete scrolling code
41888                     this.createScrollButtons();
41889                 }
41890                 this.showScroll();
41891                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41892             }
41893         }else{
41894             if(this.currentTabWidth < this.preferredTabWidth){
41895                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41896             }
41897         }
41898     },
41899
41900     /**
41901      * Returns the number of tabs in this TabPanel.
41902      * @return {Number}
41903      */
41904      getCount : function(){
41905          return this.items.length;
41906      },
41907
41908     /**
41909      * Resizes all the tabs to the passed width
41910      * @param {Number} The new width
41911      */
41912     setTabWidth : function(width){
41913         this.currentTabWidth = width;
41914         for(var i = 0, len = this.items.length; i < len; i++) {
41915                 if(!this.items[i].isHidden()) {
41916                 this.items[i].setWidth(width);
41917             }
41918         }
41919     },
41920
41921     /**
41922      * Destroys this TabPanel
41923      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41924      */
41925     destroy : function(removeEl){
41926         Roo.EventManager.removeResizeListener(this.onResize, this);
41927         for(var i = 0, len = this.items.length; i < len; i++){
41928             this.items[i].purgeListeners();
41929         }
41930         if(removeEl === true){
41931             this.el.update("");
41932             this.el.remove();
41933         }
41934     },
41935     
41936     createStrip : function(container)
41937     {
41938         var strip = document.createElement("nav");
41939         strip.className = Roo.bootstrap.version == 4 ?
41940             "navbar-light bg-light" : 
41941             "navbar navbar-default"; //"x-tabs-wrap";
41942         container.appendChild(strip);
41943         return strip;
41944     },
41945     
41946     createStripList : function(strip)
41947     {
41948         // div wrapper for retard IE
41949         // returns the "tr" element.
41950         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41951         //'<div class="x-tabs-strip-wrap">'+
41952           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41953           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41954         return strip.firstChild; //.firstChild.firstChild.firstChild;
41955     },
41956     createBody : function(container)
41957     {
41958         var body = document.createElement("div");
41959         Roo.id(body, "tab-body");
41960         //Roo.fly(body).addClass("x-tabs-body");
41961         Roo.fly(body).addClass("tab-content");
41962         container.appendChild(body);
41963         return body;
41964     },
41965     createItemBody :function(bodyEl, id){
41966         var body = Roo.getDom(id);
41967         if(!body){
41968             body = document.createElement("div");
41969             body.id = id;
41970         }
41971         //Roo.fly(body).addClass("x-tabs-item-body");
41972         Roo.fly(body).addClass("tab-pane");
41973          bodyEl.insertBefore(body, bodyEl.firstChild);
41974         return body;
41975     },
41976     /** @private */
41977     createStripElements :  function(stripEl, text, closable, tpl)
41978     {
41979         var td = document.createElement("li"); // was td..
41980         td.className = 'nav-item';
41981         
41982         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41983         
41984         
41985         stripEl.appendChild(td);
41986         /*if(closable){
41987             td.className = "x-tabs-closable";
41988             if(!this.closeTpl){
41989                 this.closeTpl = new Roo.Template(
41990                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41991                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41992                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41993                 );
41994             }
41995             var el = this.closeTpl.overwrite(td, {"text": text});
41996             var close = el.getElementsByTagName("div")[0];
41997             var inner = el.getElementsByTagName("em")[0];
41998             return {"el": el, "close": close, "inner": inner};
41999         } else {
42000         */
42001         // not sure what this is..
42002 //            if(!this.tabTpl){
42003                 //this.tabTpl = new Roo.Template(
42004                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42005                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42006                 //);
42007 //                this.tabTpl = new Roo.Template(
42008 //                   '<a href="#">' +
42009 //                   '<span unselectable="on"' +
42010 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42011 //                            ' >{text}</span></a>'
42012 //                );
42013 //                
42014 //            }
42015
42016
42017             var template = tpl || this.tabTpl || false;
42018             
42019             if(!template){
42020                 template =  new Roo.Template(
42021                         Roo.bootstrap.version == 4 ? 
42022                             (
42023                                 '<a class="nav-link" href="#" unselectable="on"' +
42024                                      (this.disableTooltips ? '' : ' title="{text}"') +
42025                                      ' >{text}</a>'
42026                             ) : (
42027                                 '<a class="nav-link" href="#">' +
42028                                 '<span unselectable="on"' +
42029                                          (this.disableTooltips ? '' : ' title="{text}"') +
42030                                     ' >{text}</span></a>'
42031                             )
42032                 );
42033             }
42034             
42035             switch (typeof(template)) {
42036                 case 'object' :
42037                     break;
42038                 case 'string' :
42039                     template = new Roo.Template(template);
42040                     break;
42041                 default :
42042                     break;
42043             }
42044             
42045             var el = template.overwrite(td, {"text": text});
42046             
42047             var inner = el.getElementsByTagName("span")[0];
42048             
42049             return {"el": el, "inner": inner};
42050             
42051     }
42052         
42053     
42054 });
42055
42056 /**
42057  * @class Roo.TabPanelItem
42058  * @extends Roo.util.Observable
42059  * Represents an individual item (tab plus body) in a TabPanel.
42060  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42061  * @param {String} id The id of this TabPanelItem
42062  * @param {String} text The text for the tab of this TabPanelItem
42063  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42064  */
42065 Roo.bootstrap.panel.TabItem = function(config){
42066     /**
42067      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42068      * @type Roo.TabPanel
42069      */
42070     this.tabPanel = config.panel;
42071     /**
42072      * The id for this TabPanelItem
42073      * @type String
42074      */
42075     this.id = config.id;
42076     /** @private */
42077     this.disabled = false;
42078     /** @private */
42079     this.text = config.text;
42080     /** @private */
42081     this.loaded = false;
42082     this.closable = config.closable;
42083
42084     /**
42085      * The body element for this TabPanelItem.
42086      * @type Roo.Element
42087      */
42088     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42089     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42090     this.bodyEl.setStyle("display", "block");
42091     this.bodyEl.setStyle("zoom", "1");
42092     //this.hideAction();
42093
42094     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42095     /** @private */
42096     this.el = Roo.get(els.el);
42097     this.inner = Roo.get(els.inner, true);
42098      this.textEl = Roo.bootstrap.version == 4 ?
42099         this.el : Roo.get(this.el.dom.firstChild, true);
42100
42101     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42102     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42103
42104     
42105 //    this.el.on("mousedown", this.onTabMouseDown, this);
42106     this.el.on("click", this.onTabClick, this);
42107     /** @private */
42108     if(config.closable){
42109         var c = Roo.get(els.close, true);
42110         c.dom.title = this.closeText;
42111         c.addClassOnOver("close-over");
42112         c.on("click", this.closeClick, this);
42113      }
42114
42115     this.addEvents({
42116          /**
42117          * @event activate
42118          * Fires when this tab becomes the active tab.
42119          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42120          * @param {Roo.TabPanelItem} this
42121          */
42122         "activate": true,
42123         /**
42124          * @event beforeclose
42125          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42126          * @param {Roo.TabPanelItem} this
42127          * @param {Object} e Set cancel to true on this object to cancel the close.
42128          */
42129         "beforeclose": true,
42130         /**
42131          * @event close
42132          * Fires when this tab is closed.
42133          * @param {Roo.TabPanelItem} this
42134          */
42135          "close": true,
42136         /**
42137          * @event deactivate
42138          * Fires when this tab is no longer the active tab.
42139          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42140          * @param {Roo.TabPanelItem} this
42141          */
42142          "deactivate" : true
42143     });
42144     this.hidden = false;
42145
42146     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42147 };
42148
42149 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42150            {
42151     purgeListeners : function(){
42152        Roo.util.Observable.prototype.purgeListeners.call(this);
42153        this.el.removeAllListeners();
42154     },
42155     /**
42156      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42157      */
42158     show : function(){
42159         this.status_node.addClass("active");
42160         this.showAction();
42161         if(Roo.isOpera){
42162             this.tabPanel.stripWrap.repaint();
42163         }
42164         this.fireEvent("activate", this.tabPanel, this);
42165     },
42166
42167     /**
42168      * Returns true if this tab is the active tab.
42169      * @return {Boolean}
42170      */
42171     isActive : function(){
42172         return this.tabPanel.getActiveTab() == this;
42173     },
42174
42175     /**
42176      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42177      */
42178     hide : function(){
42179         this.status_node.removeClass("active");
42180         this.hideAction();
42181         this.fireEvent("deactivate", this.tabPanel, this);
42182     },
42183
42184     hideAction : function(){
42185         this.bodyEl.hide();
42186         this.bodyEl.setStyle("position", "absolute");
42187         this.bodyEl.setLeft("-20000px");
42188         this.bodyEl.setTop("-20000px");
42189     },
42190
42191     showAction : function(){
42192         this.bodyEl.setStyle("position", "relative");
42193         this.bodyEl.setTop("");
42194         this.bodyEl.setLeft("");
42195         this.bodyEl.show();
42196     },
42197
42198     /**
42199      * Set the tooltip for the tab.
42200      * @param {String} tooltip The tab's tooltip
42201      */
42202     setTooltip : function(text){
42203         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42204             this.textEl.dom.qtip = text;
42205             this.textEl.dom.removeAttribute('title');
42206         }else{
42207             this.textEl.dom.title = text;
42208         }
42209     },
42210
42211     onTabClick : function(e){
42212         e.preventDefault();
42213         this.tabPanel.activate(this.id);
42214     },
42215
42216     onTabMouseDown : function(e){
42217         e.preventDefault();
42218         this.tabPanel.activate(this.id);
42219     },
42220 /*
42221     getWidth : function(){
42222         return this.inner.getWidth();
42223     },
42224
42225     setWidth : function(width){
42226         var iwidth = width - this.linode.getPadding("lr");
42227         this.inner.setWidth(iwidth);
42228         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42229         this.linode.setWidth(width);
42230     },
42231 */
42232     /**
42233      * Show or hide the tab
42234      * @param {Boolean} hidden True to hide or false to show.
42235      */
42236     setHidden : function(hidden){
42237         this.hidden = hidden;
42238         this.linode.setStyle("display", hidden ? "none" : "");
42239     },
42240
42241     /**
42242      * Returns true if this tab is "hidden"
42243      * @return {Boolean}
42244      */
42245     isHidden : function(){
42246         return this.hidden;
42247     },
42248
42249     /**
42250      * Returns the text for this tab
42251      * @return {String}
42252      */
42253     getText : function(){
42254         return this.text;
42255     },
42256     /*
42257     autoSize : function(){
42258         //this.el.beginMeasure();
42259         this.textEl.setWidth(1);
42260         /*
42261          *  #2804 [new] Tabs in Roojs
42262          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42263          */
42264         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42265         //this.el.endMeasure();
42266     //},
42267
42268     /**
42269      * Sets the text for the tab (Note: this also sets the tooltip text)
42270      * @param {String} text The tab's text and tooltip
42271      */
42272     setText : function(text){
42273         this.text = text;
42274         this.textEl.update(text);
42275         this.setTooltip(text);
42276         //if(!this.tabPanel.resizeTabs){
42277         //    this.autoSize();
42278         //}
42279     },
42280     /**
42281      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42282      */
42283     activate : function(){
42284         this.tabPanel.activate(this.id);
42285     },
42286
42287     /**
42288      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42289      */
42290     disable : function(){
42291         if(this.tabPanel.active != this){
42292             this.disabled = true;
42293             this.status_node.addClass("disabled");
42294         }
42295     },
42296
42297     /**
42298      * Enables this TabPanelItem if it was previously disabled.
42299      */
42300     enable : function(){
42301         this.disabled = false;
42302         this.status_node.removeClass("disabled");
42303     },
42304
42305     /**
42306      * Sets the content for this TabPanelItem.
42307      * @param {String} content The content
42308      * @param {Boolean} loadScripts true to look for and load scripts
42309      */
42310     setContent : function(content, loadScripts){
42311         this.bodyEl.update(content, loadScripts);
42312     },
42313
42314     /**
42315      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42316      * @return {Roo.UpdateManager} The UpdateManager
42317      */
42318     getUpdateManager : function(){
42319         return this.bodyEl.getUpdateManager();
42320     },
42321
42322     /**
42323      * Set a URL to be used to load the content for this TabPanelItem.
42324      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42325      * @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)
42326      * @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)
42327      * @return {Roo.UpdateManager} The UpdateManager
42328      */
42329     setUrl : function(url, params, loadOnce){
42330         if(this.refreshDelegate){
42331             this.un('activate', this.refreshDelegate);
42332         }
42333         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42334         this.on("activate", this.refreshDelegate);
42335         return this.bodyEl.getUpdateManager();
42336     },
42337
42338     /** @private */
42339     _handleRefresh : function(url, params, loadOnce){
42340         if(!loadOnce || !this.loaded){
42341             var updater = this.bodyEl.getUpdateManager();
42342             updater.update(url, params, this._setLoaded.createDelegate(this));
42343         }
42344     },
42345
42346     /**
42347      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42348      *   Will fail silently if the setUrl method has not been called.
42349      *   This does not activate the panel, just updates its content.
42350      */
42351     refresh : function(){
42352         if(this.refreshDelegate){
42353            this.loaded = false;
42354            this.refreshDelegate();
42355         }
42356     },
42357
42358     /** @private */
42359     _setLoaded : function(){
42360         this.loaded = true;
42361     },
42362
42363     /** @private */
42364     closeClick : function(e){
42365         var o = {};
42366         e.stopEvent();
42367         this.fireEvent("beforeclose", this, o);
42368         if(o.cancel !== true){
42369             this.tabPanel.removeTab(this.id);
42370         }
42371     },
42372     /**
42373      * The text displayed in the tooltip for the close icon.
42374      * @type String
42375      */
42376     closeText : "Close this tab"
42377 });
42378 /**
42379 *    This script refer to:
42380 *    Title: International Telephone Input
42381 *    Author: Jack O'Connor
42382 *    Code version:  v12.1.12
42383 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42384 **/
42385
42386 Roo.bootstrap.PhoneInputData = function() {
42387     var d = [
42388       [
42389         "Afghanistan (‫افغانستان‬‎)",
42390         "af",
42391         "93"
42392       ],
42393       [
42394         "Albania (Shqipëri)",
42395         "al",
42396         "355"
42397       ],
42398       [
42399         "Algeria (‫الجزائر‬‎)",
42400         "dz",
42401         "213"
42402       ],
42403       [
42404         "American Samoa",
42405         "as",
42406         "1684"
42407       ],
42408       [
42409         "Andorra",
42410         "ad",
42411         "376"
42412       ],
42413       [
42414         "Angola",
42415         "ao",
42416         "244"
42417       ],
42418       [
42419         "Anguilla",
42420         "ai",
42421         "1264"
42422       ],
42423       [
42424         "Antigua and Barbuda",
42425         "ag",
42426         "1268"
42427       ],
42428       [
42429         "Argentina",
42430         "ar",
42431         "54"
42432       ],
42433       [
42434         "Armenia (Հայաստան)",
42435         "am",
42436         "374"
42437       ],
42438       [
42439         "Aruba",
42440         "aw",
42441         "297"
42442       ],
42443       [
42444         "Australia",
42445         "au",
42446         "61",
42447         0
42448       ],
42449       [
42450         "Austria (Österreich)",
42451         "at",
42452         "43"
42453       ],
42454       [
42455         "Azerbaijan (Azərbaycan)",
42456         "az",
42457         "994"
42458       ],
42459       [
42460         "Bahamas",
42461         "bs",
42462         "1242"
42463       ],
42464       [
42465         "Bahrain (‫البحرين‬‎)",
42466         "bh",
42467         "973"
42468       ],
42469       [
42470         "Bangladesh (বাংলাদেশ)",
42471         "bd",
42472         "880"
42473       ],
42474       [
42475         "Barbados",
42476         "bb",
42477         "1246"
42478       ],
42479       [
42480         "Belarus (Беларусь)",
42481         "by",
42482         "375"
42483       ],
42484       [
42485         "Belgium (België)",
42486         "be",
42487         "32"
42488       ],
42489       [
42490         "Belize",
42491         "bz",
42492         "501"
42493       ],
42494       [
42495         "Benin (Bénin)",
42496         "bj",
42497         "229"
42498       ],
42499       [
42500         "Bermuda",
42501         "bm",
42502         "1441"
42503       ],
42504       [
42505         "Bhutan (འབྲུག)",
42506         "bt",
42507         "975"
42508       ],
42509       [
42510         "Bolivia",
42511         "bo",
42512         "591"
42513       ],
42514       [
42515         "Bosnia and Herzegovina (Босна и Херцеговина)",
42516         "ba",
42517         "387"
42518       ],
42519       [
42520         "Botswana",
42521         "bw",
42522         "267"
42523       ],
42524       [
42525         "Brazil (Brasil)",
42526         "br",
42527         "55"
42528       ],
42529       [
42530         "British Indian Ocean Territory",
42531         "io",
42532         "246"
42533       ],
42534       [
42535         "British Virgin Islands",
42536         "vg",
42537         "1284"
42538       ],
42539       [
42540         "Brunei",
42541         "bn",
42542         "673"
42543       ],
42544       [
42545         "Bulgaria (България)",
42546         "bg",
42547         "359"
42548       ],
42549       [
42550         "Burkina Faso",
42551         "bf",
42552         "226"
42553       ],
42554       [
42555         "Burundi (Uburundi)",
42556         "bi",
42557         "257"
42558       ],
42559       [
42560         "Cambodia (កម្ពុជា)",
42561         "kh",
42562         "855"
42563       ],
42564       [
42565         "Cameroon (Cameroun)",
42566         "cm",
42567         "237"
42568       ],
42569       [
42570         "Canada",
42571         "ca",
42572         "1",
42573         1,
42574         ["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"]
42575       ],
42576       [
42577         "Cape Verde (Kabu Verdi)",
42578         "cv",
42579         "238"
42580       ],
42581       [
42582         "Caribbean Netherlands",
42583         "bq",
42584         "599",
42585         1
42586       ],
42587       [
42588         "Cayman Islands",
42589         "ky",
42590         "1345"
42591       ],
42592       [
42593         "Central African Republic (République centrafricaine)",
42594         "cf",
42595         "236"
42596       ],
42597       [
42598         "Chad (Tchad)",
42599         "td",
42600         "235"
42601       ],
42602       [
42603         "Chile",
42604         "cl",
42605         "56"
42606       ],
42607       [
42608         "China (中国)",
42609         "cn",
42610         "86"
42611       ],
42612       [
42613         "Christmas Island",
42614         "cx",
42615         "61",
42616         2
42617       ],
42618       [
42619         "Cocos (Keeling) Islands",
42620         "cc",
42621         "61",
42622         1
42623       ],
42624       [
42625         "Colombia",
42626         "co",
42627         "57"
42628       ],
42629       [
42630         "Comoros (‫جزر القمر‬‎)",
42631         "km",
42632         "269"
42633       ],
42634       [
42635         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42636         "cd",
42637         "243"
42638       ],
42639       [
42640         "Congo (Republic) (Congo-Brazzaville)",
42641         "cg",
42642         "242"
42643       ],
42644       [
42645         "Cook Islands",
42646         "ck",
42647         "682"
42648       ],
42649       [
42650         "Costa Rica",
42651         "cr",
42652         "506"
42653       ],
42654       [
42655         "Côte d’Ivoire",
42656         "ci",
42657         "225"
42658       ],
42659       [
42660         "Croatia (Hrvatska)",
42661         "hr",
42662         "385"
42663       ],
42664       [
42665         "Cuba",
42666         "cu",
42667         "53"
42668       ],
42669       [
42670         "Curaçao",
42671         "cw",
42672         "599",
42673         0
42674       ],
42675       [
42676         "Cyprus (Κύπρος)",
42677         "cy",
42678         "357"
42679       ],
42680       [
42681         "Czech Republic (Česká republika)",
42682         "cz",
42683         "420"
42684       ],
42685       [
42686         "Denmark (Danmark)",
42687         "dk",
42688         "45"
42689       ],
42690       [
42691         "Djibouti",
42692         "dj",
42693         "253"
42694       ],
42695       [
42696         "Dominica",
42697         "dm",
42698         "1767"
42699       ],
42700       [
42701         "Dominican Republic (República Dominicana)",
42702         "do",
42703         "1",
42704         2,
42705         ["809", "829", "849"]
42706       ],
42707       [
42708         "Ecuador",
42709         "ec",
42710         "593"
42711       ],
42712       [
42713         "Egypt (‫مصر‬‎)",
42714         "eg",
42715         "20"
42716       ],
42717       [
42718         "El Salvador",
42719         "sv",
42720         "503"
42721       ],
42722       [
42723         "Equatorial Guinea (Guinea Ecuatorial)",
42724         "gq",
42725         "240"
42726       ],
42727       [
42728         "Eritrea",
42729         "er",
42730         "291"
42731       ],
42732       [
42733         "Estonia (Eesti)",
42734         "ee",
42735         "372"
42736       ],
42737       [
42738         "Ethiopia",
42739         "et",
42740         "251"
42741       ],
42742       [
42743         "Falkland Islands (Islas Malvinas)",
42744         "fk",
42745         "500"
42746       ],
42747       [
42748         "Faroe Islands (Føroyar)",
42749         "fo",
42750         "298"
42751       ],
42752       [
42753         "Fiji",
42754         "fj",
42755         "679"
42756       ],
42757       [
42758         "Finland (Suomi)",
42759         "fi",
42760         "358",
42761         0
42762       ],
42763       [
42764         "France",
42765         "fr",
42766         "33"
42767       ],
42768       [
42769         "French Guiana (Guyane française)",
42770         "gf",
42771         "594"
42772       ],
42773       [
42774         "French Polynesia (Polynésie française)",
42775         "pf",
42776         "689"
42777       ],
42778       [
42779         "Gabon",
42780         "ga",
42781         "241"
42782       ],
42783       [
42784         "Gambia",
42785         "gm",
42786         "220"
42787       ],
42788       [
42789         "Georgia (საქართველო)",
42790         "ge",
42791         "995"
42792       ],
42793       [
42794         "Germany (Deutschland)",
42795         "de",
42796         "49"
42797       ],
42798       [
42799         "Ghana (Gaana)",
42800         "gh",
42801         "233"
42802       ],
42803       [
42804         "Gibraltar",
42805         "gi",
42806         "350"
42807       ],
42808       [
42809         "Greece (Ελλάδα)",
42810         "gr",
42811         "30"
42812       ],
42813       [
42814         "Greenland (Kalaallit Nunaat)",
42815         "gl",
42816         "299"
42817       ],
42818       [
42819         "Grenada",
42820         "gd",
42821         "1473"
42822       ],
42823       [
42824         "Guadeloupe",
42825         "gp",
42826         "590",
42827         0
42828       ],
42829       [
42830         "Guam",
42831         "gu",
42832         "1671"
42833       ],
42834       [
42835         "Guatemala",
42836         "gt",
42837         "502"
42838       ],
42839       [
42840         "Guernsey",
42841         "gg",
42842         "44",
42843         1
42844       ],
42845       [
42846         "Guinea (Guinée)",
42847         "gn",
42848         "224"
42849       ],
42850       [
42851         "Guinea-Bissau (Guiné Bissau)",
42852         "gw",
42853         "245"
42854       ],
42855       [
42856         "Guyana",
42857         "gy",
42858         "592"
42859       ],
42860       [
42861         "Haiti",
42862         "ht",
42863         "509"
42864       ],
42865       [
42866         "Honduras",
42867         "hn",
42868         "504"
42869       ],
42870       [
42871         "Hong Kong (香港)",
42872         "hk",
42873         "852"
42874       ],
42875       [
42876         "Hungary (Magyarország)",
42877         "hu",
42878         "36"
42879       ],
42880       [
42881         "Iceland (Ísland)",
42882         "is",
42883         "354"
42884       ],
42885       [
42886         "India (भारत)",
42887         "in",
42888         "91"
42889       ],
42890       [
42891         "Indonesia",
42892         "id",
42893         "62"
42894       ],
42895       [
42896         "Iran (‫ایران‬‎)",
42897         "ir",
42898         "98"
42899       ],
42900       [
42901         "Iraq (‫العراق‬‎)",
42902         "iq",
42903         "964"
42904       ],
42905       [
42906         "Ireland",
42907         "ie",
42908         "353"
42909       ],
42910       [
42911         "Isle of Man",
42912         "im",
42913         "44",
42914         2
42915       ],
42916       [
42917         "Israel (‫ישראל‬‎)",
42918         "il",
42919         "972"
42920       ],
42921       [
42922         "Italy (Italia)",
42923         "it",
42924         "39",
42925         0
42926       ],
42927       [
42928         "Jamaica",
42929         "jm",
42930         "1876"
42931       ],
42932       [
42933         "Japan (日本)",
42934         "jp",
42935         "81"
42936       ],
42937       [
42938         "Jersey",
42939         "je",
42940         "44",
42941         3
42942       ],
42943       [
42944         "Jordan (‫الأردن‬‎)",
42945         "jo",
42946         "962"
42947       ],
42948       [
42949         "Kazakhstan (Казахстан)",
42950         "kz",
42951         "7",
42952         1
42953       ],
42954       [
42955         "Kenya",
42956         "ke",
42957         "254"
42958       ],
42959       [
42960         "Kiribati",
42961         "ki",
42962         "686"
42963       ],
42964       [
42965         "Kosovo",
42966         "xk",
42967         "383"
42968       ],
42969       [
42970         "Kuwait (‫الكويت‬‎)",
42971         "kw",
42972         "965"
42973       ],
42974       [
42975         "Kyrgyzstan (Кыргызстан)",
42976         "kg",
42977         "996"
42978       ],
42979       [
42980         "Laos (ລາວ)",
42981         "la",
42982         "856"
42983       ],
42984       [
42985         "Latvia (Latvija)",
42986         "lv",
42987         "371"
42988       ],
42989       [
42990         "Lebanon (‫لبنان‬‎)",
42991         "lb",
42992         "961"
42993       ],
42994       [
42995         "Lesotho",
42996         "ls",
42997         "266"
42998       ],
42999       [
43000         "Liberia",
43001         "lr",
43002         "231"
43003       ],
43004       [
43005         "Libya (‫ليبيا‬‎)",
43006         "ly",
43007         "218"
43008       ],
43009       [
43010         "Liechtenstein",
43011         "li",
43012         "423"
43013       ],
43014       [
43015         "Lithuania (Lietuva)",
43016         "lt",
43017         "370"
43018       ],
43019       [
43020         "Luxembourg",
43021         "lu",
43022         "352"
43023       ],
43024       [
43025         "Macau (澳門)",
43026         "mo",
43027         "853"
43028       ],
43029       [
43030         "Macedonia (FYROM) (Македонија)",
43031         "mk",
43032         "389"
43033       ],
43034       [
43035         "Madagascar (Madagasikara)",
43036         "mg",
43037         "261"
43038       ],
43039       [
43040         "Malawi",
43041         "mw",
43042         "265"
43043       ],
43044       [
43045         "Malaysia",
43046         "my",
43047         "60"
43048       ],
43049       [
43050         "Maldives",
43051         "mv",
43052         "960"
43053       ],
43054       [
43055         "Mali",
43056         "ml",
43057         "223"
43058       ],
43059       [
43060         "Malta",
43061         "mt",
43062         "356"
43063       ],
43064       [
43065         "Marshall Islands",
43066         "mh",
43067         "692"
43068       ],
43069       [
43070         "Martinique",
43071         "mq",
43072         "596"
43073       ],
43074       [
43075         "Mauritania (‫موريتانيا‬‎)",
43076         "mr",
43077         "222"
43078       ],
43079       [
43080         "Mauritius (Moris)",
43081         "mu",
43082         "230"
43083       ],
43084       [
43085         "Mayotte",
43086         "yt",
43087         "262",
43088         1
43089       ],
43090       [
43091         "Mexico (México)",
43092         "mx",
43093         "52"
43094       ],
43095       [
43096         "Micronesia",
43097         "fm",
43098         "691"
43099       ],
43100       [
43101         "Moldova (Republica Moldova)",
43102         "md",
43103         "373"
43104       ],
43105       [
43106         "Monaco",
43107         "mc",
43108         "377"
43109       ],
43110       [
43111         "Mongolia (Монгол)",
43112         "mn",
43113         "976"
43114       ],
43115       [
43116         "Montenegro (Crna Gora)",
43117         "me",
43118         "382"
43119       ],
43120       [
43121         "Montserrat",
43122         "ms",
43123         "1664"
43124       ],
43125       [
43126         "Morocco (‫المغرب‬‎)",
43127         "ma",
43128         "212",
43129         0
43130       ],
43131       [
43132         "Mozambique (Moçambique)",
43133         "mz",
43134         "258"
43135       ],
43136       [
43137         "Myanmar (Burma) (မြန်မာ)",
43138         "mm",
43139         "95"
43140       ],
43141       [
43142         "Namibia (Namibië)",
43143         "na",
43144         "264"
43145       ],
43146       [
43147         "Nauru",
43148         "nr",
43149         "674"
43150       ],
43151       [
43152         "Nepal (नेपाल)",
43153         "np",
43154         "977"
43155       ],
43156       [
43157         "Netherlands (Nederland)",
43158         "nl",
43159         "31"
43160       ],
43161       [
43162         "New Caledonia (Nouvelle-Calédonie)",
43163         "nc",
43164         "687"
43165       ],
43166       [
43167         "New Zealand",
43168         "nz",
43169         "64"
43170       ],
43171       [
43172         "Nicaragua",
43173         "ni",
43174         "505"
43175       ],
43176       [
43177         "Niger (Nijar)",
43178         "ne",
43179         "227"
43180       ],
43181       [
43182         "Nigeria",
43183         "ng",
43184         "234"
43185       ],
43186       [
43187         "Niue",
43188         "nu",
43189         "683"
43190       ],
43191       [
43192         "Norfolk Island",
43193         "nf",
43194         "672"
43195       ],
43196       [
43197         "North Korea (조선 민주주의 인민 공화국)",
43198         "kp",
43199         "850"
43200       ],
43201       [
43202         "Northern Mariana Islands",
43203         "mp",
43204         "1670"
43205       ],
43206       [
43207         "Norway (Norge)",
43208         "no",
43209         "47",
43210         0
43211       ],
43212       [
43213         "Oman (‫عُمان‬‎)",
43214         "om",
43215         "968"
43216       ],
43217       [
43218         "Pakistan (‫پاکستان‬‎)",
43219         "pk",
43220         "92"
43221       ],
43222       [
43223         "Palau",
43224         "pw",
43225         "680"
43226       ],
43227       [
43228         "Palestine (‫فلسطين‬‎)",
43229         "ps",
43230         "970"
43231       ],
43232       [
43233         "Panama (Panamá)",
43234         "pa",
43235         "507"
43236       ],
43237       [
43238         "Papua New Guinea",
43239         "pg",
43240         "675"
43241       ],
43242       [
43243         "Paraguay",
43244         "py",
43245         "595"
43246       ],
43247       [
43248         "Peru (Perú)",
43249         "pe",
43250         "51"
43251       ],
43252       [
43253         "Philippines",
43254         "ph",
43255         "63"
43256       ],
43257       [
43258         "Poland (Polska)",
43259         "pl",
43260         "48"
43261       ],
43262       [
43263         "Portugal",
43264         "pt",
43265         "351"
43266       ],
43267       [
43268         "Puerto Rico",
43269         "pr",
43270         "1",
43271         3,
43272         ["787", "939"]
43273       ],
43274       [
43275         "Qatar (‫قطر‬‎)",
43276         "qa",
43277         "974"
43278       ],
43279       [
43280         "Réunion (La Réunion)",
43281         "re",
43282         "262",
43283         0
43284       ],
43285       [
43286         "Romania (România)",
43287         "ro",
43288         "40"
43289       ],
43290       [
43291         "Russia (Россия)",
43292         "ru",
43293         "7",
43294         0
43295       ],
43296       [
43297         "Rwanda",
43298         "rw",
43299         "250"
43300       ],
43301       [
43302         "Saint Barthélemy",
43303         "bl",
43304         "590",
43305         1
43306       ],
43307       [
43308         "Saint Helena",
43309         "sh",
43310         "290"
43311       ],
43312       [
43313         "Saint Kitts and Nevis",
43314         "kn",
43315         "1869"
43316       ],
43317       [
43318         "Saint Lucia",
43319         "lc",
43320         "1758"
43321       ],
43322       [
43323         "Saint Martin (Saint-Martin (partie française))",
43324         "mf",
43325         "590",
43326         2
43327       ],
43328       [
43329         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43330         "pm",
43331         "508"
43332       ],
43333       [
43334         "Saint Vincent and the Grenadines",
43335         "vc",
43336         "1784"
43337       ],
43338       [
43339         "Samoa",
43340         "ws",
43341         "685"
43342       ],
43343       [
43344         "San Marino",
43345         "sm",
43346         "378"
43347       ],
43348       [
43349         "São Tomé and Príncipe (São Tomé e Príncipe)",
43350         "st",
43351         "239"
43352       ],
43353       [
43354         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43355         "sa",
43356         "966"
43357       ],
43358       [
43359         "Senegal (Sénégal)",
43360         "sn",
43361         "221"
43362       ],
43363       [
43364         "Serbia (Србија)",
43365         "rs",
43366         "381"
43367       ],
43368       [
43369         "Seychelles",
43370         "sc",
43371         "248"
43372       ],
43373       [
43374         "Sierra Leone",
43375         "sl",
43376         "232"
43377       ],
43378       [
43379         "Singapore",
43380         "sg",
43381         "65"
43382       ],
43383       [
43384         "Sint Maarten",
43385         "sx",
43386         "1721"
43387       ],
43388       [
43389         "Slovakia (Slovensko)",
43390         "sk",
43391         "421"
43392       ],
43393       [
43394         "Slovenia (Slovenija)",
43395         "si",
43396         "386"
43397       ],
43398       [
43399         "Solomon Islands",
43400         "sb",
43401         "677"
43402       ],
43403       [
43404         "Somalia (Soomaaliya)",
43405         "so",
43406         "252"
43407       ],
43408       [
43409         "South Africa",
43410         "za",
43411         "27"
43412       ],
43413       [
43414         "South Korea (대한민국)",
43415         "kr",
43416         "82"
43417       ],
43418       [
43419         "South Sudan (‫جنوب السودان‬‎)",
43420         "ss",
43421         "211"
43422       ],
43423       [
43424         "Spain (España)",
43425         "es",
43426         "34"
43427       ],
43428       [
43429         "Sri Lanka (ශ්‍රී ලංකාව)",
43430         "lk",
43431         "94"
43432       ],
43433       [
43434         "Sudan (‫السودان‬‎)",
43435         "sd",
43436         "249"
43437       ],
43438       [
43439         "Suriname",
43440         "sr",
43441         "597"
43442       ],
43443       [
43444         "Svalbard and Jan Mayen",
43445         "sj",
43446         "47",
43447         1
43448       ],
43449       [
43450         "Swaziland",
43451         "sz",
43452         "268"
43453       ],
43454       [
43455         "Sweden (Sverige)",
43456         "se",
43457         "46"
43458       ],
43459       [
43460         "Switzerland (Schweiz)",
43461         "ch",
43462         "41"
43463       ],
43464       [
43465         "Syria (‫سوريا‬‎)",
43466         "sy",
43467         "963"
43468       ],
43469       [
43470         "Taiwan (台灣)",
43471         "tw",
43472         "886"
43473       ],
43474       [
43475         "Tajikistan",
43476         "tj",
43477         "992"
43478       ],
43479       [
43480         "Tanzania",
43481         "tz",
43482         "255"
43483       ],
43484       [
43485         "Thailand (ไทย)",
43486         "th",
43487         "66"
43488       ],
43489       [
43490         "Timor-Leste",
43491         "tl",
43492         "670"
43493       ],
43494       [
43495         "Togo",
43496         "tg",
43497         "228"
43498       ],
43499       [
43500         "Tokelau",
43501         "tk",
43502         "690"
43503       ],
43504       [
43505         "Tonga",
43506         "to",
43507         "676"
43508       ],
43509       [
43510         "Trinidad and Tobago",
43511         "tt",
43512         "1868"
43513       ],
43514       [
43515         "Tunisia (‫تونس‬‎)",
43516         "tn",
43517         "216"
43518       ],
43519       [
43520         "Turkey (Türkiye)",
43521         "tr",
43522         "90"
43523       ],
43524       [
43525         "Turkmenistan",
43526         "tm",
43527         "993"
43528       ],
43529       [
43530         "Turks and Caicos Islands",
43531         "tc",
43532         "1649"
43533       ],
43534       [
43535         "Tuvalu",
43536         "tv",
43537         "688"
43538       ],
43539       [
43540         "U.S. Virgin Islands",
43541         "vi",
43542         "1340"
43543       ],
43544       [
43545         "Uganda",
43546         "ug",
43547         "256"
43548       ],
43549       [
43550         "Ukraine (Україна)",
43551         "ua",
43552         "380"
43553       ],
43554       [
43555         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43556         "ae",
43557         "971"
43558       ],
43559       [
43560         "United Kingdom",
43561         "gb",
43562         "44",
43563         0
43564       ],
43565       [
43566         "United States",
43567         "us",
43568         "1",
43569         0
43570       ],
43571       [
43572         "Uruguay",
43573         "uy",
43574         "598"
43575       ],
43576       [
43577         "Uzbekistan (Oʻzbekiston)",
43578         "uz",
43579         "998"
43580       ],
43581       [
43582         "Vanuatu",
43583         "vu",
43584         "678"
43585       ],
43586       [
43587         "Vatican City (Città del Vaticano)",
43588         "va",
43589         "39",
43590         1
43591       ],
43592       [
43593         "Venezuela",
43594         "ve",
43595         "58"
43596       ],
43597       [
43598         "Vietnam (Việt Nam)",
43599         "vn",
43600         "84"
43601       ],
43602       [
43603         "Wallis and Futuna (Wallis-et-Futuna)",
43604         "wf",
43605         "681"
43606       ],
43607       [
43608         "Western Sahara (‫الصحراء الغربية‬‎)",
43609         "eh",
43610         "212",
43611         1
43612       ],
43613       [
43614         "Yemen (‫اليمن‬‎)",
43615         "ye",
43616         "967"
43617       ],
43618       [
43619         "Zambia",
43620         "zm",
43621         "260"
43622       ],
43623       [
43624         "Zimbabwe",
43625         "zw",
43626         "263"
43627       ],
43628       [
43629         "Åland Islands",
43630         "ax",
43631         "358",
43632         1
43633       ]
43634   ];
43635   
43636   return d;
43637 }/**
43638 *    This script refer to:
43639 *    Title: International Telephone Input
43640 *    Author: Jack O'Connor
43641 *    Code version:  v12.1.12
43642 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43643 **/
43644
43645 /**
43646  * @class Roo.bootstrap.PhoneInput
43647  * @extends Roo.bootstrap.TriggerField
43648  * An input with International dial-code selection
43649  
43650  * @cfg {String} defaultDialCode default '+852'
43651  * @cfg {Array} preferedCountries default []
43652   
43653  * @constructor
43654  * Create a new PhoneInput.
43655  * @param {Object} config Configuration options
43656  */
43657
43658 Roo.bootstrap.PhoneInput = function(config) {
43659     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43660 };
43661
43662 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43663         /**
43664         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43665         */
43666         listWidth: undefined,
43667         
43668         selectedClass: 'active',
43669         
43670         invalidClass : "has-warning",
43671         
43672         validClass: 'has-success',
43673         
43674         allowed: '0123456789',
43675         
43676         max_length: 15,
43677         
43678         /**
43679          * @cfg {String} defaultDialCode The default dial code when initializing the input
43680          */
43681         defaultDialCode: '+852',
43682         
43683         /**
43684          * @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
43685          */
43686         preferedCountries: false,
43687         
43688         getAutoCreate : function()
43689         {
43690             var data = Roo.bootstrap.PhoneInputData();
43691             var align = this.labelAlign || this.parentLabelAlign();
43692             var id = Roo.id();
43693             
43694             this.allCountries = [];
43695             this.dialCodeMapping = [];
43696             
43697             for (var i = 0; i < data.length; i++) {
43698               var c = data[i];
43699               this.allCountries[i] = {
43700                 name: c[0],
43701                 iso2: c[1],
43702                 dialCode: c[2],
43703                 priority: c[3] || 0,
43704                 areaCodes: c[4] || null
43705               };
43706               this.dialCodeMapping[c[2]] = {
43707                   name: c[0],
43708                   iso2: c[1],
43709                   priority: c[3] || 0,
43710                   areaCodes: c[4] || null
43711               };
43712             }
43713             
43714             var cfg = {
43715                 cls: 'form-group',
43716                 cn: []
43717             };
43718             
43719             var input =  {
43720                 tag: 'input',
43721                 id : id,
43722                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43723                 maxlength: this.max_length,
43724                 cls : 'form-control tel-input',
43725                 autocomplete: 'new-password'
43726             };
43727             
43728             var hiddenInput = {
43729                 tag: 'input',
43730                 type: 'hidden',
43731                 cls: 'hidden-tel-input'
43732             };
43733             
43734             if (this.name) {
43735                 hiddenInput.name = this.name;
43736             }
43737             
43738             if (this.disabled) {
43739                 input.disabled = true;
43740             }
43741             
43742             var flag_container = {
43743                 tag: 'div',
43744                 cls: 'flag-box',
43745                 cn: [
43746                     {
43747                         tag: 'div',
43748                         cls: 'flag'
43749                     },
43750                     {
43751                         tag: 'div',
43752                         cls: 'caret'
43753                     }
43754                 ]
43755             };
43756             
43757             var box = {
43758                 tag: 'div',
43759                 cls: this.hasFeedback ? 'has-feedback' : '',
43760                 cn: [
43761                     hiddenInput,
43762                     input,
43763                     {
43764                         tag: 'input',
43765                         cls: 'dial-code-holder',
43766                         disabled: true
43767                     }
43768                 ]
43769             };
43770             
43771             var container = {
43772                 cls: 'roo-select2-container input-group',
43773                 cn: [
43774                     flag_container,
43775                     box
43776                 ]
43777             };
43778             
43779             if (this.fieldLabel.length) {
43780                 var indicator = {
43781                     tag: 'i',
43782                     tooltip: 'This field is required'
43783                 };
43784                 
43785                 var label = {
43786                     tag: 'label',
43787                     'for':  id,
43788                     cls: 'control-label',
43789                     cn: []
43790                 };
43791                 
43792                 var label_text = {
43793                     tag: 'span',
43794                     html: this.fieldLabel
43795                 };
43796                 
43797                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43798                 label.cn = [
43799                     indicator,
43800                     label_text
43801                 ];
43802                 
43803                 if(this.indicatorpos == 'right') {
43804                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43805                     label.cn = [
43806                         label_text,
43807                         indicator
43808                     ];
43809                 }
43810                 
43811                 if(align == 'left') {
43812                     container = {
43813                         tag: 'div',
43814                         cn: [
43815                             container
43816                         ]
43817                     };
43818                     
43819                     if(this.labelWidth > 12){
43820                         label.style = "width: " + this.labelWidth + 'px';
43821                     }
43822                     if(this.labelWidth < 13 && this.labelmd == 0){
43823                         this.labelmd = this.labelWidth;
43824                     }
43825                     if(this.labellg > 0){
43826                         label.cls += ' col-lg-' + this.labellg;
43827                         input.cls += ' col-lg-' + (12 - this.labellg);
43828                     }
43829                     if(this.labelmd > 0){
43830                         label.cls += ' col-md-' + this.labelmd;
43831                         container.cls += ' col-md-' + (12 - this.labelmd);
43832                     }
43833                     if(this.labelsm > 0){
43834                         label.cls += ' col-sm-' + this.labelsm;
43835                         container.cls += ' col-sm-' + (12 - this.labelsm);
43836                     }
43837                     if(this.labelxs > 0){
43838                         label.cls += ' col-xs-' + this.labelxs;
43839                         container.cls += ' col-xs-' + (12 - this.labelxs);
43840                     }
43841                 }
43842             }
43843             
43844             cfg.cn = [
43845                 label,
43846                 container
43847             ];
43848             
43849             var settings = this;
43850             
43851             ['xs','sm','md','lg'].map(function(size){
43852                 if (settings[size]) {
43853                     cfg.cls += ' col-' + size + '-' + settings[size];
43854                 }
43855             });
43856             
43857             this.store = new Roo.data.Store({
43858                 proxy : new Roo.data.MemoryProxy({}),
43859                 reader : new Roo.data.JsonReader({
43860                     fields : [
43861                         {
43862                             'name' : 'name',
43863                             'type' : 'string'
43864                         },
43865                         {
43866                             'name' : 'iso2',
43867                             'type' : 'string'
43868                         },
43869                         {
43870                             'name' : 'dialCode',
43871                             'type' : 'string'
43872                         },
43873                         {
43874                             'name' : 'priority',
43875                             'type' : 'string'
43876                         },
43877                         {
43878                             'name' : 'areaCodes',
43879                             'type' : 'string'
43880                         }
43881                     ]
43882                 })
43883             });
43884             
43885             if(!this.preferedCountries) {
43886                 this.preferedCountries = [
43887                     'hk',
43888                     'gb',
43889                     'us'
43890                 ];
43891             }
43892             
43893             var p = this.preferedCountries.reverse();
43894             
43895             if(p) {
43896                 for (var i = 0; i < p.length; i++) {
43897                     for (var j = 0; j < this.allCountries.length; j++) {
43898                         if(this.allCountries[j].iso2 == p[i]) {
43899                             var t = this.allCountries[j];
43900                             this.allCountries.splice(j,1);
43901                             this.allCountries.unshift(t);
43902                         }
43903                     } 
43904                 }
43905             }
43906             
43907             this.store.proxy.data = {
43908                 success: true,
43909                 data: this.allCountries
43910             };
43911             
43912             return cfg;
43913         },
43914         
43915         initEvents : function()
43916         {
43917             this.createList();
43918             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43919             
43920             this.indicator = this.indicatorEl();
43921             this.flag = this.flagEl();
43922             this.dialCodeHolder = this.dialCodeHolderEl();
43923             
43924             this.trigger = this.el.select('div.flag-box',true).first();
43925             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43926             
43927             var _this = this;
43928             
43929             (function(){
43930                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43931                 _this.list.setWidth(lw);
43932             }).defer(100);
43933             
43934             this.list.on('mouseover', this.onViewOver, this);
43935             this.list.on('mousemove', this.onViewMove, this);
43936             this.inputEl().on("keyup", this.onKeyUp, this);
43937             this.inputEl().on("keypress", this.onKeyPress, this);
43938             
43939             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43940
43941             this.view = new Roo.View(this.list, this.tpl, {
43942                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43943             });
43944             
43945             this.view.on('click', this.onViewClick, this);
43946             this.setValue(this.defaultDialCode);
43947         },
43948         
43949         onTriggerClick : function(e)
43950         {
43951             Roo.log('trigger click');
43952             if(this.disabled){
43953                 return;
43954             }
43955             
43956             if(this.isExpanded()){
43957                 this.collapse();
43958                 this.hasFocus = false;
43959             }else {
43960                 this.store.load({});
43961                 this.hasFocus = true;
43962                 this.expand();
43963             }
43964         },
43965         
43966         isExpanded : function()
43967         {
43968             return this.list.isVisible();
43969         },
43970         
43971         collapse : function()
43972         {
43973             if(!this.isExpanded()){
43974                 return;
43975             }
43976             this.list.hide();
43977             Roo.get(document).un('mousedown', this.collapseIf, this);
43978             Roo.get(document).un('mousewheel', this.collapseIf, this);
43979             this.fireEvent('collapse', this);
43980             this.validate();
43981         },
43982         
43983         expand : function()
43984         {
43985             Roo.log('expand');
43986
43987             if(this.isExpanded() || !this.hasFocus){
43988                 return;
43989             }
43990             
43991             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43992             this.list.setWidth(lw);
43993             
43994             this.list.show();
43995             this.restrictHeight();
43996             
43997             Roo.get(document).on('mousedown', this.collapseIf, this);
43998             Roo.get(document).on('mousewheel', this.collapseIf, this);
43999             
44000             this.fireEvent('expand', this);
44001         },
44002         
44003         restrictHeight : function()
44004         {
44005             this.list.alignTo(this.inputEl(), this.listAlign);
44006             this.list.alignTo(this.inputEl(), this.listAlign);
44007         },
44008         
44009         onViewOver : function(e, t)
44010         {
44011             if(this.inKeyMode){
44012                 return;
44013             }
44014             var item = this.view.findItemFromChild(t);
44015             
44016             if(item){
44017                 var index = this.view.indexOf(item);
44018                 this.select(index, false);
44019             }
44020         },
44021
44022         // private
44023         onViewClick : function(view, doFocus, el, e)
44024         {
44025             var index = this.view.getSelectedIndexes()[0];
44026             
44027             var r = this.store.getAt(index);
44028             
44029             if(r){
44030                 this.onSelect(r, index);
44031             }
44032             if(doFocus !== false && !this.blockFocus){
44033                 this.inputEl().focus();
44034             }
44035         },
44036         
44037         onViewMove : function(e, t)
44038         {
44039             this.inKeyMode = false;
44040         },
44041         
44042         select : function(index, scrollIntoView)
44043         {
44044             this.selectedIndex = index;
44045             this.view.select(index);
44046             if(scrollIntoView !== false){
44047                 var el = this.view.getNode(index);
44048                 if(el){
44049                     this.list.scrollChildIntoView(el, false);
44050                 }
44051             }
44052         },
44053         
44054         createList : function()
44055         {
44056             this.list = Roo.get(document.body).createChild({
44057                 tag: 'ul',
44058                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44059                 style: 'display:none'
44060             });
44061             
44062             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44063         },
44064         
44065         collapseIf : function(e)
44066         {
44067             var in_combo  = e.within(this.el);
44068             var in_list =  e.within(this.list);
44069             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44070             
44071             if (in_combo || in_list || is_list) {
44072                 return;
44073             }
44074             this.collapse();
44075         },
44076         
44077         onSelect : function(record, index)
44078         {
44079             if(this.fireEvent('beforeselect', this, record, index) !== false){
44080                 
44081                 this.setFlagClass(record.data.iso2);
44082                 this.setDialCode(record.data.dialCode);
44083                 this.hasFocus = false;
44084                 this.collapse();
44085                 this.fireEvent('select', this, record, index);
44086             }
44087         },
44088         
44089         flagEl : function()
44090         {
44091             var flag = this.el.select('div.flag',true).first();
44092             if(!flag){
44093                 return false;
44094             }
44095             return flag;
44096         },
44097         
44098         dialCodeHolderEl : function()
44099         {
44100             var d = this.el.select('input.dial-code-holder',true).first();
44101             if(!d){
44102                 return false;
44103             }
44104             return d;
44105         },
44106         
44107         setDialCode : function(v)
44108         {
44109             this.dialCodeHolder.dom.value = '+'+v;
44110         },
44111         
44112         setFlagClass : function(n)
44113         {
44114             this.flag.dom.className = 'flag '+n;
44115         },
44116         
44117         getValue : function()
44118         {
44119             var v = this.inputEl().getValue();
44120             if(this.dialCodeHolder) {
44121                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44122             }
44123             return v;
44124         },
44125         
44126         setValue : function(v)
44127         {
44128             var d = this.getDialCode(v);
44129             
44130             //invalid dial code
44131             if(v.length == 0 || !d || d.length == 0) {
44132                 if(this.rendered){
44133                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44134                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44135                 }
44136                 return;
44137             }
44138             
44139             //valid dial code
44140             this.setFlagClass(this.dialCodeMapping[d].iso2);
44141             this.setDialCode(d);
44142             this.inputEl().dom.value = v.replace('+'+d,'');
44143             this.hiddenEl().dom.value = this.getValue();
44144             
44145             this.validate();
44146         },
44147         
44148         getDialCode : function(v)
44149         {
44150             v = v ||  '';
44151             
44152             if (v.length == 0) {
44153                 return this.dialCodeHolder.dom.value;
44154             }
44155             
44156             var dialCode = "";
44157             if (v.charAt(0) != "+") {
44158                 return false;
44159             }
44160             var numericChars = "";
44161             for (var i = 1; i < v.length; i++) {
44162               var c = v.charAt(i);
44163               if (!isNaN(c)) {
44164                 numericChars += c;
44165                 if (this.dialCodeMapping[numericChars]) {
44166                   dialCode = v.substr(1, i);
44167                 }
44168                 if (numericChars.length == 4) {
44169                   break;
44170                 }
44171               }
44172             }
44173             return dialCode;
44174         },
44175         
44176         reset : function()
44177         {
44178             this.setValue(this.defaultDialCode);
44179             this.validate();
44180         },
44181         
44182         hiddenEl : function()
44183         {
44184             return this.el.select('input.hidden-tel-input',true).first();
44185         },
44186         
44187         // after setting val
44188         onKeyUp : function(e){
44189             this.setValue(this.getValue());
44190         },
44191         
44192         onKeyPress : function(e){
44193             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44194                 e.stopEvent();
44195             }
44196         }
44197         
44198 });
44199 /**
44200  * @class Roo.bootstrap.MoneyField
44201  * @extends Roo.bootstrap.ComboBox
44202  * Bootstrap MoneyField class
44203  * 
44204  * @constructor
44205  * Create a new MoneyField.
44206  * @param {Object} config Configuration options
44207  */
44208
44209 Roo.bootstrap.MoneyField = function(config) {
44210     
44211     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44212     
44213 };
44214
44215 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44216     
44217     /**
44218      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44219      */
44220     allowDecimals : true,
44221     /**
44222      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44223      */
44224     decimalSeparator : ".",
44225     /**
44226      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44227      */
44228     decimalPrecision : 0,
44229     /**
44230      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44231      */
44232     allowNegative : true,
44233     /**
44234      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44235      */
44236     allowZero: true,
44237     /**
44238      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44239      */
44240     minValue : Number.NEGATIVE_INFINITY,
44241     /**
44242      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44243      */
44244     maxValue : Number.MAX_VALUE,
44245     /**
44246      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44247      */
44248     minText : "The minimum value for this field is {0}",
44249     /**
44250      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44251      */
44252     maxText : "The maximum value for this field is {0}",
44253     /**
44254      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44255      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44256      */
44257     nanText : "{0} is not a valid number",
44258     /**
44259      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44260      */
44261     castInt : true,
44262     /**
44263      * @cfg {String} defaults currency of the MoneyField
44264      * value should be in lkey
44265      */
44266     defaultCurrency : false,
44267     /**
44268      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44269      */
44270     thousandsDelimiter : false,
44271     /**
44272      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44273      */
44274     max_length: false,
44275     
44276     inputlg : 9,
44277     inputmd : 9,
44278     inputsm : 9,
44279     inputxs : 6,
44280     
44281     store : false,
44282     
44283     getAutoCreate : function()
44284     {
44285         var align = this.labelAlign || this.parentLabelAlign();
44286         
44287         var id = Roo.id();
44288
44289         var cfg = {
44290             cls: 'form-group',
44291             cn: []
44292         };
44293
44294         var input =  {
44295             tag: 'input',
44296             id : id,
44297             cls : 'form-control roo-money-amount-input',
44298             autocomplete: 'new-password'
44299         };
44300         
44301         var hiddenInput = {
44302             tag: 'input',
44303             type: 'hidden',
44304             id: Roo.id(),
44305             cls: 'hidden-number-input'
44306         };
44307         
44308         if(this.max_length) {
44309             input.maxlength = this.max_length; 
44310         }
44311         
44312         if (this.name) {
44313             hiddenInput.name = this.name;
44314         }
44315
44316         if (this.disabled) {
44317             input.disabled = true;
44318         }
44319
44320         var clg = 12 - this.inputlg;
44321         var cmd = 12 - this.inputmd;
44322         var csm = 12 - this.inputsm;
44323         var cxs = 12 - this.inputxs;
44324         
44325         var container = {
44326             tag : 'div',
44327             cls : 'row roo-money-field',
44328             cn : [
44329                 {
44330                     tag : 'div',
44331                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44332                     cn : [
44333                         {
44334                             tag : 'div',
44335                             cls: 'roo-select2-container input-group',
44336                             cn: [
44337                                 {
44338                                     tag : 'input',
44339                                     cls : 'form-control roo-money-currency-input',
44340                                     autocomplete: 'new-password',
44341                                     readOnly : 1,
44342                                     name : this.currencyName
44343                                 },
44344                                 {
44345                                     tag :'span',
44346                                     cls : 'input-group-addon',
44347                                     cn : [
44348                                         {
44349                                             tag: 'span',
44350                                             cls: 'caret'
44351                                         }
44352                                     ]
44353                                 }
44354                             ]
44355                         }
44356                     ]
44357                 },
44358                 {
44359                     tag : 'div',
44360                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44361                     cn : [
44362                         {
44363                             tag: 'div',
44364                             cls: this.hasFeedback ? 'has-feedback' : '',
44365                             cn: [
44366                                 input
44367                             ]
44368                         }
44369                     ]
44370                 }
44371             ]
44372             
44373         };
44374         
44375         if (this.fieldLabel.length) {
44376             var indicator = {
44377                 tag: 'i',
44378                 tooltip: 'This field is required'
44379             };
44380
44381             var label = {
44382                 tag: 'label',
44383                 'for':  id,
44384                 cls: 'control-label',
44385                 cn: []
44386             };
44387
44388             var label_text = {
44389                 tag: 'span',
44390                 html: this.fieldLabel
44391             };
44392
44393             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44394             label.cn = [
44395                 indicator,
44396                 label_text
44397             ];
44398
44399             if(this.indicatorpos == 'right') {
44400                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44401                 label.cn = [
44402                     label_text,
44403                     indicator
44404                 ];
44405             }
44406
44407             if(align == 'left') {
44408                 container = {
44409                     tag: 'div',
44410                     cn: [
44411                         container
44412                     ]
44413                 };
44414
44415                 if(this.labelWidth > 12){
44416                     label.style = "width: " + this.labelWidth + 'px';
44417                 }
44418                 if(this.labelWidth < 13 && this.labelmd == 0){
44419                     this.labelmd = this.labelWidth;
44420                 }
44421                 if(this.labellg > 0){
44422                     label.cls += ' col-lg-' + this.labellg;
44423                     input.cls += ' col-lg-' + (12 - this.labellg);
44424                 }
44425                 if(this.labelmd > 0){
44426                     label.cls += ' col-md-' + this.labelmd;
44427                     container.cls += ' col-md-' + (12 - this.labelmd);
44428                 }
44429                 if(this.labelsm > 0){
44430                     label.cls += ' col-sm-' + this.labelsm;
44431                     container.cls += ' col-sm-' + (12 - this.labelsm);
44432                 }
44433                 if(this.labelxs > 0){
44434                     label.cls += ' col-xs-' + this.labelxs;
44435                     container.cls += ' col-xs-' + (12 - this.labelxs);
44436                 }
44437             }
44438         }
44439
44440         cfg.cn = [
44441             label,
44442             container,
44443             hiddenInput
44444         ];
44445         
44446         var settings = this;
44447
44448         ['xs','sm','md','lg'].map(function(size){
44449             if (settings[size]) {
44450                 cfg.cls += ' col-' + size + '-' + settings[size];
44451             }
44452         });
44453         
44454         return cfg;
44455     },
44456     
44457     initEvents : function()
44458     {
44459         this.indicator = this.indicatorEl();
44460         
44461         this.initCurrencyEvent();
44462         
44463         this.initNumberEvent();
44464     },
44465     
44466     initCurrencyEvent : function()
44467     {
44468         if (!this.store) {
44469             throw "can not find store for combo";
44470         }
44471         
44472         this.store = Roo.factory(this.store, Roo.data);
44473         this.store.parent = this;
44474         
44475         this.createList();
44476         
44477         this.triggerEl = this.el.select('.input-group-addon', true).first();
44478         
44479         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44480         
44481         var _this = this;
44482         
44483         (function(){
44484             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44485             _this.list.setWidth(lw);
44486         }).defer(100);
44487         
44488         this.list.on('mouseover', this.onViewOver, this);
44489         this.list.on('mousemove', this.onViewMove, this);
44490         this.list.on('scroll', this.onViewScroll, this);
44491         
44492         if(!this.tpl){
44493             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44494         }
44495         
44496         this.view = new Roo.View(this.list, this.tpl, {
44497             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44498         });
44499         
44500         this.view.on('click', this.onViewClick, this);
44501         
44502         this.store.on('beforeload', this.onBeforeLoad, this);
44503         this.store.on('load', this.onLoad, this);
44504         this.store.on('loadexception', this.onLoadException, this);
44505         
44506         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44507             "up" : function(e){
44508                 this.inKeyMode = true;
44509                 this.selectPrev();
44510             },
44511
44512             "down" : function(e){
44513                 if(!this.isExpanded()){
44514                     this.onTriggerClick();
44515                 }else{
44516                     this.inKeyMode = true;
44517                     this.selectNext();
44518                 }
44519             },
44520
44521             "enter" : function(e){
44522                 this.collapse();
44523                 
44524                 if(this.fireEvent("specialkey", this, e)){
44525                     this.onViewClick(false);
44526                 }
44527                 
44528                 return true;
44529             },
44530
44531             "esc" : function(e){
44532                 this.collapse();
44533             },
44534
44535             "tab" : function(e){
44536                 this.collapse();
44537                 
44538                 if(this.fireEvent("specialkey", this, e)){
44539                     this.onViewClick(false);
44540                 }
44541                 
44542                 return true;
44543             },
44544
44545             scope : this,
44546
44547             doRelay : function(foo, bar, hname){
44548                 if(hname == 'down' || this.scope.isExpanded()){
44549                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44550                 }
44551                 return true;
44552             },
44553
44554             forceKeyDown: true
44555         });
44556         
44557         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44558         
44559     },
44560     
44561     initNumberEvent : function(e)
44562     {
44563         this.inputEl().on("keydown" , this.fireKey,  this);
44564         this.inputEl().on("focus", this.onFocus,  this);
44565         this.inputEl().on("blur", this.onBlur,  this);
44566         
44567         this.inputEl().relayEvent('keyup', this);
44568         
44569         if(this.indicator){
44570             this.indicator.addClass('invisible');
44571         }
44572  
44573         this.originalValue = this.getValue();
44574         
44575         if(this.validationEvent == 'keyup'){
44576             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44577             this.inputEl().on('keyup', this.filterValidation, this);
44578         }
44579         else if(this.validationEvent !== false){
44580             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44581         }
44582         
44583         if(this.selectOnFocus){
44584             this.on("focus", this.preFocus, this);
44585             
44586         }
44587         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44588             this.inputEl().on("keypress", this.filterKeys, this);
44589         } else {
44590             this.inputEl().relayEvent('keypress', this);
44591         }
44592         
44593         var allowed = "0123456789";
44594         
44595         if(this.allowDecimals){
44596             allowed += this.decimalSeparator;
44597         }
44598         
44599         if(this.allowNegative){
44600             allowed += "-";
44601         }
44602         
44603         if(this.thousandsDelimiter) {
44604             allowed += ",";
44605         }
44606         
44607         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44608         
44609         var keyPress = function(e){
44610             
44611             var k = e.getKey();
44612             
44613             var c = e.getCharCode();
44614             
44615             if(
44616                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44617                     allowed.indexOf(String.fromCharCode(c)) === -1
44618             ){
44619                 e.stopEvent();
44620                 return;
44621             }
44622             
44623             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44624                 return;
44625             }
44626             
44627             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44628                 e.stopEvent();
44629             }
44630         };
44631         
44632         this.inputEl().on("keypress", keyPress, this);
44633         
44634     },
44635     
44636     onTriggerClick : function(e)
44637     {   
44638         if(this.disabled){
44639             return;
44640         }
44641         
44642         this.page = 0;
44643         this.loadNext = false;
44644         
44645         if(this.isExpanded()){
44646             this.collapse();
44647             return;
44648         }
44649         
44650         this.hasFocus = true;
44651         
44652         if(this.triggerAction == 'all') {
44653             this.doQuery(this.allQuery, true);
44654             return;
44655         }
44656         
44657         this.doQuery(this.getRawValue());
44658     },
44659     
44660     getCurrency : function()
44661     {   
44662         var v = this.currencyEl().getValue();
44663         
44664         return v;
44665     },
44666     
44667     restrictHeight : function()
44668     {
44669         this.list.alignTo(this.currencyEl(), this.listAlign);
44670         this.list.alignTo(this.currencyEl(), this.listAlign);
44671     },
44672     
44673     onViewClick : function(view, doFocus, el, e)
44674     {
44675         var index = this.view.getSelectedIndexes()[0];
44676         
44677         var r = this.store.getAt(index);
44678         
44679         if(r){
44680             this.onSelect(r, index);
44681         }
44682     },
44683     
44684     onSelect : function(record, index){
44685         
44686         if(this.fireEvent('beforeselect', this, record, index) !== false){
44687         
44688             this.setFromCurrencyData(index > -1 ? record.data : false);
44689             
44690             this.collapse();
44691             
44692             this.fireEvent('select', this, record, index);
44693         }
44694     },
44695     
44696     setFromCurrencyData : function(o)
44697     {
44698         var currency = '';
44699         
44700         this.lastCurrency = o;
44701         
44702         if (this.currencyField) {
44703             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44704         } else {
44705             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44706         }
44707         
44708         this.lastSelectionText = currency;
44709         
44710         //setting default currency
44711         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44712             this.setCurrency(this.defaultCurrency);
44713             return;
44714         }
44715         
44716         this.setCurrency(currency);
44717     },
44718     
44719     setFromData : function(o)
44720     {
44721         var c = {};
44722         
44723         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44724         
44725         this.setFromCurrencyData(c);
44726         
44727         var value = '';
44728         
44729         if (this.name) {
44730             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44731         } else {
44732             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44733         }
44734         
44735         this.setValue(value);
44736         
44737     },
44738     
44739     setCurrency : function(v)
44740     {   
44741         this.currencyValue = v;
44742         
44743         if(this.rendered){
44744             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44745             this.validate();
44746         }
44747     },
44748     
44749     setValue : function(v)
44750     {
44751         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44752         
44753         this.value = v;
44754         
44755         if(this.rendered){
44756             
44757             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44758             
44759             this.inputEl().dom.value = (v == '') ? '' :
44760                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44761             
44762             if(!this.allowZero && v === '0') {
44763                 this.hiddenEl().dom.value = '';
44764                 this.inputEl().dom.value = '';
44765             }
44766             
44767             this.validate();
44768         }
44769     },
44770     
44771     getRawValue : function()
44772     {
44773         var v = this.inputEl().getValue();
44774         
44775         return v;
44776     },
44777     
44778     getValue : function()
44779     {
44780         return this.fixPrecision(this.parseValue(this.getRawValue()));
44781     },
44782     
44783     parseValue : function(value)
44784     {
44785         if(this.thousandsDelimiter) {
44786             value += "";
44787             r = new RegExp(",", "g");
44788             value = value.replace(r, "");
44789         }
44790         
44791         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44792         return isNaN(value) ? '' : value;
44793         
44794     },
44795     
44796     fixPrecision : function(value)
44797     {
44798         if(this.thousandsDelimiter) {
44799             value += "";
44800             r = new RegExp(",", "g");
44801             value = value.replace(r, "");
44802         }
44803         
44804         var nan = isNaN(value);
44805         
44806         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44807             return nan ? '' : value;
44808         }
44809         return parseFloat(value).toFixed(this.decimalPrecision);
44810     },
44811     
44812     decimalPrecisionFcn : function(v)
44813     {
44814         return Math.floor(v);
44815     },
44816     
44817     validateValue : function(value)
44818     {
44819         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44820             return false;
44821         }
44822         
44823         var num = this.parseValue(value);
44824         
44825         if(isNaN(num)){
44826             this.markInvalid(String.format(this.nanText, value));
44827             return false;
44828         }
44829         
44830         if(num < this.minValue){
44831             this.markInvalid(String.format(this.minText, this.minValue));
44832             return false;
44833         }
44834         
44835         if(num > this.maxValue){
44836             this.markInvalid(String.format(this.maxText, this.maxValue));
44837             return false;
44838         }
44839         
44840         return true;
44841     },
44842     
44843     validate : function()
44844     {
44845         if(this.disabled || this.allowBlank){
44846             this.markValid();
44847             return true;
44848         }
44849         
44850         var currency = this.getCurrency();
44851         
44852         if(this.validateValue(this.getRawValue()) && currency.length){
44853             this.markValid();
44854             return true;
44855         }
44856         
44857         this.markInvalid();
44858         return false;
44859     },
44860     
44861     getName: function()
44862     {
44863         return this.name;
44864     },
44865     
44866     beforeBlur : function()
44867     {
44868         if(!this.castInt){
44869             return;
44870         }
44871         
44872         var v = this.parseValue(this.getRawValue());
44873         
44874         if(v || v == 0){
44875             this.setValue(v);
44876         }
44877     },
44878     
44879     onBlur : function()
44880     {
44881         this.beforeBlur();
44882         
44883         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44884             //this.el.removeClass(this.focusClass);
44885         }
44886         
44887         this.hasFocus = false;
44888         
44889         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44890             this.validate();
44891         }
44892         
44893         var v = this.getValue();
44894         
44895         if(String(v) !== String(this.startValue)){
44896             this.fireEvent('change', this, v, this.startValue);
44897         }
44898         
44899         this.fireEvent("blur", this);
44900     },
44901     
44902     inputEl : function()
44903     {
44904         return this.el.select('.roo-money-amount-input', true).first();
44905     },
44906     
44907     currencyEl : function()
44908     {
44909         return this.el.select('.roo-money-currency-input', true).first();
44910     },
44911     
44912     hiddenEl : function()
44913     {
44914         return this.el.select('input.hidden-number-input',true).first();
44915     }
44916     
44917 });/**
44918  * @class Roo.bootstrap.BezierSignature
44919  * @extends Roo.bootstrap.Component
44920  * Bootstrap BezierSignature class
44921  * This script refer to:
44922  *    Title: Signature Pad
44923  *    Author: szimek
44924  *    Availability: https://github.com/szimek/signature_pad
44925  *
44926  * @constructor
44927  * Create a new BezierSignature
44928  * @param {Object} config The config object
44929  */
44930
44931 Roo.bootstrap.BezierSignature = function(config){
44932     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44933     this.addEvents({
44934         "resize" : true
44935     });
44936 };
44937
44938 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44939 {
44940      
44941     curve_data: [],
44942     
44943     is_empty: true,
44944     
44945     mouse_btn_down: true,
44946     
44947     /**
44948      * @cfg {int} canvas height
44949      */
44950     canvas_height: '200px',
44951     
44952     /**
44953      * @cfg {float|function} Radius of a single dot.
44954      */ 
44955     dot_size: false,
44956     
44957     /**
44958      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44959      */
44960     min_width: 0.5,
44961     
44962     /**
44963      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44964      */
44965     max_width: 2.5,
44966     
44967     /**
44968      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44969      */
44970     throttle: 16,
44971     
44972     /**
44973      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44974      */
44975     min_distance: 5,
44976     
44977     /**
44978      * @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.
44979      */
44980     bg_color: 'rgba(0, 0, 0, 0)',
44981     
44982     /**
44983      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44984      */
44985     dot_color: 'black',
44986     
44987     /**
44988      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44989      */ 
44990     velocity_filter_weight: 0.7,
44991     
44992     /**
44993      * @cfg {function} Callback when stroke begin. 
44994      */
44995     onBegin: false,
44996     
44997     /**
44998      * @cfg {function} Callback when stroke end.
44999      */
45000     onEnd: false,
45001     
45002     getAutoCreate : function()
45003     {
45004         var cls = 'roo-signature column';
45005         
45006         if(this.cls){
45007             cls += ' ' + this.cls;
45008         }
45009         
45010         var col_sizes = [
45011             'lg',
45012             'md',
45013             'sm',
45014             'xs'
45015         ];
45016         
45017         for(var i = 0; i < col_sizes.length; i++) {
45018             if(this[col_sizes[i]]) {
45019                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45020             }
45021         }
45022         
45023         var cfg = {
45024             tag: 'div',
45025             cls: cls,
45026             cn: [
45027                 {
45028                     tag: 'div',
45029                     cls: 'roo-signature-body',
45030                     cn: [
45031                         {
45032                             tag: 'canvas',
45033                             cls: 'roo-signature-body-canvas',
45034                             height: this.canvas_height,
45035                             width: this.canvas_width
45036                         }
45037                     ]
45038                 },
45039                 {
45040                     tag: 'input',
45041                     type: 'file',
45042                     style: 'display: none'
45043                 }
45044             ]
45045         };
45046         
45047         return cfg;
45048     },
45049     
45050     initEvents: function() 
45051     {
45052         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45053         
45054         var canvas = this.canvasEl();
45055         
45056         // mouse && touch event swapping...
45057         canvas.dom.style.touchAction = 'none';
45058         canvas.dom.style.msTouchAction = 'none';
45059         
45060         this.mouse_btn_down = false;
45061         canvas.on('mousedown', this._handleMouseDown, this);
45062         canvas.on('mousemove', this._handleMouseMove, this);
45063         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45064         
45065         if (window.PointerEvent) {
45066             canvas.on('pointerdown', this._handleMouseDown, this);
45067             canvas.on('pointermove', this._handleMouseMove, this);
45068             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45069         }
45070         
45071         if ('ontouchstart' in window) {
45072             canvas.on('touchstart', this._handleTouchStart, this);
45073             canvas.on('touchmove', this._handleTouchMove, this);
45074             canvas.on('touchend', this._handleTouchEnd, this);
45075         }
45076         
45077         Roo.EventManager.onWindowResize(this.resize, this, true);
45078         
45079         // file input event
45080         this.fileEl().on('change', this.uploadImage, this);
45081         
45082         this.clear();
45083         
45084         this.resize();
45085     },
45086     
45087     resize: function(){
45088         
45089         var canvas = this.canvasEl().dom;
45090         var ctx = this.canvasElCtx();
45091         var img_data = false;
45092         
45093         if(canvas.width > 0) {
45094             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45095         }
45096         // setting canvas width will clean img data
45097         canvas.width = 0;
45098         
45099         var style = window.getComputedStyle ? 
45100             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45101             
45102         var padding_left = parseInt(style.paddingLeft) || 0;
45103         var padding_right = parseInt(style.paddingRight) || 0;
45104         
45105         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45106         
45107         if(img_data) {
45108             ctx.putImageData(img_data, 0, 0);
45109         }
45110     },
45111     
45112     _handleMouseDown: function(e)
45113     {
45114         if (e.browserEvent.which === 1) {
45115             this.mouse_btn_down = true;
45116             this.strokeBegin(e);
45117         }
45118     },
45119     
45120     _handleMouseMove: function (e)
45121     {
45122         if (this.mouse_btn_down) {
45123             this.strokeMoveUpdate(e);
45124         }
45125     },
45126     
45127     _handleMouseUp: function (e)
45128     {
45129         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45130             this.mouse_btn_down = false;
45131             this.strokeEnd(e);
45132         }
45133     },
45134     
45135     _handleTouchStart: function (e) {
45136         
45137         e.preventDefault();
45138         if (e.browserEvent.targetTouches.length === 1) {
45139             // var touch = e.browserEvent.changedTouches[0];
45140             // this.strokeBegin(touch);
45141             
45142              this.strokeBegin(e); // assume e catching the correct xy...
45143         }
45144     },
45145     
45146     _handleTouchMove: function (e) {
45147         e.preventDefault();
45148         // var touch = event.targetTouches[0];
45149         // _this._strokeMoveUpdate(touch);
45150         this.strokeMoveUpdate(e);
45151     },
45152     
45153     _handleTouchEnd: function (e) {
45154         var wasCanvasTouched = e.target === this.canvasEl().dom;
45155         if (wasCanvasTouched) {
45156             e.preventDefault();
45157             // var touch = event.changedTouches[0];
45158             // _this._strokeEnd(touch);
45159             this.strokeEnd(e);
45160         }
45161     },
45162     
45163     reset: function () {
45164         this._lastPoints = [];
45165         this._lastVelocity = 0;
45166         this._lastWidth = (this.min_width + this.max_width) / 2;
45167         this.canvasElCtx().fillStyle = this.dot_color;
45168     },
45169     
45170     strokeMoveUpdate: function(e)
45171     {
45172         this.strokeUpdate(e);
45173         
45174         if (this.throttle) {
45175             this.throttleStroke(this.strokeUpdate, this.throttle);
45176         }
45177         else {
45178             this.strokeUpdate(e);
45179         }
45180     },
45181     
45182     strokeBegin: function(e)
45183     {
45184         var newPointGroup = {
45185             color: this.dot_color,
45186             points: []
45187         };
45188         
45189         if (typeof this.onBegin === 'function') {
45190             this.onBegin(e);
45191         }
45192         
45193         this.curve_data.push(newPointGroup);
45194         this.reset();
45195         this.strokeUpdate(e);
45196     },
45197     
45198     strokeUpdate: function(e)
45199     {
45200         var rect = this.canvasEl().dom.getBoundingClientRect();
45201         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45202         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45203         var lastPoints = lastPointGroup.points;
45204         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45205         var isLastPointTooClose = lastPoint
45206             ? point.distanceTo(lastPoint) <= this.min_distance
45207             : false;
45208         var color = lastPointGroup.color;
45209         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45210             var curve = this.addPoint(point);
45211             if (!lastPoint) {
45212                 this.drawDot({color: color, point: point});
45213             }
45214             else if (curve) {
45215                 this.drawCurve({color: color, curve: curve});
45216             }
45217             lastPoints.push({
45218                 time: point.time,
45219                 x: point.x,
45220                 y: point.y
45221             });
45222         }
45223     },
45224     
45225     strokeEnd: function(e)
45226     {
45227         this.strokeUpdate(e);
45228         if (typeof this.onEnd === 'function') {
45229             this.onEnd(e);
45230         }
45231     },
45232     
45233     addPoint:  function (point) {
45234         var _lastPoints = this._lastPoints;
45235         _lastPoints.push(point);
45236         if (_lastPoints.length > 2) {
45237             if (_lastPoints.length === 3) {
45238                 _lastPoints.unshift(_lastPoints[0]);
45239             }
45240             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45241             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45242             _lastPoints.shift();
45243             return curve;
45244         }
45245         return null;
45246     },
45247     
45248     calculateCurveWidths: function (startPoint, endPoint) {
45249         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45250             (1 - this.velocity_filter_weight) * this._lastVelocity;
45251
45252         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45253         var widths = {
45254             end: newWidth,
45255             start: this._lastWidth
45256         };
45257         
45258         this._lastVelocity = velocity;
45259         this._lastWidth = newWidth;
45260         return widths;
45261     },
45262     
45263     drawDot: function (_a) {
45264         var color = _a.color, point = _a.point;
45265         var ctx = this.canvasElCtx();
45266         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45267         ctx.beginPath();
45268         this.drawCurveSegment(point.x, point.y, width);
45269         ctx.closePath();
45270         ctx.fillStyle = color;
45271         ctx.fill();
45272     },
45273     
45274     drawCurve: function (_a) {
45275         var color = _a.color, curve = _a.curve;
45276         var ctx = this.canvasElCtx();
45277         var widthDelta = curve.endWidth - curve.startWidth;
45278         var drawSteps = Math.floor(curve.length()) * 2;
45279         ctx.beginPath();
45280         ctx.fillStyle = color;
45281         for (var i = 0; i < drawSteps; i += 1) {
45282         var t = i / drawSteps;
45283         var tt = t * t;
45284         var ttt = tt * t;
45285         var u = 1 - t;
45286         var uu = u * u;
45287         var uuu = uu * u;
45288         var x = uuu * curve.startPoint.x;
45289         x += 3 * uu * t * curve.control1.x;
45290         x += 3 * u * tt * curve.control2.x;
45291         x += ttt * curve.endPoint.x;
45292         var y = uuu * curve.startPoint.y;
45293         y += 3 * uu * t * curve.control1.y;
45294         y += 3 * u * tt * curve.control2.y;
45295         y += ttt * curve.endPoint.y;
45296         var width = curve.startWidth + ttt * widthDelta;
45297         this.drawCurveSegment(x, y, width);
45298         }
45299         ctx.closePath();
45300         ctx.fill();
45301     },
45302     
45303     drawCurveSegment: function (x, y, width) {
45304         var ctx = this.canvasElCtx();
45305         ctx.moveTo(x, y);
45306         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45307         this.is_empty = false;
45308     },
45309     
45310     clear: function()
45311     {
45312         var ctx = this.canvasElCtx();
45313         var canvas = this.canvasEl().dom;
45314         ctx.fillStyle = this.bg_color;
45315         ctx.clearRect(0, 0, canvas.width, canvas.height);
45316         ctx.fillRect(0, 0, canvas.width, canvas.height);
45317         this.curve_data = [];
45318         this.reset();
45319         this.is_empty = true;
45320     },
45321     
45322     fileEl: function()
45323     {
45324         return  this.el.select('input',true).first();
45325     },
45326     
45327     canvasEl: function()
45328     {
45329         return this.el.select('canvas',true).first();
45330     },
45331     
45332     canvasElCtx: function()
45333     {
45334         return this.el.select('canvas',true).first().dom.getContext('2d');
45335     },
45336     
45337     getImage: function(type)
45338     {
45339         if(this.is_empty) {
45340             return false;
45341         }
45342         
45343         // encryption ?
45344         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45345     },
45346     
45347     drawFromImage: function(img_src)
45348     {
45349         var img = new Image();
45350         
45351         img.onload = function(){
45352             this.canvasElCtx().drawImage(img, 0, 0);
45353         }.bind(this);
45354         
45355         img.src = img_src;
45356         
45357         this.is_empty = false;
45358     },
45359     
45360     selectImage: function()
45361     {
45362         this.fileEl().dom.click();
45363     },
45364     
45365     uploadImage: function(e)
45366     {
45367         var reader = new FileReader();
45368         
45369         reader.onload = function(e){
45370             var img = new Image();
45371             img.onload = function(){
45372                 this.reset();
45373                 this.canvasElCtx().drawImage(img, 0, 0);
45374             }.bind(this);
45375             img.src = e.target.result;
45376         }.bind(this);
45377         
45378         reader.readAsDataURL(e.target.files[0]);
45379     },
45380     
45381     // Bezier Point Constructor
45382     Point: (function () {
45383         function Point(x, y, time) {
45384             this.x = x;
45385             this.y = y;
45386             this.time = time || Date.now();
45387         }
45388         Point.prototype.distanceTo = function (start) {
45389             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45390         };
45391         Point.prototype.equals = function (other) {
45392             return this.x === other.x && this.y === other.y && this.time === other.time;
45393         };
45394         Point.prototype.velocityFrom = function (start) {
45395             return this.time !== start.time
45396             ? this.distanceTo(start) / (this.time - start.time)
45397             : 0;
45398         };
45399         return Point;
45400     }()),
45401     
45402     
45403     // Bezier Constructor
45404     Bezier: (function () {
45405         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45406             this.startPoint = startPoint;
45407             this.control2 = control2;
45408             this.control1 = control1;
45409             this.endPoint = endPoint;
45410             this.startWidth = startWidth;
45411             this.endWidth = endWidth;
45412         }
45413         Bezier.fromPoints = function (points, widths, scope) {
45414             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45415             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45416             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45417         };
45418         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45419             var dx1 = s1.x - s2.x;
45420             var dy1 = s1.y - s2.y;
45421             var dx2 = s2.x - s3.x;
45422             var dy2 = s2.y - s3.y;
45423             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45424             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45425             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45426             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45427             var dxm = m1.x - m2.x;
45428             var dym = m1.y - m2.y;
45429             var k = l2 / (l1 + l2);
45430             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45431             var tx = s2.x - cm.x;
45432             var ty = s2.y - cm.y;
45433             return {
45434                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45435                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45436             };
45437         };
45438         Bezier.prototype.length = function () {
45439             var steps = 10;
45440             var length = 0;
45441             var px;
45442             var py;
45443             for (var i = 0; i <= steps; i += 1) {
45444                 var t = i / steps;
45445                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45446                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45447                 if (i > 0) {
45448                     var xdiff = cx - px;
45449                     var ydiff = cy - py;
45450                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45451                 }
45452                 px = cx;
45453                 py = cy;
45454             }
45455             return length;
45456         };
45457         Bezier.prototype.point = function (t, start, c1, c2, end) {
45458             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45459             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45460             + (3.0 * c2 * (1.0 - t) * t * t)
45461             + (end * t * t * t);
45462         };
45463         return Bezier;
45464     }()),
45465     
45466     throttleStroke: function(fn, wait) {
45467       if (wait === void 0) { wait = 250; }
45468       var previous = 0;
45469       var timeout = null;
45470       var result;
45471       var storedContext;
45472       var storedArgs;
45473       var later = function () {
45474           previous = Date.now();
45475           timeout = null;
45476           result = fn.apply(storedContext, storedArgs);
45477           if (!timeout) {
45478               storedContext = null;
45479               storedArgs = [];
45480           }
45481       };
45482       return function wrapper() {
45483           var args = [];
45484           for (var _i = 0; _i < arguments.length; _i++) {
45485               args[_i] = arguments[_i];
45486           }
45487           var now = Date.now();
45488           var remaining = wait - (now - previous);
45489           storedContext = this;
45490           storedArgs = args;
45491           if (remaining <= 0 || remaining > wait) {
45492               if (timeout) {
45493                   clearTimeout(timeout);
45494                   timeout = null;
45495               }
45496               previous = now;
45497               result = fn.apply(storedContext, storedArgs);
45498               if (!timeout) {
45499                   storedContext = null;
45500                   storedArgs = [];
45501               }
45502           }
45503           else if (!timeout) {
45504               timeout = window.setTimeout(later, remaining);
45505           }
45506           return result;
45507       };
45508   }
45509   
45510 });
45511
45512  
45513
45514