roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     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  * Bootstrap Link Class
3272  * @cfg {String} alt image alternative text
3273  * @cfg {String} href a tag href
3274  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3275  * @cfg {String} html the content of the link.
3276  * @cfg {String} anchor name for the anchor link
3277  * @cfg {String} fa - favicon
3278
3279  * @cfg {Boolean} preventDefault (true | false) default false
3280
3281  * 
3282  * @constructor
3283  * Create a new Input
3284  * @param {Object} config The config object
3285  */
3286
3287 Roo.bootstrap.Link = function(config){
3288     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3289     
3290     this.addEvents({
3291         // img events
3292         /**
3293          * @event click
3294          * The img click event for the img.
3295          * @param {Roo.EventObject} e
3296          */
3297         "click" : true
3298     });
3299 };
3300
3301 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3302     
3303     href: false,
3304     target: false,
3305     preventDefault: false,
3306     anchor : false,
3307     alt : false,
3308     fa: false,
3309
3310
3311     getAutoCreate : function()
3312     {
3313         var html = this.html || '';
3314         
3315         if (this.fa !== false) {
3316             html = '<i class="fa fa-' + this.fa + '"></i>';
3317         }
3318         var cfg = {
3319             tag: 'a'
3320         };
3321         // anchor's do not require html/href...
3322         if (this.anchor === false) {
3323             cfg.html = html;
3324             cfg.href = this.href || '#';
3325         } else {
3326             cfg.name = this.anchor;
3327             if (this.html !== false || this.fa !== false) {
3328                 cfg.html = html;
3329             }
3330             if (this.href !== false) {
3331                 cfg.href = this.href;
3332             }
3333         }
3334         
3335         if(this.alt !== false){
3336             cfg.alt = this.alt;
3337         }
3338         
3339         
3340         if(this.target !== false) {
3341             cfg.target = this.target;
3342         }
3343         
3344         return cfg;
3345     },
3346     
3347     initEvents: function() {
3348         
3349         if(!this.href || this.preventDefault){
3350             this.el.on('click', this.onClick, this);
3351         }
3352     },
3353     
3354     onClick : function(e)
3355     {
3356         if(this.preventDefault){
3357             e.preventDefault();
3358         }
3359         //Roo.log('img onclick');
3360         this.fireEvent('click', this, e);
3361     }
3362    
3363 });
3364
3365  /*
3366  * - LGPL
3367  *
3368  * header
3369  * 
3370  */
3371
3372 /**
3373  * @class Roo.bootstrap.Header
3374  * @extends Roo.bootstrap.Component
3375  * Bootstrap Header class
3376  * @cfg {String} html content of header
3377  * @cfg {Number} level (1|2|3|4|5|6) default 1
3378  * 
3379  * @constructor
3380  * Create a new Header
3381  * @param {Object} config The config object
3382  */
3383
3384
3385 Roo.bootstrap.Header  = function(config){
3386     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3387 };
3388
3389 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3390     
3391     //href : false,
3392     html : false,
3393     level : 1,
3394     
3395     
3396     
3397     getAutoCreate : function(){
3398         
3399         
3400         
3401         var cfg = {
3402             tag: 'h' + (1 *this.level),
3403             html: this.html || ''
3404         } ;
3405         
3406         return cfg;
3407     }
3408    
3409 });
3410
3411  
3412
3413  /*
3414  * Based on:
3415  * Ext JS Library 1.1.1
3416  * Copyright(c) 2006-2007, Ext JS, LLC.
3417  *
3418  * Originally Released Under LGPL - original licence link has changed is not relivant.
3419  *
3420  * Fork - LGPL
3421  * <script type="text/javascript">
3422  */
3423  
3424 /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3427  * @singleton
3428  */
3429 Roo.bootstrap.MenuMgr = function(){
3430    var menus, active, groups = {}, attached = false, lastShow = new Date();
3431
3432    // private - called when first menu is created
3433    function init(){
3434        menus = {};
3435        active = new Roo.util.MixedCollection();
3436        Roo.get(document).addKeyListener(27, function(){
3437            if(active.length > 0){
3438                hideAll();
3439            }
3440        });
3441    }
3442
3443    // private
3444    function hideAll(){
3445        if(active && active.length > 0){
3446            var c = active.clone();
3447            c.each(function(m){
3448                m.hide();
3449            });
3450        }
3451    }
3452
3453    // private
3454    function onHide(m){
3455        active.remove(m);
3456        if(active.length < 1){
3457            Roo.get(document).un("mouseup", onMouseDown);
3458             
3459            attached = false;
3460        }
3461    }
3462
3463    // private
3464    function onShow(m){
3465        var last = active.last();
3466        lastShow = new Date();
3467        active.add(m);
3468        if(!attached){
3469           Roo.get(document).on("mouseup", onMouseDown);
3470            
3471            attached = true;
3472        }
3473        if(m.parentMenu){
3474           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3475           m.parentMenu.activeChild = m;
3476        }else if(last && last.isVisible()){
3477           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3478        }
3479    }
3480
3481    // private
3482    function onBeforeHide(m){
3483        if(m.activeChild){
3484            m.activeChild.hide();
3485        }
3486        if(m.autoHideTimer){
3487            clearTimeout(m.autoHideTimer);
3488            delete m.autoHideTimer;
3489        }
3490    }
3491
3492    // private
3493    function onBeforeShow(m){
3494        var pm = m.parentMenu;
3495        if(!pm && !m.allowOtherMenus){
3496            hideAll();
3497        }else if(pm && pm.activeChild && active != m){
3498            pm.activeChild.hide();
3499        }
3500    }
3501
3502    // private this should really trigger on mouseup..
3503    function onMouseDown(e){
3504         Roo.log("on Mouse Up");
3505         
3506         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3507             Roo.log("MenuManager hideAll");
3508             hideAll();
3509             e.stopEvent();
3510         }
3511         
3512         
3513    }
3514
3515    // private
3516    function onBeforeCheck(mi, state){
3517        if(state){
3518            var g = groups[mi.group];
3519            for(var i = 0, l = g.length; i < l; i++){
3520                if(g[i] != mi){
3521                    g[i].setChecked(false);
3522                }
3523            }
3524        }
3525    }
3526
3527    return {
3528
3529        /**
3530         * Hides all menus that are currently visible
3531         */
3532        hideAll : function(){
3533             hideAll();  
3534        },
3535
3536        // private
3537        register : function(menu){
3538            if(!menus){
3539                init();
3540            }
3541            menus[menu.id] = menu;
3542            menu.on("beforehide", onBeforeHide);
3543            menu.on("hide", onHide);
3544            menu.on("beforeshow", onBeforeShow);
3545            menu.on("show", onShow);
3546            var g = menu.group;
3547            if(g && menu.events["checkchange"]){
3548                if(!groups[g]){
3549                    groups[g] = [];
3550                }
3551                groups[g].push(menu);
3552                menu.on("checkchange", onCheck);
3553            }
3554        },
3555
3556         /**
3557          * Returns a {@link Roo.menu.Menu} object
3558          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3559          * be used to generate and return a new Menu instance.
3560          */
3561        get : function(menu){
3562            if(typeof menu == "string"){ // menu id
3563                return menus[menu];
3564            }else if(menu.events){  // menu instance
3565                return menu;
3566            }
3567            /*else if(typeof menu.length == 'number'){ // array of menu items?
3568                return new Roo.bootstrap.Menu({items:menu});
3569            }else{ // otherwise, must be a config
3570                return new Roo.bootstrap.Menu(menu);
3571            }
3572            */
3573            return false;
3574        },
3575
3576        // private
3577        unregister : function(menu){
3578            delete menus[menu.id];
3579            menu.un("beforehide", onBeforeHide);
3580            menu.un("hide", onHide);
3581            menu.un("beforeshow", onBeforeShow);
3582            menu.un("show", onShow);
3583            var g = menu.group;
3584            if(g && menu.events["checkchange"]){
3585                groups[g].remove(menu);
3586                menu.un("checkchange", onCheck);
3587            }
3588        },
3589
3590        // private
3591        registerCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                if(!groups[g]){
3595                    groups[g] = [];
3596                }
3597                groups[g].push(menuItem);
3598                menuItem.on("beforecheckchange", onBeforeCheck);
3599            }
3600        },
3601
3602        // private
3603        unregisterCheckable : function(menuItem){
3604            var g = menuItem.group;
3605            if(g){
3606                groups[g].remove(menuItem);
3607                menuItem.un("beforecheckchange", onBeforeCheck);
3608            }
3609        }
3610    };
3611 }();/*
3612  * - LGPL
3613  *
3614  * menu
3615  * 
3616  */
3617
3618 /**
3619  * @class Roo.bootstrap.Menu
3620  * @extends Roo.bootstrap.Component
3621  * Bootstrap Menu class - container for MenuItems
3622  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3623  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3624  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3625  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3626   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3627   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3628  
3629  * @constructor
3630  * Create a new Menu
3631  * @param {Object} config The config object
3632  */
3633
3634
3635 Roo.bootstrap.Menu = function(config){
3636     
3637     if (config.type == 'treeview') {
3638         // normally menu's are drawn attached to the document to handle layering etc..
3639         // however treeview (used by the docs menu is drawn into the parent element)
3640         this.container_method = 'getChildContainer'; 
3641     }
3642     
3643     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3644     if (this.registerMenu && this.type != 'treeview')  {
3645         Roo.bootstrap.MenuMgr.register(this);
3646     }
3647     
3648     
3649     this.addEvents({
3650         /**
3651          * @event beforeshow
3652          * Fires before this menu is displayed (return false to block)
3653          * @param {Roo.menu.Menu} this
3654          */
3655         beforeshow : true,
3656         /**
3657          * @event beforehide
3658          * Fires before this menu is hidden (return false to block)
3659          * @param {Roo.menu.Menu} this
3660          */
3661         beforehide : true,
3662         /**
3663          * @event show
3664          * Fires after this menu is displayed
3665          * @param {Roo.menu.Menu} this
3666          */
3667         show : true,
3668         /**
3669          * @event hide
3670          * Fires after this menu is hidden
3671          * @param {Roo.menu.Menu} this
3672          */
3673         hide : true,
3674         /**
3675          * @event click
3676          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3677          * @param {Roo.menu.Menu} this
3678          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3679          * @param {Roo.EventObject} e
3680          */
3681         click : true,
3682         /**
3683          * @event mouseover
3684          * Fires when the mouse is hovering over this menu
3685          * @param {Roo.menu.Menu} this
3686          * @param {Roo.EventObject} e
3687          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688          */
3689         mouseover : true,
3690         /**
3691          * @event mouseout
3692          * Fires when the mouse exits this menu
3693          * @param {Roo.menu.Menu} this
3694          * @param {Roo.EventObject} e
3695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3696          */
3697         mouseout : true,
3698         /**
3699          * @event itemclick
3700          * Fires when a menu item contained in this menu is clicked
3701          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3702          * @param {Roo.EventObject} e
3703          */
3704         itemclick: true
3705     });
3706     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3707 };
3708
3709 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3710     
3711    /// html : false,
3712    
3713     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3714     type: false,
3715     /**
3716      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717      */
3718     registerMenu : true,
3719     
3720     menuItems :false, // stores the menu items..
3721     
3722     hidden:true,
3723         
3724     parentMenu : false,
3725     
3726     stopEvent : true,
3727     
3728     isLink : false,
3729     
3730     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731     
3732     hideTrigger : false,
3733     
3734     align : 'tl-bl?',
3735     
3736     
3737     getChildContainer : function() {
3738         return this.el;  
3739     },
3740     
3741     getAutoCreate : function(){
3742          
3743         //if (['right'].indexOf(this.align)!==-1) {
3744         //    cfg.cn[1].cls += ' pull-right'
3745         //}
3746          
3747         var cfg = {
3748             tag : 'ul',
3749             cls : 'dropdown-menu shadow' ,
3750             style : 'z-index:1000'
3751             
3752         };
3753         
3754         if (this.type === 'submenu') {
3755             cfg.cls = 'submenu active';
3756         }
3757         if (this.type === 'treeview') {
3758             cfg.cls = 'treeview-menu';
3759         }
3760         
3761         return cfg;
3762     },
3763     initEvents : function() {
3764         
3765        // Roo.log("ADD event");
3766        // Roo.log(this.triggerEl.dom);
3767         if (this.triggerEl) {
3768             
3769             this.triggerEl.on('click', this.onTriggerClick, this);
3770             
3771             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772             
3773             if (!this.hideTrigger) {
3774                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3775                     // dropdown toggle on the 'a' in BS4?
3776                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777                 } else {
3778                     this.triggerEl.addClass('dropdown-toggle');
3779                 }
3780             }
3781         }
3782         
3783         if (Roo.isTouch) {
3784             this.el.on('touchstart'  , this.onTouch, this);
3785         }
3786         this.el.on('click' , this.onClick, this);
3787
3788         this.el.on("mouseover", this.onMouseOver, this);
3789         this.el.on("mouseout", this.onMouseOut, this);
3790         
3791     },
3792     
3793     findTargetItem : function(e)
3794     {
3795         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3796         if(!t){
3797             return false;
3798         }
3799         //Roo.log(t);         Roo.log(t.id);
3800         if(t && t.id){
3801             //Roo.log(this.menuitems);
3802             return this.menuitems.get(t.id);
3803             
3804             //return this.items.get(t.menuItemId);
3805         }
3806         
3807         return false;
3808     },
3809     
3810     onTouch : function(e) 
3811     {
3812         Roo.log("menu.onTouch");
3813         //e.stopEvent(); this make the user popdown broken
3814         this.onClick(e);
3815     },
3816     
3817     onClick : function(e)
3818     {
3819         Roo.log("menu.onClick");
3820         
3821         var t = this.findTargetItem(e);
3822         if(!t || t.isContainer){
3823             return;
3824         }
3825         Roo.log(e);
3826         /*
3827         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3828             if(t == this.activeItem && t.shouldDeactivate(e)){
3829                 this.activeItem.deactivate();
3830                 delete this.activeItem;
3831                 return;
3832             }
3833             if(t.canActivate){
3834                 this.setActiveItem(t, true);
3835             }
3836             return;
3837             
3838             
3839         }
3840         */
3841        
3842         Roo.log('pass click event');
3843         
3844         t.onClick(e);
3845         
3846         this.fireEvent("click", this, t, e);
3847         
3848         var _this = this;
3849         
3850         if(!t.href.length || t.href == '#'){
3851             (function() { _this.hide(); }).defer(100);
3852         }
3853         
3854     },
3855     
3856     onMouseOver : function(e){
3857         var t  = this.findTargetItem(e);
3858         //Roo.log(t);
3859         //if(t){
3860         //    if(t.canActivate && !t.disabled){
3861         //        this.setActiveItem(t, true);
3862         //    }
3863         //}
3864         
3865         this.fireEvent("mouseover", this, e, t);
3866     },
3867     isVisible : function(){
3868         return !this.hidden;
3869     },
3870     onMouseOut : function(e){
3871         var t  = this.findTargetItem(e);
3872         
3873         //if(t ){
3874         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3875         //        this.activeItem.deactivate();
3876         //        delete this.activeItem;
3877         //    }
3878         //}
3879         this.fireEvent("mouseout", this, e, t);
3880     },
3881     
3882     
3883     /**
3884      * Displays this menu relative to another element
3885      * @param {String/HTMLElement/Roo.Element} element The element to align to
3886      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3887      * the element (defaults to this.defaultAlign)
3888      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889      */
3890     show : function(el, pos, parentMenu)
3891     {
3892         if (false === this.fireEvent("beforeshow", this)) {
3893             Roo.log("show canceled");
3894             return;
3895         }
3896         this.parentMenu = parentMenu;
3897         if(!this.el){
3898             this.render();
3899         }
3900         this.el.addClass('show'); // show otherwise we do not know how big we are..
3901          
3902         var xy = this.el.getAlignToXY(el, pos);
3903         
3904         // bl-tl << left align  below
3905         // tl-bl << left align 
3906         
3907         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3908             // if it goes to far to the right.. -> align left.
3909             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3910         }
3911         if(xy[0] < 0){
3912             // was left align - go right?
3913             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3914         }
3915         
3916         // goes down the bottom
3917         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918            xy[1]  < 0 ){
3919             var a = this.align.replace('?', '').split('-');
3920             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3921             
3922         }
3923         
3924         this.showAt(  xy , parentMenu, false);
3925     },
3926      /**
3927      * Displays this menu at a specific xy position
3928      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3929      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930      */
3931     showAt : function(xy, parentMenu, /* private: */_e){
3932         this.parentMenu = parentMenu;
3933         if(!this.el){
3934             this.render();
3935         }
3936         if(_e !== false){
3937             this.fireEvent("beforeshow", this);
3938             //xy = this.el.adjustForConstraints(xy);
3939         }
3940         
3941         //this.el.show();
3942         this.hideMenuItems();
3943         this.hidden = false;
3944         if (this.triggerEl) {
3945             this.triggerEl.addClass('open');
3946         }
3947         
3948         this.el.addClass('show');
3949         
3950         
3951         
3952         // reassign x when hitting right
3953         
3954         // reassign y when hitting bottom
3955         
3956         // but the list may align on trigger left or trigger top... should it be a properity?
3957         
3958         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3959             this.el.setXY(xy);
3960         }
3961         
3962         this.focus();
3963         this.fireEvent("show", this);
3964     },
3965     
3966     focus : function(){
3967         return;
3968         if(!this.hidden){
3969             this.doFocus.defer(50, this);
3970         }
3971     },
3972
3973     doFocus : function(){
3974         if(!this.hidden){
3975             this.focusEl.focus();
3976         }
3977     },
3978
3979     /**
3980      * Hides this menu and optionally all parent menus
3981      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982      */
3983     hide : function(deep)
3984     {
3985         if (false === this.fireEvent("beforehide", this)) {
3986             Roo.log("hide canceled");
3987             return;
3988         }
3989         this.hideMenuItems();
3990         if(this.el && this.isVisible()){
3991            
3992             if(this.activeItem){
3993                 this.activeItem.deactivate();
3994                 this.activeItem = null;
3995             }
3996             if (this.triggerEl) {
3997                 this.triggerEl.removeClass('open');
3998             }
3999             
4000             this.el.removeClass('show');
4001             this.hidden = true;
4002             this.fireEvent("hide", this);
4003         }
4004         if(deep === true && this.parentMenu){
4005             this.parentMenu.hide(true);
4006         }
4007     },
4008     
4009     onTriggerClick : function(e)
4010     {
4011         Roo.log('trigger click');
4012         
4013         var target = e.getTarget();
4014         
4015         Roo.log(target.nodeName.toLowerCase());
4016         
4017         if(target.nodeName.toLowerCase() === 'i'){
4018             e.preventDefault();
4019         }
4020         
4021     },
4022     
4023     onTriggerPress  : function(e)
4024     {
4025         Roo.log('trigger press');
4026         //Roo.log(e.getTarget());
4027        // Roo.log(this.triggerEl.dom);
4028        
4029         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4030         var pel = Roo.get(e.getTarget());
4031         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4032             Roo.log('is treeview or dropdown?');
4033             return;
4034         }
4035         
4036         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4037             return;
4038         }
4039         
4040         if (this.isVisible()) {
4041             Roo.log('hide');
4042             this.hide();
4043         } else {
4044             Roo.log('show');
4045             
4046             this.show(this.triggerEl, this.align, false);
4047         }
4048         
4049         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4050             e.stopEvent();
4051         }
4052         
4053     },
4054        
4055     
4056     hideMenuItems : function()
4057     {
4058         Roo.log("hide Menu Items");
4059         if (!this.el) { 
4060             return;
4061         }
4062         
4063         this.el.select('.open',true).each(function(aa) {
4064             
4065             aa.removeClass('open');
4066          
4067         });
4068     },
4069     addxtypeChild : function (tree, cntr) {
4070         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071           
4072         this.menuitems.add(comp);
4073         return comp;
4074
4075     },
4076     getEl : function()
4077     {
4078         Roo.log(this.el);
4079         return this.el;
4080     },
4081     
4082     clear : function()
4083     {
4084         this.getEl().dom.innerHTML = '';
4085         this.menuitems.clear();
4086     }
4087 });
4088
4089  
4090  /*
4091  * - LGPL
4092  *
4093  * menu item
4094  * 
4095  */
4096
4097
4098 /**
4099  * @class Roo.bootstrap.MenuItem
4100  * @extends Roo.bootstrap.Component
4101  * Bootstrap MenuItem class
4102  * @cfg {String} html the menu label
4103  * @cfg {String} href the link
4104  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4105  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4106  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4107  * @cfg {String} fa favicon to show on left of menu item.
4108  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109  * 
4110  * 
4111  * @constructor
4112  * Create a new MenuItem
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.MenuItem = function(config){
4118     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4119     this.addEvents({
4120         // raw events
4121         /**
4122          * @event click
4123          * The raw click event for the entire grid.
4124          * @param {Roo.bootstrap.MenuItem} this
4125          * @param {Roo.EventObject} e
4126          */
4127         "click" : true
4128     });
4129 };
4130
4131 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4132     
4133     href : false,
4134     html : false,
4135     preventDefault: false,
4136     isContainer : false,
4137     active : false,
4138     fa: false,
4139     
4140     getAutoCreate : function(){
4141         
4142         if(this.isContainer){
4143             return {
4144                 tag: 'li',
4145                 cls: 'dropdown-menu-item '
4146             };
4147         }
4148         var ctag = {
4149             tag: 'span',
4150             html: 'Link'
4151         };
4152         
4153         var anc = {
4154             tag : 'a',
4155             cls : 'dropdown-item',
4156             href : '#',
4157             cn : [  ]
4158         };
4159         
4160         if (this.fa !== false) {
4161             anc.cn.push({
4162                 tag : 'i',
4163                 cls : 'fa fa-' + this.fa
4164             });
4165         }
4166         
4167         anc.cn.push(ctag);
4168         
4169         
4170         var cfg= {
4171             tag: 'li',
4172             cls: 'dropdown-menu-item',
4173             cn: [ anc ]
4174         };
4175         if (this.parent().type == 'treeview') {
4176             cfg.cls = 'treeview-menu';
4177         }
4178         if (this.active) {
4179             cfg.cls += ' active';
4180         }
4181         
4182         
4183         
4184         anc.href = this.href || cfg.cn[0].href ;
4185         ctag.html = this.html || cfg.cn[0].html ;
4186         return cfg;
4187     },
4188     
4189     initEvents: function()
4190     {
4191         if (this.parent().type == 'treeview') {
4192             this.el.select('a').on('click', this.onClick, this);
4193         }
4194         
4195         if (this.menu) {
4196             this.menu.parentType = this.xtype;
4197             this.menu.triggerEl = this.el;
4198             this.menu = this.addxtype(Roo.apply({}, this.menu));
4199         }
4200         
4201     },
4202     onClick : function(e)
4203     {
4204         Roo.log('item on click ');
4205         
4206         if(this.preventDefault){
4207             e.preventDefault();
4208         }
4209         //this.parent().hideMenuItems();
4210         
4211         this.fireEvent('click', this, e);
4212     },
4213     getEl : function()
4214     {
4215         return this.el;
4216     } 
4217 });
4218
4219  
4220
4221  /*
4222  * - LGPL
4223  *
4224  * menu separator
4225  * 
4226  */
4227
4228
4229 /**
4230  * @class Roo.bootstrap.MenuSeparator
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap MenuSeparator class
4233  * 
4234  * @constructor
4235  * Create a new MenuItem
4236  * @param {Object} config The config object
4237  */
4238
4239
4240 Roo.bootstrap.MenuSeparator = function(config){
4241     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4242 };
4243
4244 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4245     
4246     getAutoCreate : function(){
4247         var cfg = {
4248             cls: 'divider',
4249             tag : 'li'
4250         };
4251         
4252         return cfg;
4253     }
4254    
4255 });
4256
4257  
4258
4259  
4260 /*
4261 * Licence: LGPL
4262 */
4263
4264 /**
4265  * @class Roo.bootstrap.Modal
4266  * @extends Roo.bootstrap.Component
4267  * @builder-top
4268  * @parent none
4269  * @children Roo.bootstrap.Component
4270  * Bootstrap Modal class
4271  * @cfg {String} title Title of dialog
4272  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4273  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4274  * @cfg {Boolean} specificTitle default false
4275  * @cfg {Array} buttons Array of buttons or standard button set..
4276  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4277  * @cfg {Boolean} animate default true
4278  * @cfg {Boolean} allow_close default true
4279  * @cfg {Boolean} fitwindow default false
4280  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4281  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4282  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4283  * @cfg {String} size (sm|lg|xl) default empty
4284  * @cfg {Number} max_width set the max width of modal
4285  * @cfg {Boolean} editableTitle can the title be edited
4286
4287  *
4288  *
4289  * @constructor
4290  * Create a new Modal Dialog
4291  * @param {Object} config The config object
4292  */
4293
4294 Roo.bootstrap.Modal = function(config){
4295     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4296     this.addEvents({
4297         // raw events
4298         /**
4299          * @event btnclick
4300          * The raw btnclick event for the button
4301          * @param {Roo.EventObject} e
4302          */
4303         "btnclick" : true,
4304         /**
4305          * @event resize
4306          * Fire when dialog resize
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} e
4309          */
4310         "resize" : true,
4311         /**
4312          * @event titlechanged
4313          * Fire when the editable title has been changed
4314          * @param {Roo.bootstrap.Modal} this
4315          * @param {Roo.EventObject} value
4316          */
4317         "titlechanged" : true 
4318         
4319     });
4320     this.buttons = this.buttons || [];
4321
4322     if (this.tmpl) {
4323         this.tmpl = Roo.factory(this.tmpl);
4324     }
4325
4326 };
4327
4328 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4329
4330     title : 'test dialog',
4331
4332     buttons : false,
4333
4334     // set on load...
4335
4336     html: false,
4337
4338     tmp: false,
4339
4340     specificTitle: false,
4341
4342     buttonPosition: 'right',
4343
4344     allow_close : true,
4345
4346     animate : true,
4347
4348     fitwindow: false,
4349     
4350      // private
4351     dialogEl: false,
4352     bodyEl:  false,
4353     footerEl:  false,
4354     titleEl:  false,
4355     closeEl:  false,
4356
4357     size: '',
4358     
4359     max_width: 0,
4360     
4361     max_height: 0,
4362     
4363     fit_content: false,
4364     editableTitle  : false,
4365
4366     onRender : function(ct, position)
4367     {
4368         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4369
4370         if(!this.el){
4371             var cfg = Roo.apply({},  this.getAutoCreate());
4372             cfg.id = Roo.id();
4373             //if(!cfg.name){
4374             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4375             //}
4376             //if (!cfg.name.length) {
4377             //    delete cfg.name;
4378            // }
4379             if (this.cls) {
4380                 cfg.cls += ' ' + this.cls;
4381             }
4382             if (this.style) {
4383                 cfg.style = this.style;
4384             }
4385             this.el = Roo.get(document.body).createChild(cfg, position);
4386         }
4387         //var type = this.el.dom.type;
4388
4389
4390         if(this.tabIndex !== undefined){
4391             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4392         }
4393
4394         this.dialogEl = this.el.select('.modal-dialog',true).first();
4395         this.bodyEl = this.el.select('.modal-body',true).first();
4396         this.closeEl = this.el.select('.modal-header .close', true).first();
4397         this.headerEl = this.el.select('.modal-header',true).first();
4398         this.titleEl = this.el.select('.modal-title',true).first();
4399         this.footerEl = this.el.select('.modal-footer',true).first();
4400
4401         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4402         
4403         //this.el.addClass("x-dlg-modal");
4404
4405         if (this.buttons.length) {
4406             Roo.each(this.buttons, function(bb) {
4407                 var b = Roo.apply({}, bb);
4408                 b.xns = b.xns || Roo.bootstrap;
4409                 b.xtype = b.xtype || 'Button';
4410                 if (typeof(b.listeners) == 'undefined') {
4411                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4412                 }
4413
4414                 var btn = Roo.factory(b);
4415
4416                 btn.render(this.getButtonContainer());
4417
4418             },this);
4419         }
4420         // render the children.
4421         var nitems = [];
4422
4423         if(typeof(this.items) != 'undefined'){
4424             var items = this.items;
4425             delete this.items;
4426
4427             for(var i =0;i < items.length;i++) {
4428                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4429             }
4430         }
4431
4432         this.items = nitems;
4433
4434         // where are these used - they used to be body/close/footer
4435
4436
4437         this.initEvents();
4438         //this.el.addClass([this.fieldClass, this.cls]);
4439
4440     },
4441
4442     getAutoCreate : function()
4443     {
4444         // we will default to modal-body-overflow - might need to remove or make optional later.
4445         var bdy = {
4446                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4447                 html : this.html || ''
4448         };
4449
4450         var title = {
4451             tag: 'h5',
4452             cls : 'modal-title',
4453             html : this.title
4454         };
4455
4456         if(this.specificTitle){ // WTF is this?
4457             title = this.title;
4458         }
4459
4460         var header = [];
4461         if (this.allow_close && Roo.bootstrap.version == 3) {
4462             header.push({
4463                 tag: 'button',
4464                 cls : 'close',
4465                 html : '&times'
4466             });
4467         }
4468
4469         header.push(title);
4470
4471         if (this.editableTitle) {
4472             header.push({
4473                 cls: 'form-control roo-editable-title d-none',
4474                 tag: 'input',
4475                 type: 'text'
4476             });
4477         }
4478         
4479         if (this.allow_close && Roo.bootstrap.version == 4) {
4480             header.push({
4481                 tag: 'button',
4482                 cls : 'close',
4483                 html : '&times'
4484             });
4485         }
4486         
4487         var size = '';
4488
4489         if(this.size.length){
4490             size = 'modal-' + this.size;
4491         }
4492         
4493         var footer = Roo.bootstrap.version == 3 ?
4494             {
4495                 cls : 'modal-footer',
4496                 cn : [
4497                     {
4498                         tag: 'div',
4499                         cls: 'btn-' + this.buttonPosition
4500                     }
4501                 ]
4502
4503             } :
4504             {  // BS4 uses mr-auto on left buttons....
4505                 cls : 'modal-footer'
4506             };
4507
4508             
4509
4510         
4511         
4512         var modal = {
4513             cls: "modal",
4514              cn : [
4515                 {
4516                     cls: "modal-dialog " + size,
4517                     cn : [
4518                         {
4519                             cls : "modal-content",
4520                             cn : [
4521                                 {
4522                                     cls : 'modal-header',
4523                                     cn : header
4524                                 },
4525                                 bdy,
4526                                 footer
4527                             ]
4528
4529                         }
4530                     ]
4531
4532                 }
4533             ]
4534         };
4535
4536         if(this.animate){
4537             modal.cls += ' fade';
4538         }
4539
4540         return modal;
4541
4542     },
4543     getChildContainer : function() {
4544
4545          return this.bodyEl;
4546
4547     },
4548     getButtonContainer : function() {
4549         
4550          return Roo.bootstrap.version == 4 ?
4551             this.el.select('.modal-footer',true).first()
4552             : this.el.select('.modal-footer div',true).first();
4553
4554     },
4555     initEvents : function()
4556     {
4557         if (this.allow_close) {
4558             this.closeEl.on('click', this.hide, this);
4559         }
4560         Roo.EventManager.onWindowResize(this.resize, this, true);
4561         if (this.editableTitle) {
4562             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4563             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4564             this.headerEditEl.on('keyup', function(e) {
4565                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4566                         this.toggleHeaderInput(false)
4567                     }
4568                 }, this);
4569             this.headerEditEl.on('blur', function(e) {
4570                 this.toggleHeaderInput(false)
4571             },this);
4572         }
4573
4574     },
4575   
4576
4577     resize : function()
4578     {
4579         this.maskEl.setSize(
4580             Roo.lib.Dom.getViewWidth(true),
4581             Roo.lib.Dom.getViewHeight(true)
4582         );
4583         
4584         if (this.fitwindow) {
4585             
4586            this.dialogEl.setStyle( { 'max-width' : '100%' });
4587             this.setSize(
4588                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4589                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4590             );
4591             return;
4592         }
4593         
4594         if(this.max_width !== 0) {
4595             
4596             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4597             
4598             if(this.height) {
4599                 this.setSize(w, this.height);
4600                 return;
4601             }
4602             
4603             if(this.max_height) {
4604                 this.setSize(w,Math.min(
4605                     this.max_height,
4606                     Roo.lib.Dom.getViewportHeight(true) - 60
4607                 ));
4608                 
4609                 return;
4610             }
4611             
4612             if(!this.fit_content) {
4613                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4614                 return;
4615             }
4616             
4617             this.setSize(w, Math.min(
4618                 60 +
4619                 this.headerEl.getHeight() + 
4620                 this.footerEl.getHeight() + 
4621                 this.getChildHeight(this.bodyEl.dom.childNodes),
4622                 Roo.lib.Dom.getViewportHeight(true) - 60)
4623             );
4624         }
4625         
4626     },
4627
4628     setSize : function(w,h)
4629     {
4630         if (!w && !h) {
4631             return;
4632         }
4633         
4634         this.resizeTo(w,h);
4635     },
4636
4637     show : function() {
4638
4639         if (!this.rendered) {
4640             this.render();
4641         }
4642         this.toggleHeaderInput(false);
4643         //this.el.setStyle('display', 'block');
4644         this.el.removeClass('hideing');
4645         this.el.dom.style.display='block';
4646         
4647         Roo.get(document.body).addClass('modal-open');
4648  
4649         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4650             
4651             (function(){
4652                 this.el.addClass('show');
4653                 this.el.addClass('in');
4654             }).defer(50, this);
4655         }else{
4656             this.el.addClass('show');
4657             this.el.addClass('in');
4658         }
4659
4660         // not sure how we can show data in here..
4661         //if (this.tmpl) {
4662         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4663         //}
4664
4665         Roo.get(document.body).addClass("x-body-masked");
4666         
4667         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4668         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4669         this.maskEl.dom.style.display = 'block';
4670         this.maskEl.addClass('show');
4671         
4672         
4673         this.resize();
4674         
4675         this.fireEvent('show', this);
4676
4677         // set zindex here - otherwise it appears to be ignored...
4678         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679
4680         (function () {
4681             this.items.forEach( function(e) {
4682                 e.layout ? e.layout() : false;
4683
4684             });
4685         }).defer(100,this);
4686
4687     },
4688     hide : function()
4689     {
4690         if(this.fireEvent("beforehide", this) !== false){
4691             
4692             this.maskEl.removeClass('show');
4693             
4694             this.maskEl.dom.style.display = '';
4695             Roo.get(document.body).removeClass("x-body-masked");
4696             this.el.removeClass('in');
4697             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4698
4699             if(this.animate){ // why
4700                 this.el.addClass('hideing');
4701                 this.el.removeClass('show');
4702                 (function(){
4703                     if (!this.el.hasClass('hideing')) {
4704                         return; // it's been shown again...
4705                     }
4706                     
4707                     this.el.dom.style.display='';
4708
4709                     Roo.get(document.body).removeClass('modal-open');
4710                     this.el.removeClass('hideing');
4711                 }).defer(150,this);
4712                 
4713             }else{
4714                 this.el.removeClass('show');
4715                 this.el.dom.style.display='';
4716                 Roo.get(document.body).removeClass('modal-open');
4717
4718             }
4719             this.fireEvent('hide', this);
4720         }
4721     },
4722     isVisible : function()
4723     {
4724         
4725         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4726         
4727     },
4728
4729     addButton : function(str, cb)
4730     {
4731
4732
4733         var b = Roo.apply({}, { html : str } );
4734         b.xns = b.xns || Roo.bootstrap;
4735         b.xtype = b.xtype || 'Button';
4736         if (typeof(b.listeners) == 'undefined') {
4737             b.listeners = { click : cb.createDelegate(this)  };
4738         }
4739
4740         var btn = Roo.factory(b);
4741
4742         btn.render(this.getButtonContainer());
4743
4744         return btn;
4745
4746     },
4747
4748     setDefaultButton : function(btn)
4749     {
4750         //this.el.select('.modal-footer').()
4751     },
4752
4753     resizeTo: function(w,h)
4754     {
4755         this.dialogEl.setWidth(w);
4756         
4757         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4758
4759         this.bodyEl.setHeight(h - diff);
4760         
4761         this.fireEvent('resize', this);
4762     },
4763     
4764     setContentSize  : function(w, h)
4765     {
4766
4767     },
4768     onButtonClick: function(btn,e)
4769     {
4770         //Roo.log([a,b,c]);
4771         this.fireEvent('btnclick', btn.name, e);
4772     },
4773      /**
4774      * Set the title of the Dialog
4775      * @param {String} str new Title
4776      */
4777     setTitle: function(str) {
4778         this.titleEl.dom.innerHTML = str;
4779         this.title = str;
4780     },
4781     /**
4782      * Set the body of the Dialog
4783      * @param {String} str new Title
4784      */
4785     setBody: function(str) {
4786         this.bodyEl.dom.innerHTML = str;
4787     },
4788     /**
4789      * Set the body of the Dialog using the template
4790      * @param {Obj} data - apply this data to the template and replace the body contents.
4791      */
4792     applyBody: function(obj)
4793     {
4794         if (!this.tmpl) {
4795             Roo.log("Error - using apply Body without a template");
4796             //code
4797         }
4798         this.tmpl.overwrite(this.bodyEl, obj);
4799     },
4800     
4801     getChildHeight : function(child_nodes)
4802     {
4803         if(
4804             !child_nodes ||
4805             child_nodes.length == 0
4806         ) {
4807             return 0;
4808         }
4809         
4810         var child_height = 0;
4811         
4812         for(var i = 0; i < child_nodes.length; i++) {
4813             
4814             /*
4815             * for modal with tabs...
4816             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4817                 
4818                 var layout_childs = child_nodes[i].childNodes;
4819                 
4820                 for(var j = 0; j < layout_childs.length; j++) {
4821                     
4822                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4823                         
4824                         var layout_body_childs = layout_childs[j].childNodes;
4825                         
4826                         for(var k = 0; k < layout_body_childs.length; k++) {
4827                             
4828                             if(layout_body_childs[k].classList.contains('navbar')) {
4829                                 child_height += layout_body_childs[k].offsetHeight;
4830                                 continue;
4831                             }
4832                             
4833                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4834                                 
4835                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4836                                 
4837                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4838                                     
4839                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4840                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4841                                         continue;
4842                                     }
4843                                     
4844                                 }
4845                                 
4846                             }
4847                             
4848                         }
4849                     }
4850                 }
4851                 continue;
4852             }
4853             */
4854             
4855             child_height += child_nodes[i].offsetHeight;
4856             // Roo.log(child_nodes[i].offsetHeight);
4857         }
4858         
4859         return child_height;
4860     },
4861     toggleHeaderInput : function(is_edit)
4862     {
4863         if (!this.editableTitle) {
4864             return; // not editable.
4865         }
4866         if (is_edit && this.is_header_editing) {
4867             return; // already editing..
4868         }
4869         if (is_edit) {
4870     
4871             this.headerEditEl.dom.value = this.title;
4872             this.headerEditEl.removeClass('d-none');
4873             this.headerEditEl.dom.focus();
4874             this.titleEl.addClass('d-none');
4875             
4876             this.is_header_editing = true;
4877             return
4878         }
4879         // flip back to not editing.
4880         this.title = this.headerEditEl.dom.value;
4881         this.headerEditEl.addClass('d-none');
4882         this.titleEl.removeClass('d-none');
4883         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4884         this.is_header_editing = false;
4885         this.fireEvent('titlechanged', this, this.title);
4886     
4887             
4888         
4889     }
4890
4891 });
4892
4893
4894 Roo.apply(Roo.bootstrap.Modal,  {
4895     /**
4896          * Button config that displays a single OK button
4897          * @type Object
4898          */
4899         OK :  [{
4900             name : 'ok',
4901             weight : 'primary',
4902             html : 'OK'
4903         }],
4904         /**
4905          * Button config that displays Yes and No buttons
4906          * @type Object
4907          */
4908         YESNO : [
4909             {
4910                 name  : 'no',
4911                 html : 'No'
4912             },
4913             {
4914                 name  :'yes',
4915                 weight : 'primary',
4916                 html : 'Yes'
4917             }
4918         ],
4919
4920         /**
4921          * Button config that displays OK and Cancel buttons
4922          * @type Object
4923          */
4924         OKCANCEL : [
4925             {
4926                name : 'cancel',
4927                 html : 'Cancel'
4928             },
4929             {
4930                 name : 'ok',
4931                 weight : 'primary',
4932                 html : 'OK'
4933             }
4934         ],
4935         /**
4936          * Button config that displays Yes, No and Cancel buttons
4937          * @type Object
4938          */
4939         YESNOCANCEL : [
4940             {
4941                 name : 'yes',
4942                 weight : 'primary',
4943                 html : 'Yes'
4944             },
4945             {
4946                 name : 'no',
4947                 html : 'No'
4948             },
4949             {
4950                 name : 'cancel',
4951                 html : 'Cancel'
4952             }
4953         ],
4954         
4955         zIndex : 10001
4956 });
4957
4958 /*
4959  * - LGPL
4960  *
4961  * messagebox - can be used as a replace
4962  * 
4963  */
4964 /**
4965  * @class Roo.MessageBox
4966  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4967  * Example usage:
4968  *<pre><code>
4969 // Basic alert:
4970 Roo.Msg.alert('Status', 'Changes saved successfully.');
4971
4972 // Prompt for user data:
4973 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4974     if (btn == 'ok'){
4975         // process text value...
4976     }
4977 });
4978
4979 // Show a dialog using config options:
4980 Roo.Msg.show({
4981    title:'Save Changes?',
4982    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4983    buttons: Roo.Msg.YESNOCANCEL,
4984    fn: processResult,
4985    animEl: 'elId'
4986 });
4987 </code></pre>
4988  * @singleton
4989  */
4990 Roo.bootstrap.MessageBox = function(){
4991     var dlg, opt, mask, waitTimer;
4992     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4993     var buttons, activeTextEl, bwidth;
4994
4995     
4996     // private
4997     var handleButton = function(button){
4998         dlg.hide();
4999         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5000     };
5001
5002     // private
5003     var handleHide = function(){
5004         if(opt && opt.cls){
5005             dlg.el.removeClass(opt.cls);
5006         }
5007         //if(waitTimer){
5008         //    Roo.TaskMgr.stop(waitTimer);
5009         //    waitTimer = null;
5010         //}
5011     };
5012
5013     // private
5014     var updateButtons = function(b){
5015         var width = 0;
5016         if(!b){
5017             buttons["ok"].hide();
5018             buttons["cancel"].hide();
5019             buttons["yes"].hide();
5020             buttons["no"].hide();
5021             dlg.footerEl.hide();
5022             
5023             return width;
5024         }
5025         dlg.footerEl.show();
5026         for(var k in buttons){
5027             if(typeof buttons[k] != "function"){
5028                 if(b[k]){
5029                     buttons[k].show();
5030                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5031                     width += buttons[k].el.getWidth()+15;
5032                 }else{
5033                     buttons[k].hide();
5034                 }
5035             }
5036         }
5037         return width;
5038     };
5039
5040     // private
5041     var handleEsc = function(d, k, e){
5042         if(opt && opt.closable !== false){
5043             dlg.hide();
5044         }
5045         if(e){
5046             e.stopEvent();
5047         }
5048     };
5049
5050     return {
5051         /**
5052          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5053          * @return {Roo.BasicDialog} The BasicDialog element
5054          */
5055         getDialog : function(){
5056            if(!dlg){
5057                 dlg = new Roo.bootstrap.Modal( {
5058                     //draggable: true,
5059                     //resizable:false,
5060                     //constraintoviewport:false,
5061                     //fixedcenter:true,
5062                     //collapsible : false,
5063                     //shim:true,
5064                     //modal: true,
5065                 //    width: 'auto',
5066                   //  height:100,
5067                     //buttonAlign:"center",
5068                     closeClick : function(){
5069                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5070                             handleButton("no");
5071                         }else{
5072                             handleButton("cancel");
5073                         }
5074                     }
5075                 });
5076                 dlg.render();
5077                 dlg.on("hide", handleHide);
5078                 mask = dlg.mask;
5079                 //dlg.addKeyListener(27, handleEsc);
5080                 buttons = {};
5081                 this.buttons = buttons;
5082                 var bt = this.buttonText;
5083                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5084                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5085                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5086                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5087                 //Roo.log(buttons);
5088                 bodyEl = dlg.bodyEl.createChild({
5089
5090                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5091                         '<textarea class="roo-mb-textarea"></textarea>' +
5092                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5093                 });
5094                 msgEl = bodyEl.dom.firstChild;
5095                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5096                 textboxEl.enableDisplayMode();
5097                 textboxEl.addKeyListener([10,13], function(){
5098                     if(dlg.isVisible() && opt && opt.buttons){
5099                         if(opt.buttons.ok){
5100                             handleButton("ok");
5101                         }else if(opt.buttons.yes){
5102                             handleButton("yes");
5103                         }
5104                     }
5105                 });
5106                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5107                 textareaEl.enableDisplayMode();
5108                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5109                 progressEl.enableDisplayMode();
5110                 
5111                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5112                 var pf = progressEl.dom.firstChild;
5113                 if (pf) {
5114                     pp = Roo.get(pf.firstChild);
5115                     pp.setHeight(pf.offsetHeight);
5116                 }
5117                 
5118             }
5119             return dlg;
5120         },
5121
5122         /**
5123          * Updates the message box body text
5124          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5125          * the XHTML-compliant non-breaking space character '&amp;#160;')
5126          * @return {Roo.MessageBox} This message box
5127          */
5128         updateText : function(text)
5129         {
5130             if(!dlg.isVisible() && !opt.width){
5131                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5132                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5133             }
5134             msgEl.innerHTML = text || '&#160;';
5135       
5136             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5137             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5138             var w = Math.max(
5139                     Math.min(opt.width || cw , this.maxWidth), 
5140                     Math.max(opt.minWidth || this.minWidth, bwidth)
5141             );
5142             if(opt.prompt){
5143                 activeTextEl.setWidth(w);
5144             }
5145             if(dlg.isVisible()){
5146                 dlg.fixedcenter = false;
5147             }
5148             // to big, make it scroll. = But as usual stupid IE does not support
5149             // !important..
5150             
5151             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5152                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5153                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5154             } else {
5155                 bodyEl.dom.style.height = '';
5156                 bodyEl.dom.style.overflowY = '';
5157             }
5158             if (cw > w) {
5159                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5160             } else {
5161                 bodyEl.dom.style.overflowX = '';
5162             }
5163             
5164             dlg.setContentSize(w, bodyEl.getHeight());
5165             if(dlg.isVisible()){
5166                 dlg.fixedcenter = true;
5167             }
5168             return this;
5169         },
5170
5171         /**
5172          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5173          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5174          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5175          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5176          * @return {Roo.MessageBox} This message box
5177          */
5178         updateProgress : function(value, text){
5179             if(text){
5180                 this.updateText(text);
5181             }
5182             
5183             if (pp) { // weird bug on my firefox - for some reason this is not defined
5184                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5185                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5186             }
5187             return this;
5188         },        
5189
5190         /**
5191          * Returns true if the message box is currently displayed
5192          * @return {Boolean} True if the message box is visible, else false
5193          */
5194         isVisible : function(){
5195             return dlg && dlg.isVisible();  
5196         },
5197
5198         /**
5199          * Hides the message box if it is displayed
5200          */
5201         hide : function(){
5202             if(this.isVisible()){
5203                 dlg.hide();
5204             }  
5205         },
5206
5207         /**
5208          * Displays a new message box, or reinitializes an existing message box, based on the config options
5209          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5210          * The following config object properties are supported:
5211          * <pre>
5212 Property    Type             Description
5213 ----------  ---------------  ------------------------------------------------------------------------------------
5214 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5215                                    closes (defaults to undefined)
5216 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5217                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5218 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5219                                    progress and wait dialogs will ignore this property and always hide the
5220                                    close button as they can only be closed programmatically.
5221 cls               String           A custom CSS class to apply to the message box element
5222 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5223                                    displayed (defaults to 75)
5224 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5225                                    function will be btn (the name of the button that was clicked, if applicable,
5226                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5227                                    Progress and wait dialogs will ignore this option since they do not respond to
5228                                    user actions and can only be closed programmatically, so any required function
5229                                    should be called by the same code after it closes the dialog.
5230 icon              String           A CSS class that provides a background image to be used as an icon for
5231                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5232 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5233 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5234 modal             Boolean          False to allow user interaction with the page while the message box is
5235                                    displayed (defaults to true)
5236 msg               String           A string that will replace the existing message box body text (defaults
5237                                    to the XHTML-compliant non-breaking space character '&#160;')
5238 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5239 progress          Boolean          True to display a progress bar (defaults to false)
5240 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5241 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5242 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5243 title             String           The title text
5244 value             String           The string value to set into the active textbox element if displayed
5245 wait              Boolean          True to display a progress bar (defaults to false)
5246 width             Number           The width of the dialog in pixels
5247 </pre>
5248          *
5249          * Example usage:
5250          * <pre><code>
5251 Roo.Msg.show({
5252    title: 'Address',
5253    msg: 'Please enter your address:',
5254    width: 300,
5255    buttons: Roo.MessageBox.OKCANCEL,
5256    multiline: true,
5257    fn: saveAddress,
5258    animEl: 'addAddressBtn'
5259 });
5260 </code></pre>
5261          * @param {Object} config Configuration options
5262          * @return {Roo.MessageBox} This message box
5263          */
5264         show : function(options)
5265         {
5266             
5267             // this causes nightmares if you show one dialog after another
5268             // especially on callbacks..
5269              
5270             if(this.isVisible()){
5271                 
5272                 this.hide();
5273                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5274                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5275                 Roo.log("New Dialog Message:" +  options.msg )
5276                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5277                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5278                 
5279             }
5280             var d = this.getDialog();
5281             opt = options;
5282             d.setTitle(opt.title || "&#160;");
5283             d.closeEl.setDisplayed(opt.closable !== false);
5284             activeTextEl = textboxEl;
5285             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5286             if(opt.prompt){
5287                 if(opt.multiline){
5288                     textboxEl.hide();
5289                     textareaEl.show();
5290                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5291                         opt.multiline : this.defaultTextHeight);
5292                     activeTextEl = textareaEl;
5293                 }else{
5294                     textboxEl.show();
5295                     textareaEl.hide();
5296                 }
5297             }else{
5298                 textboxEl.hide();
5299                 textareaEl.hide();
5300             }
5301             progressEl.setDisplayed(opt.progress === true);
5302             if (opt.progress) {
5303                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5304             }
5305             this.updateProgress(0);
5306             activeTextEl.dom.value = opt.value || "";
5307             if(opt.prompt){
5308                 dlg.setDefaultButton(activeTextEl);
5309             }else{
5310                 var bs = opt.buttons;
5311                 var db = null;
5312                 if(bs && bs.ok){
5313                     db = buttons["ok"];
5314                 }else if(bs && bs.yes){
5315                     db = buttons["yes"];
5316                 }
5317                 dlg.setDefaultButton(db);
5318             }
5319             bwidth = updateButtons(opt.buttons);
5320             this.updateText(opt.msg);
5321             if(opt.cls){
5322                 d.el.addClass(opt.cls);
5323             }
5324             d.proxyDrag = opt.proxyDrag === true;
5325             d.modal = opt.modal !== false;
5326             d.mask = opt.modal !== false ? mask : false;
5327             if(!d.isVisible()){
5328                 // force it to the end of the z-index stack so it gets a cursor in FF
5329                 document.body.appendChild(dlg.el.dom);
5330                 d.animateTarget = null;
5331                 d.show(options.animEl);
5332             }
5333             return this;
5334         },
5335
5336         /**
5337          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5338          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5339          * and closing the message box when the process is complete.
5340          * @param {String} title The title bar text
5341          * @param {String} msg The message box body text
5342          * @return {Roo.MessageBox} This message box
5343          */
5344         progress : function(title, msg){
5345             this.show({
5346                 title : title,
5347                 msg : msg,
5348                 buttons: false,
5349                 progress:true,
5350                 closable:false,
5351                 minWidth: this.minProgressWidth,
5352                 modal : true
5353             });
5354             return this;
5355         },
5356
5357         /**
5358          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5359          * If a callback function is passed it will be called after the user clicks the button, and the
5360          * id of the button that was clicked will be passed as the only parameter to the callback
5361          * (could also be the top-right close button).
5362          * @param {String} title The title bar text
5363          * @param {String} msg The message box body text
5364          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5365          * @param {Object} scope (optional) The scope of the callback function
5366          * @return {Roo.MessageBox} This message box
5367          */
5368         alert : function(title, msg, fn, scope)
5369         {
5370             this.show({
5371                 title : title,
5372                 msg : msg,
5373                 buttons: this.OK,
5374                 fn: fn,
5375                 closable : false,
5376                 scope : scope,
5377                 modal : true
5378             });
5379             return this;
5380         },
5381
5382         /**
5383          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5384          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5385          * You are responsible for closing the message box when the process is complete.
5386          * @param {String} msg The message box body text
5387          * @param {String} title (optional) The title bar text
5388          * @return {Roo.MessageBox} This message box
5389          */
5390         wait : function(msg, title){
5391             this.show({
5392                 title : title,
5393                 msg : msg,
5394                 buttons: false,
5395                 closable:false,
5396                 progress:true,
5397                 modal:true,
5398                 width:300,
5399                 wait:true
5400             });
5401             waitTimer = Roo.TaskMgr.start({
5402                 run: function(i){
5403                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5404                 },
5405                 interval: 1000
5406             });
5407             return this;
5408         },
5409
5410         /**
5411          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5412          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5413          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5414          * @param {String} title The title bar text
5415          * @param {String} msg The message box body text
5416          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5417          * @param {Object} scope (optional) The scope of the callback function
5418          * @return {Roo.MessageBox} This message box
5419          */
5420         confirm : function(title, msg, fn, scope){
5421             this.show({
5422                 title : title,
5423                 msg : msg,
5424                 buttons: this.YESNO,
5425                 fn: fn,
5426                 scope : scope,
5427                 modal : true
5428             });
5429             return this;
5430         },
5431
5432         /**
5433          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5434          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5435          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5436          * (could also be the top-right close button) and the text that was entered will be passed as the two
5437          * parameters to the callback.
5438          * @param {String} title The title bar text
5439          * @param {String} msg The message box body text
5440          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5441          * @param {Object} scope (optional) The scope of the callback function
5442          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5443          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5444          * @return {Roo.MessageBox} This message box
5445          */
5446         prompt : function(title, msg, fn, scope, multiline){
5447             this.show({
5448                 title : title,
5449                 msg : msg,
5450                 buttons: this.OKCANCEL,
5451                 fn: fn,
5452                 minWidth:250,
5453                 scope : scope,
5454                 prompt:true,
5455                 multiline: multiline,
5456                 modal : true
5457             });
5458             return this;
5459         },
5460
5461         /**
5462          * Button config that displays a single OK button
5463          * @type Object
5464          */
5465         OK : {ok:true},
5466         /**
5467          * Button config that displays Yes and No buttons
5468          * @type Object
5469          */
5470         YESNO : {yes:true, no:true},
5471         /**
5472          * Button config that displays OK and Cancel buttons
5473          * @type Object
5474          */
5475         OKCANCEL : {ok:true, cancel:true},
5476         /**
5477          * Button config that displays Yes, No and Cancel buttons
5478          * @type Object
5479          */
5480         YESNOCANCEL : {yes:true, no:true, cancel:true},
5481
5482         /**
5483          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5484          * @type Number
5485          */
5486         defaultTextHeight : 75,
5487         /**
5488          * The maximum width in pixels of the message box (defaults to 600)
5489          * @type Number
5490          */
5491         maxWidth : 600,
5492         /**
5493          * The minimum width in pixels of the message box (defaults to 100)
5494          * @type Number
5495          */
5496         minWidth : 100,
5497         /**
5498          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5499          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5500          * @type Number
5501          */
5502         minProgressWidth : 250,
5503         /**
5504          * An object containing the default button text strings that can be overriden for localized language support.
5505          * Supported properties are: ok, cancel, yes and no.
5506          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5507          * @type Object
5508          */
5509         buttonText : {
5510             ok : "OK",
5511             cancel : "Cancel",
5512             yes : "Yes",
5513             no : "No"
5514         }
5515     };
5516 }();
5517
5518 /**
5519  * Shorthand for {@link Roo.MessageBox}
5520  */
5521 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5522 Roo.Msg = Roo.Msg || Roo.MessageBox;
5523 /*
5524  * - LGPL
5525  *
5526  * navbar
5527  * 
5528  */
5529
5530 /**
5531  * @class Roo.bootstrap.Navbar
5532  * @extends Roo.bootstrap.Component
5533  * Bootstrap Navbar class
5534
5535  * @constructor
5536  * Create a new Navbar
5537  * @param {Object} config The config object
5538  */
5539
5540
5541 Roo.bootstrap.Navbar = function(config){
5542     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5543     this.addEvents({
5544         // raw events
5545         /**
5546          * @event beforetoggle
5547          * Fire before toggle the menu
5548          * @param {Roo.EventObject} e
5549          */
5550         "beforetoggle" : true
5551     });
5552 };
5553
5554 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5555     
5556     
5557    
5558     // private
5559     navItems : false,
5560     loadMask : false,
5561     
5562     
5563     getAutoCreate : function(){
5564         
5565         
5566         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5567         
5568     },
5569     
5570     initEvents :function ()
5571     {
5572         //Roo.log(this.el.select('.navbar-toggle',true));
5573         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5574         
5575         var mark = {
5576             tag: "div",
5577             cls:"x-dlg-mask"
5578         };
5579         
5580         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5581         
5582         var size = this.el.getSize();
5583         this.maskEl.setSize(size.width, size.height);
5584         this.maskEl.enableDisplayMode("block");
5585         this.maskEl.hide();
5586         
5587         if(this.loadMask){
5588             this.maskEl.show();
5589         }
5590     },
5591     
5592     
5593     getChildContainer : function()
5594     {
5595         if (this.el && this.el.select('.collapse').getCount()) {
5596             return this.el.select('.collapse',true).first();
5597         }
5598         
5599         return this.el;
5600     },
5601     
5602     mask : function()
5603     {
5604         this.maskEl.show();
5605     },
5606     
5607     unmask : function()
5608     {
5609         this.maskEl.hide();
5610     },
5611     onToggle : function()
5612     {
5613         
5614         if(this.fireEvent('beforetoggle', this) === false){
5615             return;
5616         }
5617         var ce = this.el.select('.navbar-collapse',true).first();
5618       
5619         if (!ce.hasClass('show')) {
5620            this.expand();
5621         } else {
5622             this.collapse();
5623         }
5624         
5625         
5626     
5627     },
5628     /**
5629      * Expand the navbar pulldown 
5630      */
5631     expand : function ()
5632     {
5633        
5634         var ce = this.el.select('.navbar-collapse',true).first();
5635         if (ce.hasClass('collapsing')) {
5636             return;
5637         }
5638         ce.dom.style.height = '';
5639                // show it...
5640         ce.addClass('in'); // old...
5641         ce.removeClass('collapse');
5642         ce.addClass('show');
5643         var h = ce.getHeight();
5644         Roo.log(h);
5645         ce.removeClass('show');
5646         // at this point we should be able to see it..
5647         ce.addClass('collapsing');
5648         
5649         ce.setHeight(0); // resize it ...
5650         ce.on('transitionend', function() {
5651             //Roo.log('done transition');
5652             ce.removeClass('collapsing');
5653             ce.addClass('show');
5654             ce.removeClass('collapse');
5655
5656             ce.dom.style.height = '';
5657         }, this, { single: true} );
5658         ce.setHeight(h);
5659         ce.dom.scrollTop = 0;
5660     },
5661     /**
5662      * Collapse the navbar pulldown 
5663      */
5664     collapse : function()
5665     {
5666          var ce = this.el.select('.navbar-collapse',true).first();
5667        
5668         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5669             // it's collapsed or collapsing..
5670             return;
5671         }
5672         ce.removeClass('in'); // old...
5673         ce.setHeight(ce.getHeight());
5674         ce.removeClass('show');
5675         ce.addClass('collapsing');
5676         
5677         ce.on('transitionend', function() {
5678             ce.dom.style.height = '';
5679             ce.removeClass('collapsing');
5680             ce.addClass('collapse');
5681         }, this, { single: true} );
5682         ce.setHeight(0);
5683     }
5684     
5685     
5686     
5687 });
5688
5689
5690
5691  
5692
5693  /*
5694  * - LGPL
5695  *
5696  * navbar
5697  * 
5698  */
5699
5700 /**
5701  * @class Roo.bootstrap.NavSimplebar
5702  * @extends Roo.bootstrap.Navbar
5703  * Bootstrap Sidebar class
5704  *
5705  * @cfg {Boolean} inverse is inverted color
5706  * 
5707  * @cfg {String} type (nav | pills | tabs)
5708  * @cfg {Boolean} arrangement stacked | justified
5709  * @cfg {String} align (left | right) alignment
5710  * 
5711  * @cfg {Boolean} main (true|false) main nav bar? default false
5712  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5713  * 
5714  * @cfg {String} tag (header|footer|nav|div) default is nav 
5715
5716  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5717  * 
5718  * 
5719  * @constructor
5720  * Create a new Sidebar
5721  * @param {Object} config The config object
5722  */
5723
5724
5725 Roo.bootstrap.NavSimplebar = function(config){
5726     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5727 };
5728
5729 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5730     
5731     inverse: false,
5732     
5733     type: false,
5734     arrangement: '',
5735     align : false,
5736     
5737     weight : 'light',
5738     
5739     main : false,
5740     
5741     
5742     tag : false,
5743     
5744     
5745     getAutoCreate : function(){
5746         
5747         
5748         var cfg = {
5749             tag : this.tag || 'div',
5750             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5751         };
5752         if (['light','white'].indexOf(this.weight) > -1) {
5753             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5754         }
5755         cfg.cls += ' bg-' + this.weight;
5756         
5757         if (this.inverse) {
5758             cfg.cls += ' navbar-inverse';
5759             
5760         }
5761         
5762         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5763         
5764         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5765             return cfg;
5766         }
5767         
5768         
5769     
5770         
5771         cfg.cn = [
5772             {
5773                 cls: 'nav nav-' + this.xtype,
5774                 tag : 'ul'
5775             }
5776         ];
5777         
5778          
5779         this.type = this.type || 'nav';
5780         if (['tabs','pills'].indexOf(this.type) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.type
5782         
5783         
5784         } else {
5785             if (this.type!=='nav') {
5786                 Roo.log('nav type must be nav/tabs/pills')
5787             }
5788             cfg.cn[0].cls += ' navbar-nav'
5789         }
5790         
5791         
5792         
5793         
5794         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5795             cfg.cn[0].cls += ' nav-' + this.arrangement;
5796         }
5797         
5798         
5799         if (this.align === 'right') {
5800             cfg.cn[0].cls += ' navbar-right';
5801         }
5802         
5803         
5804         
5805         
5806         return cfg;
5807     
5808         
5809     }
5810     
5811     
5812     
5813 });
5814
5815
5816
5817  
5818
5819  
5820        /*
5821  * - LGPL
5822  *
5823  * navbar
5824  * navbar-fixed-top
5825  * navbar-expand-md  fixed-top 
5826  */
5827
5828 /**
5829  * @class Roo.bootstrap.NavHeaderbar
5830  * @extends Roo.bootstrap.NavSimplebar
5831  * Bootstrap Sidebar class
5832  *
5833  * @cfg {String} brand what is brand
5834  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5835  * @cfg {String} brand_href href of the brand
5836  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5837  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5838  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5839  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840  * 
5841  * @constructor
5842  * Create a new Sidebar
5843  * @param {Object} config The config object
5844  */
5845
5846
5847 Roo.bootstrap.NavHeaderbar = function(config){
5848     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5849       
5850 };
5851
5852 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5853     
5854     position: '',
5855     brand: '',
5856     brand_href: false,
5857     srButton : true,
5858     autohide : false,
5859     desktopCenter : false,
5860    
5861     
5862     getAutoCreate : function(){
5863         
5864         var   cfg = {
5865             tag: this.nav || 'nav',
5866             cls: 'navbar navbar-expand-md',
5867             role: 'navigation',
5868             cn: []
5869         };
5870         
5871         var cn = cfg.cn;
5872         if (this.desktopCenter) {
5873             cn.push({cls : 'container', cn : []});
5874             cn = cn[0].cn;
5875         }
5876         
5877         if(this.srButton){
5878             var btn = {
5879                 tag: 'button',
5880                 type: 'button',
5881                 cls: 'navbar-toggle navbar-toggler',
5882                 'data-toggle': 'collapse',
5883                 cn: [
5884                     {
5885                         tag: 'span',
5886                         cls: 'sr-only',
5887                         html: 'Toggle navigation'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar navbar-toggler-icon'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar'
5900                     }
5901                 ]
5902             };
5903             
5904             cn.push( Roo.bootstrap.version == 4 ? btn : {
5905                 tag: 'div',
5906                 cls: 'navbar-header',
5907                 cn: [
5908                     btn
5909                 ]
5910             });
5911         }
5912         
5913         cn.push({
5914             tag: 'div',
5915             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5916             cn : []
5917         });
5918         
5919         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5920         
5921         if (['light','white'].indexOf(this.weight) > -1) {
5922             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5923         }
5924         cfg.cls += ' bg-' + this.weight;
5925         
5926         
5927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5928             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5929             
5930             // tag can override this..
5931             
5932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5933         }
5934         
5935         if (this.brand !== '') {
5936             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5937             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5938                 tag: 'a',
5939                 href: this.brand_href ? this.brand_href : '#',
5940                 cls: 'navbar-brand',
5941                 cn: [
5942                 this.brand
5943                 ]
5944             });
5945         }
5946         
5947         if(this.main){
5948             cfg.cls += ' main-nav';
5949         }
5950         
5951         
5952         return cfg;
5953
5954         
5955     },
5956     getHeaderChildContainer : function()
5957     {
5958         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5959             return this.el.select('.navbar-header',true).first();
5960         }
5961         
5962         return this.getChildContainer();
5963     },
5964     
5965     getChildContainer : function()
5966     {
5967          
5968         return this.el.select('.roo-navbar-collapse',true).first();
5969          
5970         
5971     },
5972     
5973     initEvents : function()
5974     {
5975         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5976         
5977         if (this.autohide) {
5978             
5979             var prevScroll = 0;
5980             var ft = this.el;
5981             
5982             Roo.get(document).on('scroll',function(e) {
5983                 var ns = Roo.get(document).getScroll().top;
5984                 var os = prevScroll;
5985                 prevScroll = ns;
5986                 
5987                 if(ns > os){
5988                     ft.removeClass('slideDown');
5989                     ft.addClass('slideUp');
5990                     return;
5991                 }
5992                 ft.removeClass('slideUp');
5993                 ft.addClass('slideDown');
5994                  
5995               
5996           },this);
5997         }
5998     }    
5999     
6000 });
6001
6002
6003
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * navbar
6010  * 
6011  */
6012
6013 /**
6014  * @class Roo.bootstrap.NavSidebar
6015  * @extends Roo.bootstrap.Navbar
6016  * Bootstrap Sidebar class
6017  * 
6018  * @constructor
6019  * Create a new Sidebar
6020  * @param {Object} config The config object
6021  */
6022
6023
6024 Roo.bootstrap.NavSidebar = function(config){
6025     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6026 };
6027
6028 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6029     
6030     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6031     
6032     getAutoCreate : function(){
6033         
6034         
6035         return  {
6036             tag: 'div',
6037             cls: 'sidebar sidebar-nav'
6038         };
6039     
6040         
6041     }
6042     
6043     
6044     
6045 });
6046
6047
6048
6049  
6050
6051  /*
6052  * - LGPL
6053  *
6054  * nav group
6055  * 
6056  */
6057
6058 /**
6059  * @class Roo.bootstrap.NavGroup
6060  * @extends Roo.bootstrap.Component
6061  * Bootstrap NavGroup class
6062  * @cfg {String} align (left|right)
6063  * @cfg {Boolean} inverse
6064  * @cfg {String} type (nav|pills|tab) default nav
6065  * @cfg {String} navId - reference Id for navbar.
6066  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067  * 
6068  * @constructor
6069  * Create a new nav group
6070  * @param {Object} config The config object
6071  */
6072
6073 Roo.bootstrap.NavGroup = function(config){
6074     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6075     this.navItems = [];
6076    
6077     Roo.bootstrap.NavGroup.register(this);
6078      this.addEvents({
6079         /**
6080              * @event changed
6081              * Fires when the active item changes
6082              * @param {Roo.bootstrap.NavGroup} this
6083              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6084              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6085          */
6086         'changed': true
6087      });
6088     
6089 };
6090
6091 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6092     
6093     align: '',
6094     inverse: false,
6095     form: false,
6096     type: 'nav',
6097     navId : '',
6098     // private
6099     pilltype : true,
6100     
6101     navItems : false, 
6102     
6103     getAutoCreate : function()
6104     {
6105         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6106         
6107         cfg = {
6108             tag : 'ul',
6109             cls: 'nav' 
6110         };
6111         if (Roo.bootstrap.version == 4) {
6112             if (['tabs','pills'].indexOf(this.type) != -1) {
6113                 cfg.cls += ' nav-' + this.type; 
6114             } else {
6115                 // trying to remove so header bar can right align top?
6116                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6117                     // do not use on header bar... 
6118                     cfg.cls += ' navbar-nav';
6119                 }
6120             }
6121             
6122         } else {
6123             if (['tabs','pills'].indexOf(this.type) != -1) {
6124                 cfg.cls += ' nav-' + this.type
6125             } else {
6126                 if (this.type !== 'nav') {
6127                     Roo.log('nav type must be nav/tabs/pills')
6128                 }
6129                 cfg.cls += ' navbar-nav'
6130             }
6131         }
6132         
6133         if (this.parent() && this.parent().sidebar) {
6134             cfg = {
6135                 tag: 'ul',
6136                 cls: 'dashboard-menu sidebar-menu'
6137             };
6138             
6139             return cfg;
6140         }
6141         
6142         if (this.form === true) {
6143             cfg = {
6144                 tag: 'form',
6145                 cls: 'navbar-form form-inline'
6146             };
6147             //nav navbar-right ml-md-auto
6148             if (this.align === 'right') {
6149                 cfg.cls += ' navbar-right ml-md-auto';
6150             } else {
6151                 cfg.cls += ' navbar-left';
6152             }
6153         }
6154         
6155         if (this.align === 'right') {
6156             cfg.cls += ' navbar-right ml-md-auto';
6157         } else {
6158             cfg.cls += ' mr-auto';
6159         }
6160         
6161         if (this.inverse) {
6162             cfg.cls += ' navbar-inverse';
6163             
6164         }
6165         
6166         
6167         return cfg;
6168     },
6169     /**
6170     * sets the active Navigation item
6171     * @param {Roo.bootstrap.NavItem} the new current navitem
6172     */
6173     setActiveItem : function(item)
6174     {
6175         var prev = false;
6176         Roo.each(this.navItems, function(v){
6177             if (v == item) {
6178                 return ;
6179             }
6180             if (v.isActive()) {
6181                 v.setActive(false, true);
6182                 prev = v;
6183                 
6184             }
6185             
6186         });
6187
6188         item.setActive(true, true);
6189         this.fireEvent('changed', this, item, prev);
6190         
6191         
6192     },
6193     /**
6194     * gets the active Navigation item
6195     * @return {Roo.bootstrap.NavItem} the current navitem
6196     */
6197     getActive : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v){
6202             
6203             if (v.isActive()) {
6204                 prev = v;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     
6212     indexOfNav : function()
6213     {
6214         
6215         var prev = false;
6216         Roo.each(this.navItems, function(v,i){
6217             
6218             if (v.isActive()) {
6219                 prev = i;
6220                 
6221             }
6222             
6223         });
6224         return prev;
6225     },
6226     /**
6227     * adds a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     addItem : function(cfg)
6231     {
6232         if (this.form && Roo.bootstrap.version == 4) {
6233             cfg.tag = 'div';
6234         }
6235         var cn = new Roo.bootstrap.NavItem(cfg);
6236         this.register(cn);
6237         cn.parentId = this.id;
6238         cn.onRender(this.el, null);
6239         return cn;
6240     },
6241     /**
6242     * register a Navigation item
6243     * @param {Roo.bootstrap.NavItem} the navitem to add
6244     */
6245     register : function(item)
6246     {
6247         this.navItems.push( item);
6248         item.navId = this.navId;
6249     
6250     },
6251     
6252     /**
6253     * clear all the Navigation item
6254     */
6255    
6256     clearAll : function()
6257     {
6258         this.navItems = [];
6259         this.el.dom.innerHTML = '';
6260     },
6261     
6262     getNavItem: function(tabId)
6263     {
6264         var ret = false;
6265         Roo.each(this.navItems, function(e) {
6266             if (e.tabId == tabId) {
6267                ret =  e;
6268                return false;
6269             }
6270             return true;
6271             
6272         });
6273         return ret;
6274     },
6275     
6276     setActiveNext : function()
6277     {
6278         var i = this.indexOfNav(this.getActive());
6279         if (i > this.navItems.length) {
6280             return;
6281         }
6282         this.setActiveItem(this.navItems[i+1]);
6283     },
6284     setActivePrev : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i  < 1) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i-1]);
6291     },
6292     clearWasActive : function(except) {
6293         Roo.each(this.navItems, function(e) {
6294             if (e.tabId != except.tabId && e.was_active) {
6295                e.was_active = false;
6296                return false;
6297             }
6298             return true;
6299             
6300         });
6301     },
6302     getWasActive : function ()
6303     {
6304         var r = false;
6305         Roo.each(this.navItems, function(e) {
6306             if (e.was_active) {
6307                r = e;
6308                return false;
6309             }
6310             return true;
6311             
6312         });
6313         return r;
6314     }
6315     
6316     
6317 });
6318
6319  
6320 Roo.apply(Roo.bootstrap.NavGroup, {
6321     
6322     groups: {},
6323      /**
6324     * register a Navigation Group
6325     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6326     */
6327     register : function(navgrp)
6328     {
6329         this.groups[navgrp.navId] = navgrp;
6330         
6331     },
6332     /**
6333     * fetch a Navigation Group based on the navigation ID
6334     * @param {string} the navgroup to add
6335     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6336     */
6337     get: function(navId) {
6338         if (typeof(this.groups[navId]) == 'undefined') {
6339             return false;
6340             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6341         }
6342         return this.groups[navId] ;
6343     }
6344     
6345     
6346     
6347 });
6348
6349  /*
6350  * - LGPL
6351  *
6352  * row
6353  * 
6354  */
6355
6356 /**
6357  * @class Roo.bootstrap.NavItem
6358  * @extends Roo.bootstrap.Component
6359  * Bootstrap Navbar.NavItem class
6360  * @cfg {String} href  link to
6361  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6362  * @cfg {Boolean} button_outline show and outlined button
6363  * @cfg {String} html content of button
6364  * @cfg {String} badge text inside badge
6365  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6366  * @cfg {String} glyphicon DEPRICATED - use fa
6367  * @cfg {String} icon DEPRICATED - use fa
6368  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6369  * @cfg {Boolean} active Is item active
6370  * @cfg {Boolean} disabled Is item disabled
6371  * @cfg {String} linkcls  Link Class
6372  * @cfg {Boolean} preventDefault (true | false) default false
6373  * @cfg {String} tabId the tab that this item activates.
6374  * @cfg {String} tagtype (a|span) render as a href or span?
6375  * @cfg {Boolean} animateRef (true|false) link to element default false  
6376   
6377  * @constructor
6378  * Create a new Navbar Item
6379  * @param {Object} config The config object
6380  */
6381 Roo.bootstrap.NavItem = function(config){
6382     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6383     this.addEvents({
6384         // raw events
6385         /**
6386          * @event click
6387          * The raw click event for the entire grid.
6388          * @param {Roo.EventObject} e
6389          */
6390         "click" : true,
6391          /**
6392             * @event changed
6393             * Fires when the active item active state changes
6394             * @param {Roo.bootstrap.NavItem} this
6395             * @param {boolean} state the new state
6396              
6397          */
6398         'changed': true,
6399         /**
6400             * @event scrollto
6401             * Fires when scroll to element
6402             * @param {Roo.bootstrap.NavItem} this
6403             * @param {Object} options
6404             * @param {Roo.EventObject} e
6405              
6406          */
6407         'scrollto': true
6408     });
6409    
6410 };
6411
6412 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6413     
6414     href: false,
6415     html: '',
6416     badge: '',
6417     icon: false,
6418     fa : false,
6419     glyphicon: false,
6420     active: false,
6421     preventDefault : false,
6422     tabId : false,
6423     tagtype : 'a',
6424     tag: 'li',
6425     disabled : false,
6426     animateRef : false,
6427     was_active : false,
6428     button_weight : '',
6429     button_outline : false,
6430     linkcls : '',
6431     navLink: false,
6432     
6433     getAutoCreate : function(){
6434          
6435         var cfg = {
6436             tag: this.tag,
6437             cls: 'nav-item'
6438         };
6439         
6440         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6441         
6442         if (this.active) {
6443             cfg.cls +=  ' active' ;
6444         }
6445         if (this.disabled) {
6446             cfg.cls += ' disabled';
6447         }
6448         
6449         // BS4 only?
6450         if (this.button_weight.length) {
6451             cfg.tag = this.href ? 'a' : 'button';
6452             cfg.html = this.html || '';
6453             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6454             if (this.href) {
6455                 cfg.href = this.href;
6456             }
6457             if (this.fa) {
6458                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6459             } else {
6460                 cfg.cls += " nav-html";
6461             }
6462             
6463             // menu .. should add dropdown-menu class - so no need for carat..
6464             
6465             if (this.badge !== '') {
6466                  
6467                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6468             }
6469             return cfg;
6470         }
6471         
6472         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6473             cfg.cn = [
6474                 {
6475                     tag: this.tagtype,
6476                     href : this.href || "#",
6477                     html: this.html || '',
6478                     cls : ''
6479                 }
6480             ];
6481             if (this.tagtype == 'a') {
6482                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6483         
6484             }
6485             if (this.icon) {
6486                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else  if (this.fa) {
6488                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6489             } else if(this.glyphicon) {
6490                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6491             } else {
6492                 cfg.cn[0].cls += " nav-html";
6493             }
6494             
6495             if (this.menu) {
6496                 cfg.cn[0].html += " <span class='caret'></span>";
6497              
6498             }
6499             
6500             if (this.badge !== '') {
6501                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6502             }
6503         }
6504         
6505         
6506         
6507         return cfg;
6508     },
6509     onRender : function(ct, position)
6510     {
6511        // Roo.log("Call onRender: " + this.xtype);
6512         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6513             this.tag = 'div';
6514         }
6515         
6516         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6517         this.navLink = this.el.select('.nav-link',true).first();
6518         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6519         return ret;
6520     },
6521       
6522     
6523     initEvents: function() 
6524     {
6525         if (typeof (this.menu) != 'undefined') {
6526             this.menu.parentType = this.xtype;
6527             this.menu.triggerEl = this.el;
6528             this.menu = this.addxtype(Roo.apply({}, this.menu));
6529         }
6530         
6531         this.el.on('click', this.onClick, this);
6532         
6533         //if(this.tagtype == 'span'){
6534         //    this.el.select('span',true).on('click', this.onClick, this);
6535         //}
6536        
6537         // at this point parent should be available..
6538         this.parent().register(this);
6539     },
6540     
6541     onClick : function(e)
6542     {
6543         if (e.getTarget('.dropdown-menu-item')) {
6544             // did you click on a menu itemm.... - then don't trigger onclick..
6545             return;
6546         }
6547         
6548         if(
6549                 this.preventDefault || 
6550                 this.href == '#' 
6551         ){
6552             Roo.log("NavItem - prevent Default?");
6553             e.preventDefault();
6554         }
6555         
6556         if (this.disabled) {
6557             return;
6558         }
6559         
6560         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6561         if (tg && tg.transition) {
6562             Roo.log("waiting for the transitionend");
6563             return;
6564         }
6565         
6566         
6567         
6568         //Roo.log("fire event clicked");
6569         if(this.fireEvent('click', this, e) === false){
6570             return;
6571         };
6572         
6573         if(this.tagtype == 'span'){
6574             return;
6575         }
6576         
6577         //Roo.log(this.href);
6578         var ael = this.el.select('a',true).first();
6579         //Roo.log(ael);
6580         
6581         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6582             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6583             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6584                 return; // ignore... - it's a 'hash' to another page.
6585             }
6586             Roo.log("NavItem - prevent Default?");
6587             e.preventDefault();
6588             this.scrollToElement(e);
6589         }
6590         
6591         
6592         var p =  this.parent();
6593    
6594         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6595             if (typeof(p.setActiveItem) !== 'undefined') {
6596                 p.setActiveItem(this);
6597             }
6598         }
6599         
6600         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6601         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6602             // remove the collapsed menu expand...
6603             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6604         }
6605     },
6606     
6607     isActive: function () {
6608         return this.active
6609     },
6610     setActive : function(state, fire, is_was_active)
6611     {
6612         if (this.active && !state && this.navId) {
6613             this.was_active = true;
6614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6615             if (nv) {
6616                 nv.clearWasActive(this);
6617             }
6618             
6619         }
6620         this.active = state;
6621         
6622         if (!state ) {
6623             this.el.removeClass('active');
6624             this.navLink ? this.navLink.removeClass('active') : false;
6625         } else if (!this.el.hasClass('active')) {
6626             
6627             this.el.addClass('active');
6628             if (Roo.bootstrap.version == 4 && this.navLink ) {
6629                 this.navLink.addClass('active');
6630             }
6631             
6632         }
6633         if (fire) {
6634             this.fireEvent('changed', this, state);
6635         }
6636         
6637         // show a panel if it's registered and related..
6638         
6639         if (!this.navId || !this.tabId || !state || is_was_active) {
6640             return;
6641         }
6642         
6643         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6644         if (!tg) {
6645             return;
6646         }
6647         var pan = tg.getPanelByName(this.tabId);
6648         if (!pan) {
6649             return;
6650         }
6651         // if we can not flip to new panel - go back to old nav highlight..
6652         if (false == tg.showPanel(pan)) {
6653             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6654             if (nv) {
6655                 var onav = nv.getWasActive();
6656                 if (onav) {
6657                     onav.setActive(true, false, true);
6658                 }
6659             }
6660             
6661         }
6662         
6663         
6664         
6665     },
6666      // this should not be here...
6667     setDisabled : function(state)
6668     {
6669         this.disabled = state;
6670         if (!state ) {
6671             this.el.removeClass('disabled');
6672         } else if (!this.el.hasClass('disabled')) {
6673             this.el.addClass('disabled');
6674         }
6675         
6676     },
6677     
6678     /**
6679      * Fetch the element to display the tooltip on.
6680      * @return {Roo.Element} defaults to this.el
6681      */
6682     tooltipEl : function()
6683     {
6684         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6685     },
6686     
6687     scrollToElement : function(e)
6688     {
6689         var c = document.body;
6690         
6691         /*
6692          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6693          */
6694         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6695             c = document.documentElement;
6696         }
6697         
6698         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6699         
6700         if(!target){
6701             return;
6702         }
6703
6704         var o = target.calcOffsetsTo(c);
6705         
6706         var options = {
6707             target : target,
6708             value : o[1]
6709         };
6710         
6711         this.fireEvent('scrollto', this, options, e);
6712         
6713         Roo.get(c).scrollTo('top', options.value, true);
6714         
6715         return;
6716     },
6717     /**
6718      * Set the HTML (text content) of the item
6719      * @param {string} html  content for the nav item
6720      */
6721     setHtml : function(html)
6722     {
6723         this.html = html;
6724         this.htmlEl.dom.innerHTML = html;
6725         
6726     } 
6727 });
6728  
6729
6730  /*
6731  * - LGPL
6732  *
6733  * sidebar item
6734  *
6735  *  li
6736  *    <span> icon </span>
6737  *    <span> text </span>
6738  *    <span>badge </span>
6739  */
6740
6741 /**
6742  * @class Roo.bootstrap.NavSidebarItem
6743  * @extends Roo.bootstrap.NavItem
6744  * Bootstrap Navbar.NavSidebarItem class
6745  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6746  * {Boolean} open is the menu open
6747  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6748  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6749  * {String} buttonSize (sm|md|lg)the extra classes for the button
6750  * {Boolean} showArrow show arrow next to the text (default true)
6751  * @constructor
6752  * Create a new Navbar Button
6753  * @param {Object} config The config object
6754  */
6755 Roo.bootstrap.NavSidebarItem = function(config){
6756     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6757     this.addEvents({
6758         // raw events
6759         /**
6760          * @event click
6761          * The raw click event for the entire grid.
6762          * @param {Roo.EventObject} e
6763          */
6764         "click" : true,
6765          /**
6766             * @event changed
6767             * Fires when the active item active state changes
6768             * @param {Roo.bootstrap.NavSidebarItem} this
6769             * @param {boolean} state the new state
6770              
6771          */
6772         'changed': true
6773     });
6774    
6775 };
6776
6777 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6778     
6779     badgeWeight : 'default',
6780     
6781     open: false,
6782     
6783     buttonView : false,
6784     
6785     buttonWeight : 'default',
6786     
6787     buttonSize : 'md',
6788     
6789     showArrow : true,
6790     
6791     getAutoCreate : function(){
6792         
6793         
6794         var a = {
6795                 tag: 'a',
6796                 href : this.href || '#',
6797                 cls: '',
6798                 html : '',
6799                 cn : []
6800         };
6801         
6802         if(this.buttonView){
6803             a = {
6804                 tag: 'button',
6805                 href : this.href || '#',
6806                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6807                 html : this.html,
6808                 cn : []
6809             };
6810         }
6811         
6812         var cfg = {
6813             tag: 'li',
6814             cls: '',
6815             cn: [ a ]
6816         };
6817         
6818         if (this.active) {
6819             cfg.cls += ' active';
6820         }
6821         
6822         if (this.disabled) {
6823             cfg.cls += ' disabled';
6824         }
6825         if (this.open) {
6826             cfg.cls += ' open x-open';
6827         }
6828         // left icon..
6829         if (this.glyphicon || this.icon) {
6830             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6831             a.cn.push({ tag : 'i', cls : c }) ;
6832         }
6833         
6834         if(!this.buttonView){
6835             var span = {
6836                 tag: 'span',
6837                 html : this.html || ''
6838             };
6839
6840             a.cn.push(span);
6841             
6842         }
6843         
6844         if (this.badge !== '') {
6845             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6846         }
6847         
6848         if (this.menu) {
6849             
6850             if(this.showArrow){
6851                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852             }
6853             
6854             a.cls += ' dropdown-toggle treeview' ;
6855         }
6856         
6857         return cfg;
6858     },
6859     
6860     initEvents : function()
6861     { 
6862         if (typeof (this.menu) != 'undefined') {
6863             this.menu.parentType = this.xtype;
6864             this.menu.triggerEl = this.el;
6865             this.menu = this.addxtype(Roo.apply({}, this.menu));
6866         }
6867         
6868         this.el.on('click', this.onClick, this);
6869         
6870         if(this.badge !== ''){
6871             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6872         }
6873         
6874     },
6875     
6876     onClick : function(e)
6877     {
6878         if(this.disabled){
6879             e.preventDefault();
6880             return;
6881         }
6882         
6883         if(this.preventDefault){
6884             e.preventDefault();
6885         }
6886         
6887         this.fireEvent('click', this, e);
6888     },
6889     
6890     disable : function()
6891     {
6892         this.setDisabled(true);
6893     },
6894     
6895     enable : function()
6896     {
6897         this.setDisabled(false);
6898     },
6899     
6900     setDisabled : function(state)
6901     {
6902         if(this.disabled == state){
6903             return;
6904         }
6905         
6906         this.disabled = state;
6907         
6908         if (state) {
6909             this.el.addClass('disabled');
6910             return;
6911         }
6912         
6913         this.el.removeClass('disabled');
6914         
6915         return;
6916     },
6917     
6918     setActive : function(state)
6919     {
6920         if(this.active == state){
6921             return;
6922         }
6923         
6924         this.active = state;
6925         
6926         if (state) {
6927             this.el.addClass('active');
6928             return;
6929         }
6930         
6931         this.el.removeClass('active');
6932         
6933         return;
6934     },
6935     
6936     isActive: function () 
6937     {
6938         return this.active;
6939     },
6940     
6941     setBadge : function(str)
6942     {
6943         if(!this.badgeEl){
6944             return;
6945         }
6946         
6947         this.badgeEl.dom.innerHTML = str;
6948     }
6949     
6950    
6951      
6952  
6953 });
6954  
6955
6956  /*
6957  * - LGPL
6958  *
6959  *  Breadcrumb Nav
6960  * 
6961  */
6962 Roo.namespace('Roo.bootstrap.breadcrumb');
6963
6964
6965 /**
6966  * @class Roo.bootstrap.breadcrumb.Nav
6967  * @extends Roo.bootstrap.Component
6968  * Bootstrap Breadcrumb Nav Class
6969  *  
6970  * @children Roo.bootstrap.breadcrumb.Item
6971  * 
6972  * @constructor
6973  * Create a new breadcrumb.Nav
6974  * @param {Object} config The config object
6975  */
6976
6977
6978 Roo.bootstrap.breadcrumb.Nav = function(config){
6979     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6980     
6981     
6982 };
6983
6984 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6985     
6986     getAutoCreate : function()
6987     {
6988
6989         var cfg = {
6990             tag: 'nav',
6991             cn : [
6992                 {
6993                     tag : 'ol',
6994                     cls : 'breadcrumb'
6995                 }
6996             ]
6997             
6998         };
6999           
7000         return cfg;
7001     },
7002     
7003     initEvents: function()
7004     {
7005         this.olEl = this.el.select('ol',true).first();    
7006     },
7007     getChildContainer : function()
7008     {
7009         return this.olEl;  
7010     }
7011     
7012 });
7013
7014  /*
7015  * - LGPL
7016  *
7017  *  Breadcrumb Item
7018  * 
7019  */
7020
7021
7022 /**
7023  * @class Roo.bootstrap.breadcrumb.Nav
7024  * @extends Roo.bootstrap.Component
7025  * Bootstrap Breadcrumb Nav Class
7026  *  
7027  * @children Roo.bootstrap.breadcrumb.Component
7028  * @cfg {String} html the content of the link.
7029  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7030  * @cfg {Boolean} active is it active
7031
7032  * 
7033  * @constructor
7034  * Create a new breadcrumb.Nav
7035  * @param {Object} config The config object
7036  */
7037
7038 Roo.bootstrap.breadcrumb.Item = function(config){
7039     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7040     this.addEvents({
7041         // img events
7042         /**
7043          * @event click
7044          * The img click event for the img.
7045          * @param {Roo.EventObject} e
7046          */
7047         "click" : true
7048     });
7049     
7050 };
7051
7052 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7053     
7054     href: false,
7055     html : '',
7056     
7057     getAutoCreate : function()
7058     {
7059
7060         var cfg = {
7061             tag: 'li',
7062             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7063         };
7064         if (this.href !== false) {
7065             cfg.cn = [{
7066                 tag : 'a',
7067                 href : this.href,
7068                 html : this.html
7069             }];
7070         } else {
7071             cfg.html = this.html;
7072         }
7073         
7074         return cfg;
7075     },
7076     
7077     initEvents: function()
7078     {
7079         if (this.href) {
7080             this.el.select('a', true).first().on('click',this.onClick, this)
7081         }
7082         
7083     },
7084     onClick : function(e)
7085     {
7086         e.preventDefault();
7087         this.fireEvent('click',this,  e);
7088     }
7089     
7090 });
7091
7092  /*
7093  * - LGPL
7094  *
7095  * row
7096  * 
7097  */
7098
7099 /**
7100  * @class Roo.bootstrap.Row
7101  * @extends Roo.bootstrap.Component
7102  * @children Roo.bootstrap.Component
7103  * Bootstrap Row class (contains columns...)
7104  * 
7105  * @constructor
7106  * Create a new Row
7107  * @param {Object} config The config object
7108  */
7109
7110 Roo.bootstrap.Row = function(config){
7111     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7112 };
7113
7114 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7115     
7116     getAutoCreate : function(){
7117        return {
7118             cls: 'row clearfix'
7119        };
7120     }
7121     
7122     
7123 });
7124
7125  
7126
7127  /*
7128  * - LGPL
7129  *
7130  * pagination
7131  * 
7132  */
7133
7134 /**
7135  * @class Roo.bootstrap.Pagination
7136  * @extends Roo.bootstrap.Component
7137  * Bootstrap Pagination class
7138  * @cfg {String} size xs | sm | md | lg
7139  * @cfg {Boolean} inverse false | true
7140  * 
7141  * @constructor
7142  * Create a new Pagination
7143  * @param {Object} config The config object
7144  */
7145
7146 Roo.bootstrap.Pagination = function(config){
7147     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7148 };
7149
7150 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7151     
7152     cls: false,
7153     size: false,
7154     inverse: false,
7155     
7156     getAutoCreate : function(){
7157         var cfg = {
7158             tag: 'ul',
7159                 cls: 'pagination'
7160         };
7161         if (this.inverse) {
7162             cfg.cls += ' inverse';
7163         }
7164         if (this.html) {
7165             cfg.html=this.html;
7166         }
7167         if (this.cls) {
7168             cfg.cls += " " + this.cls;
7169         }
7170         return cfg;
7171     }
7172    
7173 });
7174
7175  
7176
7177  /*
7178  * - LGPL
7179  *
7180  * Pagination item
7181  * 
7182  */
7183
7184
7185 /**
7186  * @class Roo.bootstrap.PaginationItem
7187  * @extends Roo.bootstrap.Component
7188  * Bootstrap PaginationItem class
7189  * @cfg {String} html text
7190  * @cfg {String} href the link
7191  * @cfg {Boolean} preventDefault (true | false) default true
7192  * @cfg {Boolean} active (true | false) default false
7193  * @cfg {Boolean} disabled default false
7194  * 
7195  * 
7196  * @constructor
7197  * Create a new PaginationItem
7198  * @param {Object} config The config object
7199  */
7200
7201
7202 Roo.bootstrap.PaginationItem = function(config){
7203     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7204     this.addEvents({
7205         // raw events
7206         /**
7207          * @event click
7208          * The raw click event for the entire grid.
7209          * @param {Roo.EventObject} e
7210          */
7211         "click" : true
7212     });
7213 };
7214
7215 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7216     
7217     href : false,
7218     html : false,
7219     preventDefault: true,
7220     active : false,
7221     cls : false,
7222     disabled: false,
7223     
7224     getAutoCreate : function(){
7225         var cfg= {
7226             tag: 'li',
7227             cn: [
7228                 {
7229                     tag : 'a',
7230                     href : this.href ? this.href : '#',
7231                     html : this.html ? this.html : ''
7232                 }
7233             ]
7234         };
7235         
7236         if(this.cls){
7237             cfg.cls = this.cls;
7238         }
7239         
7240         if(this.disabled){
7241             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7242         }
7243         
7244         if(this.active){
7245             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7246         }
7247         
7248         return cfg;
7249     },
7250     
7251     initEvents: function() {
7252         
7253         this.el.on('click', this.onClick, this);
7254         
7255     },
7256     onClick : function(e)
7257     {
7258         Roo.log('PaginationItem on click ');
7259         if(this.preventDefault){
7260             e.preventDefault();
7261         }
7262         
7263         if(this.disabled){
7264             return;
7265         }
7266         
7267         this.fireEvent('click', this, e);
7268     }
7269    
7270 });
7271
7272  
7273
7274  /*
7275  * - LGPL
7276  *
7277  * slider
7278  * 
7279  */
7280
7281
7282 /**
7283  * @class Roo.bootstrap.Slider
7284  * @extends Roo.bootstrap.Component
7285  * Bootstrap Slider class
7286  *    
7287  * @constructor
7288  * Create a new Slider
7289  * @param {Object} config The config object
7290  */
7291
7292 Roo.bootstrap.Slider = function(config){
7293     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7294 };
7295
7296 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7297     
7298     getAutoCreate : function(){
7299         
7300         var cfg = {
7301             tag: 'div',
7302             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7303             cn: [
7304                 {
7305                     tag: 'a',
7306                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7307                 }
7308             ]
7309         };
7310         
7311         return cfg;
7312     }
7313    
7314 });
7315
7316  /*
7317  * Based on:
7318  * Ext JS Library 1.1.1
7319  * Copyright(c) 2006-2007, Ext JS, LLC.
7320  *
7321  * Originally Released Under LGPL - original licence link has changed is not relivant.
7322  *
7323  * Fork - LGPL
7324  * <script type="text/javascript">
7325  */
7326  /**
7327  * @extends Roo.dd.DDProxy
7328  * @class Roo.grid.SplitDragZone
7329  * Support for Column Header resizing
7330  * @constructor
7331  * @param {Object} config
7332  */
7333 // private
7334 // This is a support class used internally by the Grid components
7335 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7336     this.grid = grid;
7337     this.view = grid.getView();
7338     this.proxy = this.view.resizeProxy;
7339     Roo.grid.SplitDragZone.superclass.constructor.call(
7340         this,
7341         hd, // ID
7342         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7343         {  // CONFIG
7344             dragElId : Roo.id(this.proxy.dom),
7345             resizeFrame:false
7346         }
7347     );
7348     
7349     this.setHandleElId(Roo.id(hd));
7350     if (hd2 !== false) {
7351         this.setOuterHandleElId(Roo.id(hd2));
7352     }
7353     
7354     this.scroll = false;
7355 };
7356 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7357     fly: Roo.Element.fly,
7358
7359     b4StartDrag : function(x, y){
7360         this.view.headersDisabled = true;
7361         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7362                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7363         );
7364         this.proxy.setHeight(h);
7365         
7366         // for old system colWidth really stored the actual width?
7367         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7368         // which in reality did not work.. - it worked only for fixed sizes
7369         // for resizable we need to use actual sizes.
7370         var w = this.cm.getColumnWidth(this.cellIndex);
7371         if (!this.view.mainWrap) {
7372             // bootstrap.
7373             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7374         }
7375         
7376         
7377         
7378         // this was w-this.grid.minColumnWidth;
7379         // doesnt really make sense? - w = thie curren width or the rendered one?
7380         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7381         this.resetConstraints();
7382         this.setXConstraint(minw, 1000);
7383         this.setYConstraint(0, 0);
7384         this.minX = x - minw;
7385         this.maxX = x + 1000;
7386         this.startPos = x;
7387         if (!this.view.mainWrap) { // this is Bootstrap code..
7388             this.getDragEl().style.display='block';
7389         }
7390         
7391         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7392     },
7393
7394
7395     handleMouseDown : function(e){
7396         ev = Roo.EventObject.setEvent(e);
7397         var t = this.fly(ev.getTarget());
7398         if(t.hasClass("x-grid-split")){
7399             this.cellIndex = this.view.getCellIndex(t.dom);
7400             this.split = t.dom;
7401             this.cm = this.grid.colModel;
7402             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7403                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7404             }
7405         }
7406     },
7407
7408     endDrag : function(e){
7409         this.view.headersDisabled = false;
7410         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7411         var diff = endX - this.startPos;
7412         // 
7413         var w = this.cm.getColumnWidth(this.cellIndex);
7414         if (!this.view.mainWrap) {
7415             w = 0;
7416         }
7417         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7418     },
7419
7420     autoOffset : function(){
7421         this.setDelta(0,0);
7422     }
7423 });/*
7424  * Based on:
7425  * Ext JS Library 1.1.1
7426  * Copyright(c) 2006-2007, Ext JS, LLC.
7427  *
7428  * Originally Released Under LGPL - original licence link has changed is not relivant.
7429  *
7430  * Fork - LGPL
7431  * <script type="text/javascript">
7432  */
7433
7434 /**
7435  * @class Roo.grid.AbstractSelectionModel
7436  * @extends Roo.util.Observable
7437  * @abstract
7438  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7439  * implemented by descendant classes.  This class should not be directly instantiated.
7440  * @constructor
7441  */
7442 Roo.grid.AbstractSelectionModel = function(){
7443     this.locked = false;
7444     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7445 };
7446
7447 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7448     /** @ignore Called by the grid automatically. Do not call directly. */
7449     init : function(grid){
7450         this.grid = grid;
7451         this.initEvents();
7452     },
7453
7454     /**
7455      * Locks the selections.
7456      */
7457     lock : function(){
7458         this.locked = true;
7459     },
7460
7461     /**
7462      * Unlocks the selections.
7463      */
7464     unlock : function(){
7465         this.locked = false;
7466     },
7467
7468     /**
7469      * Returns true if the selections are locked.
7470      * @return {Boolean}
7471      */
7472     isLocked : function(){
7473         return this.locked;
7474     }
7475 });/*
7476  * Based on:
7477  * Ext JS Library 1.1.1
7478  * Copyright(c) 2006-2007, Ext JS, LLC.
7479  *
7480  * Originally Released Under LGPL - original licence link has changed is not relivant.
7481  *
7482  * Fork - LGPL
7483  * <script type="text/javascript">
7484  */
7485 /**
7486  * @extends Roo.grid.AbstractSelectionModel
7487  * @class Roo.grid.RowSelectionModel
7488  * The default SelectionModel used by {@link Roo.grid.Grid}.
7489  * It supports multiple selections and keyboard selection/navigation. 
7490  * @constructor
7491  * @param {Object} config
7492  */
7493 Roo.grid.RowSelectionModel = function(config){
7494     Roo.apply(this, config);
7495     this.selections = new Roo.util.MixedCollection(false, function(o){
7496         return o.id;
7497     });
7498
7499     this.last = false;
7500     this.lastActive = false;
7501
7502     this.addEvents({
7503         /**
7504         * @event selectionchange
7505         * Fires when the selection changes
7506         * @param {SelectionModel} this
7507         */
7508        "selectionchange" : true,
7509        /**
7510         * @event afterselectionchange
7511         * Fires after the selection changes (eg. by key press or clicking)
7512         * @param {SelectionModel} this
7513         */
7514        "afterselectionchange" : true,
7515        /**
7516         * @event beforerowselect
7517         * Fires when a row is selected being selected, return false to cancel.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         * @param {Boolean} keepExisting False if other selections will be cleared
7521         */
7522        "beforerowselect" : true,
7523        /**
7524         * @event rowselect
7525         * Fires when a row is selected.
7526         * @param {SelectionModel} this
7527         * @param {Number} rowIndex The selected index
7528         * @param {Roo.data.Record} r The record
7529         */
7530        "rowselect" : true,
7531        /**
7532         * @event rowdeselect
7533         * Fires when a row is deselected.
7534         * @param {SelectionModel} this
7535         * @param {Number} rowIndex The selected index
7536         */
7537         "rowdeselect" : true
7538     });
7539     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7540     this.locked = false;
7541 };
7542
7543 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7544     /**
7545      * @cfg {Boolean} singleSelect
7546      * True to allow selection of only one row at a time (defaults to false)
7547      */
7548     singleSelect : false,
7549
7550     // private
7551     initEvents : function(){
7552
7553         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7554             this.grid.on("mousedown", this.handleMouseDown, this);
7555         }else{ // allow click to work like normal
7556             this.grid.on("rowclick", this.handleDragableRowClick, this);
7557         }
7558         // bootstrap does not have a view..
7559         var view = this.grid.view ? this.grid.view : this.grid;
7560         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7561             "up" : function(e){
7562                 if(!e.shiftKey){
7563                     this.selectPrevious(e.shiftKey);
7564                 }else if(this.last !== false && this.lastActive !== false){
7565                     var last = this.last;
7566                     this.selectRange(this.last,  this.lastActive-1);
7567                     view.focusRow(this.lastActive);
7568                     if(last !== false){
7569                         this.last = last;
7570                     }
7571                 }else{
7572                     this.selectFirstRow();
7573                 }
7574                 this.fireEvent("afterselectionchange", this);
7575             },
7576             "down" : function(e){
7577                 if(!e.shiftKey){
7578                     this.selectNext(e.shiftKey);
7579                 }else if(this.last !== false && this.lastActive !== false){
7580                     var last = this.last;
7581                     this.selectRange(this.last,  this.lastActive+1);
7582                     view.focusRow(this.lastActive);
7583                     if(last !== false){
7584                         this.last = last;
7585                     }
7586                 }else{
7587                     this.selectFirstRow();
7588                 }
7589                 this.fireEvent("afterselectionchange", this);
7590             },
7591             scope: this
7592         });
7593
7594          
7595         view.on("refresh", this.onRefresh, this);
7596         view.on("rowupdated", this.onRowUpdated, this);
7597         view.on("rowremoved", this.onRemove, this);
7598     },
7599
7600     // private
7601     onRefresh : function(){
7602         var ds = this.grid.ds, i, v = this.grid.view;
7603         var s = this.selections;
7604         s.each(function(r){
7605             if((i = ds.indexOfId(r.id)) != -1){
7606                 v.onRowSelect(i);
7607                 s.add(ds.getAt(i)); // updating the selection relate data
7608             }else{
7609                 s.remove(r);
7610             }
7611         });
7612     },
7613
7614     // private
7615     onRemove : function(v, index, r){
7616         this.selections.remove(r);
7617     },
7618
7619     // private
7620     onRowUpdated : function(v, index, r){
7621         if(this.isSelected(r)){
7622             v.onRowSelect(index);
7623         }
7624     },
7625
7626     /**
7627      * Select records.
7628      * @param {Array} records The records to select
7629      * @param {Boolean} keepExisting (optional) True to keep existing selections
7630      */
7631     selectRecords : function(records, keepExisting){
7632         if(!keepExisting){
7633             this.clearSelections();
7634         }
7635         var ds = this.grid.ds;
7636         for(var i = 0, len = records.length; i < len; i++){
7637             this.selectRow(ds.indexOf(records[i]), true);
7638         }
7639     },
7640
7641     /**
7642      * Gets the number of selected rows.
7643      * @return {Number}
7644      */
7645     getCount : function(){
7646         return this.selections.length;
7647     },
7648
7649     /**
7650      * Selects the first row in the grid.
7651      */
7652     selectFirstRow : function(){
7653         this.selectRow(0);
7654     },
7655
7656     /**
7657      * Select the last row.
7658      * @param {Boolean} keepExisting (optional) True to keep existing selections
7659      */
7660     selectLastRow : function(keepExisting){
7661         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7662     },
7663
7664     /**
7665      * Selects the row immediately following the last selected row.
7666      * @param {Boolean} keepExisting (optional) True to keep existing selections
7667      */
7668     selectNext : function(keepExisting){
7669         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7670             this.selectRow(this.last+1, keepExisting);
7671             var view = this.grid.view ? this.grid.view : this.grid;
7672             view.focusRow(this.last);
7673         }
7674     },
7675
7676     /**
7677      * Selects the row that precedes the last selected row.
7678      * @param {Boolean} keepExisting (optional) True to keep existing selections
7679      */
7680     selectPrevious : function(keepExisting){
7681         if(this.last){
7682             this.selectRow(this.last-1, keepExisting);
7683             var view = this.grid.view ? this.grid.view : this.grid;
7684             view.focusRow(this.last);
7685         }
7686     },
7687
7688     /**
7689      * Returns the selected records
7690      * @return {Array} Array of selected records
7691      */
7692     getSelections : function(){
7693         return [].concat(this.selections.items);
7694     },
7695
7696     /**
7697      * Returns the first selected record.
7698      * @return {Record}
7699      */
7700     getSelected : function(){
7701         return this.selections.itemAt(0);
7702     },
7703
7704
7705     /**
7706      * Clears all selections.
7707      */
7708     clearSelections : function(fast){
7709         if(this.locked) {
7710             return;
7711         }
7712         if(fast !== true){
7713             var ds = this.grid.ds;
7714             var s = this.selections;
7715             s.each(function(r){
7716                 this.deselectRow(ds.indexOfId(r.id));
7717             }, this);
7718             s.clear();
7719         }else{
7720             this.selections.clear();
7721         }
7722         this.last = false;
7723     },
7724
7725
7726     /**
7727      * Selects all rows.
7728      */
7729     selectAll : function(){
7730         if(this.locked) {
7731             return;
7732         }
7733         this.selections.clear();
7734         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7735             this.selectRow(i, true);
7736         }
7737     },
7738
7739     /**
7740      * Returns True if there is a selection.
7741      * @return {Boolean}
7742      */
7743     hasSelection : function(){
7744         return this.selections.length > 0;
7745     },
7746
7747     /**
7748      * Returns True if the specified row is selected.
7749      * @param {Number/Record} record The record or index of the record to check
7750      * @return {Boolean}
7751      */
7752     isSelected : function(index){
7753         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7754         return (r && this.selections.key(r.id) ? true : false);
7755     },
7756
7757     /**
7758      * Returns True if the specified record id is selected.
7759      * @param {String} id The id of record to check
7760      * @return {Boolean}
7761      */
7762     isIdSelected : function(id){
7763         return (this.selections.key(id) ? true : false);
7764     },
7765
7766     // private
7767     handleMouseDown : function(e, t)
7768     {
7769         var view = this.grid.view ? this.grid.view : this.grid;
7770         var rowIndex;
7771         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7772             return;
7773         };
7774         if(e.shiftKey && this.last !== false){
7775             var last = this.last;
7776             this.selectRange(last, rowIndex, e.ctrlKey);
7777             this.last = last; // reset the last
7778             view.focusRow(rowIndex);
7779         }else{
7780             var isSelected = this.isSelected(rowIndex);
7781             if(e.button !== 0 && isSelected){
7782                 view.focusRow(rowIndex);
7783             }else if(e.ctrlKey && isSelected){
7784                 this.deselectRow(rowIndex);
7785             }else if(!isSelected){
7786                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7787                 view.focusRow(rowIndex);
7788             }
7789         }
7790         this.fireEvent("afterselectionchange", this);
7791     },
7792     // private
7793     handleDragableRowClick :  function(grid, rowIndex, e) 
7794     {
7795         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7796             this.selectRow(rowIndex, false);
7797             var view = this.grid.view ? this.grid.view : this.grid;
7798             view.focusRow(rowIndex);
7799              this.fireEvent("afterselectionchange", this);
7800         }
7801     },
7802     
7803     /**
7804      * Selects multiple rows.
7805      * @param {Array} rows Array of the indexes of the row to select
7806      * @param {Boolean} keepExisting (optional) True to keep existing selections
7807      */
7808     selectRows : function(rows, keepExisting){
7809         if(!keepExisting){
7810             this.clearSelections();
7811         }
7812         for(var i = 0, len = rows.length; i < len; i++){
7813             this.selectRow(rows[i], true);
7814         }
7815     },
7816
7817     /**
7818      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7819      * @param {Number} startRow The index of the first row in the range
7820      * @param {Number} endRow The index of the last row in the range
7821      * @param {Boolean} keepExisting (optional) True to retain existing selections
7822      */
7823     selectRange : function(startRow, endRow, keepExisting){
7824         if(this.locked) {
7825             return;
7826         }
7827         if(!keepExisting){
7828             this.clearSelections();
7829         }
7830         if(startRow <= endRow){
7831             for(var i = startRow; i <= endRow; i++){
7832                 this.selectRow(i, true);
7833             }
7834         }else{
7835             for(var i = startRow; i >= endRow; i--){
7836                 this.selectRow(i, true);
7837             }
7838         }
7839     },
7840
7841     /**
7842      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7843      * @param {Number} startRow The index of the first row in the range
7844      * @param {Number} endRow The index of the last row in the range
7845      */
7846     deselectRange : function(startRow, endRow, preventViewNotify){
7847         if(this.locked) {
7848             return;
7849         }
7850         for(var i = startRow; i <= endRow; i++){
7851             this.deselectRow(i, preventViewNotify);
7852         }
7853     },
7854
7855     /**
7856      * Selects a row.
7857      * @param {Number} row The index of the row to select
7858      * @param {Boolean} keepExisting (optional) True to keep existing selections
7859      */
7860     selectRow : function(index, keepExisting, preventViewNotify){
7861         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7862             return;
7863         }
7864         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7865             if(!keepExisting || this.singleSelect){
7866                 this.clearSelections();
7867             }
7868             var r = this.grid.ds.getAt(index);
7869             this.selections.add(r);
7870             this.last = this.lastActive = index;
7871             if(!preventViewNotify){
7872                 var view = this.grid.view ? this.grid.view : this.grid;
7873                 view.onRowSelect(index);
7874             }
7875             this.fireEvent("rowselect", this, index, r);
7876             this.fireEvent("selectionchange", this);
7877         }
7878     },
7879
7880     /**
7881      * Deselects a row.
7882      * @param {Number} row The index of the row to deselect
7883      */
7884     deselectRow : function(index, preventViewNotify){
7885         if(this.locked) {
7886             return;
7887         }
7888         if(this.last == index){
7889             this.last = false;
7890         }
7891         if(this.lastActive == index){
7892             this.lastActive = false;
7893         }
7894         var r = this.grid.ds.getAt(index);
7895         this.selections.remove(r);
7896         if(!preventViewNotify){
7897             var view = this.grid.view ? this.grid.view : this.grid;
7898             view.onRowDeselect(index);
7899         }
7900         this.fireEvent("rowdeselect", this, index);
7901         this.fireEvent("selectionchange", this);
7902     },
7903
7904     // private
7905     restoreLast : function(){
7906         if(this._last){
7907             this.last = this._last;
7908         }
7909     },
7910
7911     // private
7912     acceptsNav : function(row, col, cm){
7913         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7914     },
7915
7916     // private
7917     onEditorKey : function(field, e){
7918         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7919         if(k == e.TAB){
7920             e.stopEvent();
7921             ed.completeEdit();
7922             if(e.shiftKey){
7923                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7924             }else{
7925                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7926             }
7927         }else if(k == e.ENTER && !e.ctrlKey){
7928             e.stopEvent();
7929             ed.completeEdit();
7930             if(e.shiftKey){
7931                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7932             }else{
7933                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7934             }
7935         }else if(k == e.ESC){
7936             ed.cancelEdit();
7937         }
7938         if(newCell){
7939             g.startEditing(newCell[0], newCell[1]);
7940         }
7941     }
7942 });/*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952  
7953
7954 /**
7955  * @class Roo.grid.ColumnModel
7956  * @extends Roo.util.Observable
7957  * This is the default implementation of a ColumnModel used by the Grid. It defines
7958  * the columns in the grid.
7959  * <br>Usage:<br>
7960  <pre><code>
7961  var colModel = new Roo.grid.ColumnModel([
7962         {header: "Ticker", width: 60, sortable: true, locked: true},
7963         {header: "Company Name", width: 150, sortable: true},
7964         {header: "Market Cap.", width: 100, sortable: true},
7965         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7966         {header: "Employees", width: 100, sortable: true, resizable: false}
7967  ]);
7968  </code></pre>
7969  * <p>
7970  
7971  * The config options listed for this class are options which may appear in each
7972  * individual column definition.
7973  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7974  * @constructor
7975  * @param {Object} config An Array of column config objects. See this class's
7976  * config objects for details.
7977 */
7978 Roo.grid.ColumnModel = function(config){
7979         /**
7980      * The config passed into the constructor
7981      */
7982     this.config = []; //config;
7983     this.lookup = {};
7984
7985     // if no id, create one
7986     // if the column does not have a dataIndex mapping,
7987     // map it to the order it is in the config
7988     for(var i = 0, len = config.length; i < len; i++){
7989         this.addColumn(config[i]);
7990         
7991     }
7992
7993     /**
7994      * The width of columns which have no width specified (defaults to 100)
7995      * @type Number
7996      */
7997     this.defaultWidth = 100;
7998
7999     /**
8000      * Default sortable of columns which have no sortable specified (defaults to false)
8001      * @type Boolean
8002      */
8003     this.defaultSortable = false;
8004
8005     this.addEvents({
8006         /**
8007              * @event widthchange
8008              * Fires when the width of a column changes.
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Number} newWidth The new width
8012              */
8013             "widthchange": true,
8014         /**
8015              * @event headerchange
8016              * Fires when the text of a header changes.
8017              * @param {ColumnModel} this
8018              * @param {Number} columnIndex The column index
8019              * @param {Number} newText The new header text
8020              */
8021             "headerchange": true,
8022         /**
8023              * @event hiddenchange
8024              * Fires when a column is hidden or "unhidden".
8025              * @param {ColumnModel} this
8026              * @param {Number} columnIndex The column index
8027              * @param {Boolean} hidden true if hidden, false otherwise
8028              */
8029             "hiddenchange": true,
8030             /**
8031          * @event columnmoved
8032          * Fires when a column is moved.
8033          * @param {ColumnModel} this
8034          * @param {Number} oldIndex
8035          * @param {Number} newIndex
8036          */
8037         "columnmoved" : true,
8038         /**
8039          * @event columlockchange
8040          * Fires when a column's locked state is changed
8041          * @param {ColumnModel} this
8042          * @param {Number} colIndex
8043          * @param {Boolean} locked true if locked
8044          */
8045         "columnlockchange" : true
8046     });
8047     Roo.grid.ColumnModel.superclass.constructor.call(this);
8048 };
8049 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8050     /**
8051      * @cfg {String} header The header text to display in the Grid view.
8052      */
8053         /**
8054      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8055      */
8056         /**
8057      * @cfg {String} smHeader Header at Bootsrap Small width
8058      */
8059         /**
8060      * @cfg {String} mdHeader Header at Bootsrap Medium width
8061      */
8062         /**
8063      * @cfg {String} lgHeader Header at Bootsrap Large width
8064      */
8065         /**
8066      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8067      */
8068     /**
8069      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8070      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8071      * specified, the column's index is used as an index into the Record's data Array.
8072      */
8073     /**
8074      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8075      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8076      */
8077     /**
8078      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8079      * Defaults to the value of the {@link #defaultSortable} property.
8080      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8081      */
8082     /**
8083      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8084      */
8085     /**
8086      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8087      */
8088     /**
8089      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8090      */
8091     /**
8092      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8093      */
8094     /**
8095      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8096      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8097      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8098      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8099      */
8100        /**
8101      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8102      */
8103     /**
8104      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8105      */
8106     /**
8107      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8108      */
8109     /**
8110      * @cfg {String} cursor (Optional)
8111      */
8112     /**
8113      * @cfg {String} tooltip (Optional)
8114      */
8115     /**
8116      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118     /**
8119      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8120      */
8121     /**
8122      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8123      */
8124     /**
8125      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8126      */
8127         /**
8128      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8129      */
8130     /**
8131      * Returns the id of the column at the specified index.
8132      * @param {Number} index The column index
8133      * @return {String} the id
8134      */
8135     getColumnId : function(index){
8136         return this.config[index].id;
8137     },
8138
8139     /**
8140      * Returns the column for a specified id.
8141      * @param {String} id The column id
8142      * @return {Object} the column
8143      */
8144     getColumnById : function(id){
8145         return this.lookup[id];
8146     },
8147
8148     
8149     /**
8150      * Returns the column Object for a specified dataIndex.
8151      * @param {String} dataIndex The column dataIndex
8152      * @return {Object|Boolean} the column or false if not found
8153      */
8154     getColumnByDataIndex: function(dataIndex){
8155         var index = this.findColumnIndex(dataIndex);
8156         return index > -1 ? this.config[index] : false;
8157     },
8158     
8159     /**
8160      * Returns the index for a specified column id.
8161      * @param {String} id The column id
8162      * @return {Number} the index, or -1 if not found
8163      */
8164     getIndexById : function(id){
8165         for(var i = 0, len = this.config.length; i < len; i++){
8166             if(this.config[i].id == id){
8167                 return i;
8168             }
8169         }
8170         return -1;
8171     },
8172     
8173     /**
8174      * Returns the index for a specified column dataIndex.
8175      * @param {String} dataIndex The column dataIndex
8176      * @return {Number} the index, or -1 if not found
8177      */
8178     
8179     findColumnIndex : function(dataIndex){
8180         for(var i = 0, len = this.config.length; i < len; i++){
8181             if(this.config[i].dataIndex == dataIndex){
8182                 return i;
8183             }
8184         }
8185         return -1;
8186     },
8187     
8188     
8189     moveColumn : function(oldIndex, newIndex){
8190         var c = this.config[oldIndex];
8191         this.config.splice(oldIndex, 1);
8192         this.config.splice(newIndex, 0, c);
8193         this.dataMap = null;
8194         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8195     },
8196
8197     isLocked : function(colIndex){
8198         return this.config[colIndex].locked === true;
8199     },
8200
8201     setLocked : function(colIndex, value, suppressEvent){
8202         if(this.isLocked(colIndex) == value){
8203             return;
8204         }
8205         this.config[colIndex].locked = value;
8206         if(!suppressEvent){
8207             this.fireEvent("columnlockchange", this, colIndex, value);
8208         }
8209     },
8210
8211     getTotalLockedWidth : function(){
8212         var totalWidth = 0;
8213         for(var i = 0; i < this.config.length; i++){
8214             if(this.isLocked(i) && !this.isHidden(i)){
8215                 this.totalWidth += this.getColumnWidth(i);
8216             }
8217         }
8218         return totalWidth;
8219     },
8220
8221     getLockedCount : function(){
8222         for(var i = 0, len = this.config.length; i < len; i++){
8223             if(!this.isLocked(i)){
8224                 return i;
8225             }
8226         }
8227         
8228         return this.config.length;
8229     },
8230
8231     /**
8232      * Returns the number of columns.
8233      * @return {Number}
8234      */
8235     getColumnCount : function(visibleOnly){
8236         if(visibleOnly === true){
8237             var c = 0;
8238             for(var i = 0, len = this.config.length; i < len; i++){
8239                 if(!this.isHidden(i)){
8240                     c++;
8241                 }
8242             }
8243             return c;
8244         }
8245         return this.config.length;
8246     },
8247
8248     /**
8249      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8250      * @param {Function} fn
8251      * @param {Object} scope (optional)
8252      * @return {Array} result
8253      */
8254     getColumnsBy : function(fn, scope){
8255         var r = [];
8256         for(var i = 0, len = this.config.length; i < len; i++){
8257             var c = this.config[i];
8258             if(fn.call(scope||this, c, i) === true){
8259                 r[r.length] = c;
8260             }
8261         }
8262         return r;
8263     },
8264
8265     /**
8266      * Returns true if the specified column is sortable.
8267      * @param {Number} col The column index
8268      * @return {Boolean}
8269      */
8270     isSortable : function(col){
8271         if(typeof this.config[col].sortable == "undefined"){
8272             return this.defaultSortable;
8273         }
8274         return this.config[col].sortable;
8275     },
8276
8277     /**
8278      * Returns the rendering (formatting) function defined for the column.
8279      * @param {Number} col The column index.
8280      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8281      */
8282     getRenderer : function(col){
8283         if(!this.config[col].renderer){
8284             return Roo.grid.ColumnModel.defaultRenderer;
8285         }
8286         return this.config[col].renderer;
8287     },
8288
8289     /**
8290      * Sets the rendering (formatting) function for a column.
8291      * @param {Number} col The column index
8292      * @param {Function} fn The function to use to process the cell's raw data
8293      * to return HTML markup for the grid view. The render function is called with
8294      * the following parameters:<ul>
8295      * <li>Data value.</li>
8296      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8297      * <li>css A CSS style string to apply to the table cell.</li>
8298      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8299      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8300      * <li>Row index</li>
8301      * <li>Column index</li>
8302      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8303      */
8304     setRenderer : function(col, fn){
8305         this.config[col].renderer = fn;
8306     },
8307
8308     /**
8309      * Returns the width for the specified column.
8310      * @param {Number} col The column index
8311      * @param (optional) {String} gridSize bootstrap width size.
8312      * @return {Number}
8313      */
8314     getColumnWidth : function(col, gridSize)
8315         {
8316                 var cfg = this.config[col];
8317                 
8318                 if (typeof(gridSize) == 'undefined') {
8319                         return cfg.width * 1 || this.defaultWidth;
8320                 }
8321                 if (gridSize === false) { // if we set it..
8322                         return cfg.width || false;
8323                 }
8324                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8325                 
8326                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8327                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8328                                 continue;
8329                         }
8330                         return cfg[ sizes[i] ];
8331                 }
8332                 return 1;
8333                 
8334     },
8335
8336     /**
8337      * Sets the width for a column.
8338      * @param {Number} col The column index
8339      * @param {Number} width The new width
8340      */
8341     setColumnWidth : function(col, width, suppressEvent){
8342         this.config[col].width = width;
8343         this.totalWidth = null;
8344         if(!suppressEvent){
8345              this.fireEvent("widthchange", this, col, width);
8346         }
8347     },
8348
8349     /**
8350      * Returns the total width of all columns.
8351      * @param {Boolean} includeHidden True to include hidden column widths
8352      * @return {Number}
8353      */
8354     getTotalWidth : function(includeHidden){
8355         if(!this.totalWidth){
8356             this.totalWidth = 0;
8357             for(var i = 0, len = this.config.length; i < len; i++){
8358                 if(includeHidden || !this.isHidden(i)){
8359                     this.totalWidth += this.getColumnWidth(i);
8360                 }
8361             }
8362         }
8363         return this.totalWidth;
8364     },
8365
8366     /**
8367      * Returns the header for the specified column.
8368      * @param {Number} col The column index
8369      * @return {String}
8370      */
8371     getColumnHeader : function(col){
8372         return this.config[col].header;
8373     },
8374
8375     /**
8376      * Sets the header for a column.
8377      * @param {Number} col The column index
8378      * @param {String} header The new header
8379      */
8380     setColumnHeader : function(col, header){
8381         this.config[col].header = header;
8382         this.fireEvent("headerchange", this, col, header);
8383     },
8384
8385     /**
8386      * Returns the tooltip for the specified column.
8387      * @param {Number} col The column index
8388      * @return {String}
8389      */
8390     getColumnTooltip : function(col){
8391             return this.config[col].tooltip;
8392     },
8393     /**
8394      * Sets the tooltip for a column.
8395      * @param {Number} col The column index
8396      * @param {String} tooltip The new tooltip
8397      */
8398     setColumnTooltip : function(col, tooltip){
8399             this.config[col].tooltip = tooltip;
8400     },
8401
8402     /**
8403      * Returns the dataIndex for the specified column.
8404      * @param {Number} col The column index
8405      * @return {Number}
8406      */
8407     getDataIndex : function(col){
8408         return this.config[col].dataIndex;
8409     },
8410
8411     /**
8412      * Sets the dataIndex for a column.
8413      * @param {Number} col The column index
8414      * @param {Number} dataIndex The new dataIndex
8415      */
8416     setDataIndex : function(col, dataIndex){
8417         this.config[col].dataIndex = dataIndex;
8418     },
8419
8420     
8421     
8422     /**
8423      * Returns true if the cell is editable.
8424      * @param {Number} colIndex The column index
8425      * @param {Number} rowIndex The row index - this is nto actually used..?
8426      * @return {Boolean}
8427      */
8428     isCellEditable : function(colIndex, rowIndex){
8429         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8430     },
8431
8432     /**
8433      * Returns the editor defined for the cell/column.
8434      * return false or null to disable editing.
8435      * @param {Number} colIndex The column index
8436      * @param {Number} rowIndex The row index
8437      * @return {Object}
8438      */
8439     getCellEditor : function(colIndex, rowIndex){
8440         return this.config[colIndex].editor;
8441     },
8442
8443     /**
8444      * Sets if a column is editable.
8445      * @param {Number} col The column index
8446      * @param {Boolean} editable True if the column is editable
8447      */
8448     setEditable : function(col, editable){
8449         this.config[col].editable = editable;
8450     },
8451
8452
8453     /**
8454      * Returns true if the column is hidden.
8455      * @param {Number} colIndex The column index
8456      * @return {Boolean}
8457      */
8458     isHidden : function(colIndex){
8459         return this.config[colIndex].hidden;
8460     },
8461
8462
8463     /**
8464      * Returns true if the column width cannot be changed
8465      */
8466     isFixed : function(colIndex){
8467         return this.config[colIndex].fixed;
8468     },
8469
8470     /**
8471      * Returns true if the column can be resized
8472      * @return {Boolean}
8473      */
8474     isResizable : function(colIndex){
8475         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8476     },
8477     /**
8478      * Sets if a column is hidden.
8479      * @param {Number} colIndex The column index
8480      * @param {Boolean} hidden True if the column is hidden
8481      */
8482     setHidden : function(colIndex, hidden){
8483         this.config[colIndex].hidden = hidden;
8484         this.totalWidth = null;
8485         this.fireEvent("hiddenchange", this, colIndex, hidden);
8486     },
8487
8488     /**
8489      * Sets the editor for a column.
8490      * @param {Number} col The column index
8491      * @param {Object} editor The editor object
8492      */
8493     setEditor : function(col, editor){
8494         this.config[col].editor = editor;
8495     },
8496     /**
8497      * Add a column (experimental...) - defaults to adding to the end..
8498      * @param {Object} config 
8499     */
8500     addColumn : function(c)
8501     {
8502     
8503         var i = this.config.length;
8504         this.config[i] = c;
8505         
8506         if(typeof c.dataIndex == "undefined"){
8507             c.dataIndex = i;
8508         }
8509         if(typeof c.renderer == "string"){
8510             c.renderer = Roo.util.Format[c.renderer];
8511         }
8512         if(typeof c.id == "undefined"){
8513             c.id = Roo.id();
8514         }
8515         if(c.editor && c.editor.xtype){
8516             c.editor  = Roo.factory(c.editor, Roo.grid);
8517         }
8518         if(c.editor && c.editor.isFormField){
8519             c.editor = new Roo.grid.GridEditor(c.editor);
8520         }
8521         this.lookup[c.id] = c;
8522     }
8523     
8524 });
8525
8526 Roo.grid.ColumnModel.defaultRenderer = function(value)
8527 {
8528     if(typeof value == "object") {
8529         return value;
8530     }
8531         if(typeof value == "string" && value.length < 1){
8532             return "&#160;";
8533         }
8534     
8535         return String.format("{0}", value);
8536 };
8537
8538 // Alias for backwards compatibility
8539 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8540 /*
8541  * Based on:
8542  * Ext JS Library 1.1.1
8543  * Copyright(c) 2006-2007, Ext JS, LLC.
8544  *
8545  * Originally Released Under LGPL - original licence link has changed is not relivant.
8546  *
8547  * Fork - LGPL
8548  * <script type="text/javascript">
8549  */
8550  
8551 /**
8552  * @class Roo.LoadMask
8553  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8554  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8555  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8556  * element's UpdateManager load indicator and will be destroyed after the initial load.
8557  * @constructor
8558  * Create a new LoadMask
8559  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8560  * @param {Object} config The config object
8561  */
8562 Roo.LoadMask = function(el, config){
8563     this.el = Roo.get(el);
8564     Roo.apply(this, config);
8565     if(this.store){
8566         this.store.on('beforeload', this.onBeforeLoad, this);
8567         this.store.on('load', this.onLoad, this);
8568         this.store.on('loadexception', this.onLoadException, this);
8569         this.removeMask = false;
8570     }else{
8571         var um = this.el.getUpdateManager();
8572         um.showLoadIndicator = false; // disable the default indicator
8573         um.on('beforeupdate', this.onBeforeLoad, this);
8574         um.on('update', this.onLoad, this);
8575         um.on('failure', this.onLoad, this);
8576         this.removeMask = true;
8577     }
8578 };
8579
8580 Roo.LoadMask.prototype = {
8581     /**
8582      * @cfg {Boolean} removeMask
8583      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8584      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8585      */
8586     removeMask : false,
8587     /**
8588      * @cfg {String} msg
8589      * The text to display in a centered loading message box (defaults to 'Loading...')
8590      */
8591     msg : 'Loading...',
8592     /**
8593      * @cfg {String} msgCls
8594      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8595      */
8596     msgCls : 'x-mask-loading',
8597
8598     /**
8599      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8600      * @type Boolean
8601      */
8602     disabled: false,
8603
8604     /**
8605      * Disables the mask to prevent it from being displayed
8606      */
8607     disable : function(){
8608        this.disabled = true;
8609     },
8610
8611     /**
8612      * Enables the mask so that it can be displayed
8613      */
8614     enable : function(){
8615         this.disabled = false;
8616     },
8617     
8618     onLoadException : function()
8619     {
8620         Roo.log(arguments);
8621         
8622         if (typeof(arguments[3]) != 'undefined') {
8623             Roo.MessageBox.alert("Error loading",arguments[3]);
8624         } 
8625         /*
8626         try {
8627             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8628                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8629             }   
8630         } catch(e) {
8631             
8632         }
8633         */
8634     
8635         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8636     },
8637     // private
8638     onLoad : function()
8639     {
8640         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8641     },
8642
8643     // private
8644     onBeforeLoad : function(){
8645         if(!this.disabled){
8646             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8647         }
8648     },
8649
8650     // private
8651     destroy : function(){
8652         if(this.store){
8653             this.store.un('beforeload', this.onBeforeLoad, this);
8654             this.store.un('load', this.onLoad, this);
8655             this.store.un('loadexception', this.onLoadException, this);
8656         }else{
8657             var um = this.el.getUpdateManager();
8658             um.un('beforeupdate', this.onBeforeLoad, this);
8659             um.un('update', this.onLoad, this);
8660             um.un('failure', this.onLoad, this);
8661         }
8662     }
8663 };/**
8664  * @class Roo.bootstrap.Table
8665  * @licence LGBL
8666  * @extends Roo.bootstrap.Component
8667  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8668  * Similar to Roo.grid.Grid
8669  * <pre><code>
8670  var table = Roo.factory({
8671     xtype : 'Table',
8672     xns : Roo.bootstrap,
8673     autoSizeColumns: true,
8674     
8675     
8676     store : {
8677         xtype : 'Store',
8678         xns : Roo.data,
8679         remoteSort : true,
8680         sortInfo : { direction : 'ASC', field: 'name' },
8681         proxy : {
8682            xtype : 'HttpProxy',
8683            xns : Roo.data,
8684            method : 'GET',
8685            url : 'https://example.com/some.data.url.json'
8686         },
8687         reader : {
8688            xtype : 'JsonReader',
8689            xns : Roo.data,
8690            fields : [ 'id', 'name', whatever' ],
8691            id : 'id',
8692            root : 'data'
8693         }
8694     },
8695     cm : [
8696         {
8697             xtype : 'ColumnModel',
8698             xns : Roo.grid,
8699             align : 'center',
8700             cursor : 'pointer',
8701             dataIndex : 'is_in_group',
8702             header : "Name",
8703             sortable : true,
8704             renderer : function(v, x , r) {  
8705             
8706                 return String.format("{0}", v)
8707             }
8708             width : 3
8709         } // more columns..
8710     ],
8711     selModel : {
8712         xtype : 'RowSelectionModel',
8713         xns : Roo.bootstrap.Table
8714         // you can add listeners to catch selection change here....
8715     }
8716      
8717
8718  });
8719  // set any options
8720  grid.render(Roo.get("some-div"));
8721 </code></pre>
8722
8723 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8724
8725
8726
8727  *
8728  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8729  * @cfg {Roo.data.Store} store The data store to use
8730  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8731  * 
8732  * @cfg {String} cls table class
8733  *
8734  * 
8735  * @cfg {boolean} striped Should the rows be alternative striped
8736  * @cfg {boolean} bordered Add borders to the table
8737  * @cfg {boolean} hover Add hover highlighting
8738  * @cfg {boolean} condensed Format condensed
8739  * @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,
8740  *                also adds table-responsive (see bootstrap docs for details)
8741  * @cfg {Boolean} loadMask (true|false) default false
8742  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8743  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8744  * @cfg {Boolean} rowSelection (true|false) default false
8745  * @cfg {Boolean} cellSelection (true|false) default false
8746  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8747  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8748  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8749  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8750  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8751  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8752  * 
8753  * @constructor
8754  * Create a new Table
8755  * @param {Object} config The config object
8756  */
8757
8758 Roo.bootstrap.Table = function(config)
8759 {
8760     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8761      
8762     // BC...
8763     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8764     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8765     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8766     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8767     
8768     this.view = this; // compat with grid.
8769     
8770     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8771     if (this.sm) {
8772         this.sm.grid = this;
8773         this.selModel = Roo.factory(this.sm, Roo.grid);
8774         this.sm = this.selModel;
8775         this.sm.xmodule = this.xmodule || false;
8776     }
8777     
8778     if (this.cm && typeof(this.cm.config) == 'undefined') {
8779         this.colModel = new Roo.grid.ColumnModel(this.cm);
8780         this.cm = this.colModel;
8781         this.cm.xmodule = this.xmodule || false;
8782     }
8783     if (this.store) {
8784         this.store= Roo.factory(this.store, Roo.data);
8785         this.ds = this.store;
8786         this.ds.xmodule = this.xmodule || false;
8787          
8788     }
8789     if (this.footer && this.store) {
8790         this.footer.dataSource = this.ds;
8791         this.footer = Roo.factory(this.footer);
8792     }
8793     
8794     /** @private */
8795     this.addEvents({
8796         /**
8797          * @event cellclick
8798          * Fires when a cell is clicked
8799          * @param {Roo.bootstrap.Table} this
8800          * @param {Roo.Element} el
8801          * @param {Number} rowIndex
8802          * @param {Number} columnIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "cellclick" : true,
8806         /**
8807          * @event celldblclick
8808          * Fires when a cell is double clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Number} columnIndex
8813          * @param {Roo.EventObject} e
8814          */
8815         "celldblclick" : true,
8816         /**
8817          * @event rowclick
8818          * Fires when a row is clicked
8819          * @param {Roo.bootstrap.Table} this
8820          * @param {Roo.Element} el
8821          * @param {Number} rowIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "rowclick" : true,
8825         /**
8826          * @event rowdblclick
8827          * Fires when a row is double clicked
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Roo.EventObject} e
8832          */
8833         "rowdblclick" : true,
8834         /**
8835          * @event mouseover
8836          * Fires when a mouseover occur
8837          * @param {Roo.bootstrap.Table} this
8838          * @param {Roo.Element} el
8839          * @param {Number} rowIndex
8840          * @param {Number} columnIndex
8841          * @param {Roo.EventObject} e
8842          */
8843         "mouseover" : true,
8844         /**
8845          * @event mouseout
8846          * Fires when a mouseout occur
8847          * @param {Roo.bootstrap.Table} this
8848          * @param {Roo.Element} el
8849          * @param {Number} rowIndex
8850          * @param {Number} columnIndex
8851          * @param {Roo.EventObject} e
8852          */
8853         "mouseout" : true,
8854         /**
8855          * @event rowclass
8856          * Fires when a row is rendered, so you can change add a style to it.
8857          * @param {Roo.bootstrap.Table} this
8858          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8859          */
8860         'rowclass' : true,
8861           /**
8862          * @event rowsrendered
8863          * Fires when all the  rows have been rendered
8864          * @param {Roo.bootstrap.Table} this
8865          */
8866         'rowsrendered' : true,
8867         /**
8868          * @event contextmenu
8869          * The raw contextmenu event for the entire grid.
8870          * @param {Roo.EventObject} e
8871          */
8872         "contextmenu" : true,
8873         /**
8874          * @event rowcontextmenu
8875          * Fires when a row is right clicked
8876          * @param {Roo.bootstrap.Table} this
8877          * @param {Number} rowIndex
8878          * @param {Roo.EventObject} e
8879          */
8880         "rowcontextmenu" : true,
8881         /**
8882          * @event cellcontextmenu
8883          * Fires when a cell is right clicked
8884          * @param {Roo.bootstrap.Table} this
8885          * @param {Number} rowIndex
8886          * @param {Number} cellIndex
8887          * @param {Roo.EventObject} e
8888          */
8889          "cellcontextmenu" : true,
8890          /**
8891          * @event headercontextmenu
8892          * Fires when a header is right clicked
8893          * @param {Roo.bootstrap.Table} this
8894          * @param {Number} columnIndex
8895          * @param {Roo.EventObject} e
8896          */
8897         "headercontextmenu" : true,
8898         /**
8899          * @event mousedown
8900          * The raw mousedown event for the entire grid.
8901          * @param {Roo.EventObject} e
8902          */
8903         "mousedown" : true
8904         
8905     });
8906 };
8907
8908 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8909     
8910     cls: false,
8911     
8912     striped : false,
8913     scrollBody : false,
8914     bordered: false,
8915     hover:  false,
8916     condensed : false,
8917     responsive : false,
8918     sm : false,
8919     cm : false,
8920     store : false,
8921     loadMask : false,
8922     footerShow : true,
8923     headerShow : true,
8924     enableColumnResize: true,
8925   
8926     rowSelection : false,
8927     cellSelection : false,
8928     layout : false,
8929
8930     minColumnWidth : 50,
8931     
8932     // Roo.Element - the tbody
8933     bodyEl: false,  // <tbody> Roo.Element - thead element    
8934     headEl: false,  // <thead> Roo.Element - thead element
8935     resizeProxy : false, // proxy element for dragging?
8936
8937
8938     
8939     container: false, // used by gridpanel...
8940     
8941     lazyLoad : false,
8942     
8943     CSS : Roo.util.CSS,
8944     
8945     auto_hide_footer : false,
8946     
8947     view: false, // actually points to this..
8948     
8949     getAutoCreate : function()
8950     {
8951         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'table',
8955             cls : 'table', 
8956             cn : []
8957         };
8958         // this get's auto added by panel.Grid
8959         if (this.scrollBody) {
8960             cfg.cls += ' table-body-fixed';
8961         }    
8962         if (this.striped) {
8963             cfg.cls += ' table-striped';
8964         }
8965         
8966         if (this.hover) {
8967             cfg.cls += ' table-hover';
8968         }
8969         if (this.bordered) {
8970             cfg.cls += ' table-bordered';
8971         }
8972         if (this.condensed) {
8973             cfg.cls += ' table-condensed';
8974         }
8975         
8976         if (this.responsive) {
8977             cfg.cls += ' table-responsive';
8978         }
8979         
8980         if (this.cls) {
8981             cfg.cls+=  ' ' +this.cls;
8982         }
8983         
8984         
8985         
8986         if (this.layout) {
8987             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8988         }
8989         
8990         if(this.store || this.cm){
8991             if(this.headerShow){
8992                 cfg.cn.push(this.renderHeader());
8993             }
8994             
8995             cfg.cn.push(this.renderBody());
8996             
8997             if(this.footerShow){
8998                 cfg.cn.push(this.renderFooter());
8999             }
9000             // where does this come from?
9001             //cfg.cls+=  ' TableGrid';
9002         }
9003         
9004         return { cn : [ cfg ] };
9005     },
9006     
9007     initEvents : function()
9008     {   
9009         if(!this.store || !this.cm){
9010             return;
9011         }
9012         if (this.selModel) {
9013             this.selModel.initEvents();
9014         }
9015         
9016         
9017         //Roo.log('initEvents with ds!!!!');
9018         
9019         this.bodyEl = this.el.select('tbody', true).first();
9020         this.headEl = this.el.select('thead', true).first();
9021         this.mainFoot = this.el.select('tfoot', true).first();
9022         
9023         
9024         
9025         
9026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9027             e.on('click', this.sort, this);
9028         }, this);
9029         
9030         
9031         // why is this done????? = it breaks dialogs??
9032         //this.parent().el.setStyle('position', 'relative');
9033         
9034         
9035         if (this.footer) {
9036             this.footer.parentId = this.id;
9037             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9038             
9039             if(this.lazyLoad){
9040                 this.el.select('tfoot tr td').first().addClass('hide');
9041             }
9042         } 
9043         
9044         if(this.loadMask) {
9045             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9046         }
9047         
9048         this.store.on('load', this.onLoad, this);
9049         this.store.on('beforeload', this.onBeforeLoad, this);
9050         this.store.on('update', this.onUpdate, this);
9051         this.store.on('add', this.onAdd, this);
9052         this.store.on("clear", this.clear, this);
9053         
9054         this.el.on("contextmenu", this.onContextMenu, this);
9055         
9056         
9057         this.cm.on("headerchange", this.onHeaderChange, this);
9058         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9059
9060  //?? does bodyEl get replaced on render?
9061         this.bodyEl.on("click", this.onClick, this);
9062         this.bodyEl.on("dblclick", this.onDblClick, this);        
9063         this.bodyEl.on('scroll', this.onBodyScroll, this);
9064
9065         // guessing mainbody will work - this relays usually caught by selmodel at present.
9066         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9067   
9068   
9069         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9070         
9071   
9072         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9073             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9074         }
9075         
9076         this.initCSS();
9077     },
9078     // Compatibility with grid - we implement all the view features at present.
9079     getView : function()
9080     {
9081         return this;
9082     },
9083     
9084     initCSS : function()
9085     {
9086         
9087         
9088         var cm = this.cm, styles = [];
9089         this.CSS.removeStyleSheet(this.id + '-cssrules');
9090         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9091         // we can honour xs/sm/md/xl  as widths...
9092         // we first have to decide what widht we are currently at...
9093         var sz = Roo.getGridSize();
9094         
9095         var total = 0;
9096         var last = -1;
9097         var cols = []; // visable cols.
9098         var total_abs = 0;
9099         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9100             var w = cm.getColumnWidth(i, false);
9101             if(cm.isHidden(i)){
9102                 cols.push( { rel : false, abs : 0 });
9103                 continue;
9104             }
9105             if (w !== false) {
9106                 cols.push( { rel : false, abs : w });
9107                 total_abs += w;
9108                 last = i; // not really..
9109                 continue;
9110             }
9111             var w = cm.getColumnWidth(i, sz);
9112             if (w > 0) {
9113                 last = i
9114             }
9115             total += w;
9116             cols.push( { rel : w, abs : false });
9117         }
9118         
9119         var avail = this.bodyEl.dom.clientWidth - total_abs;
9120         
9121         var unitWidth = Math.floor(avail / total);
9122         var rem = avail - (unitWidth * total);
9123         
9124         var hidden, width, pos = 0 , splithide , left;
9125         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9126             
9127             hidden = 'display:none;';
9128             left = '';
9129             width  = 'width:0px;';
9130             splithide = '';
9131             if(!cm.isHidden(i)){
9132                 hidden = '';
9133                 
9134                 
9135                 // we can honour xs/sm/md/xl ?
9136                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9137                 if (w===0) {
9138                     hidden = 'display:none;';
9139                 }
9140                 // width should return a small number...
9141                 if (i == last) {
9142                     w+=rem; // add the remaining with..
9143                 }
9144                 pos += w;
9145                 left = "left:" + (pos -4) + "px;";
9146                 width = "width:" + w+ "px;";
9147                 
9148             }
9149             if (this.responsive) {
9150                 width = '';
9151                 left = '';
9152                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9153                 splithide = 'display: none;';
9154             }
9155             
9156             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9157             if (this.headEl) {
9158                 if (i == last) {
9159                     splithide = 'display:none;';
9160                 }
9161                 
9162                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9163                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9164                 );
9165             }
9166             
9167         }
9168         //Roo.log(styles.join(''));
9169         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9170         
9171     },
9172     
9173     
9174     
9175     onContextMenu : function(e, t)
9176     {
9177         this.processEvent("contextmenu", e);
9178     },
9179     
9180     processEvent : function(name, e)
9181     {
9182         if (name != 'touchstart' ) {
9183             this.fireEvent(name, e);    
9184         }
9185         
9186         var t = e.getTarget();
9187         
9188         var cell = Roo.get(t);
9189         
9190         if(!cell){
9191             return;
9192         }
9193         
9194         if(cell.findParent('tfoot', false, true)){
9195             return;
9196         }
9197         
9198         if(cell.findParent('thead', false, true)){
9199             
9200             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9201                 cell = Roo.get(t).findParent('th', false, true);
9202                 if (!cell) {
9203                     Roo.log("failed to find th in thead?");
9204                     Roo.log(e.getTarget());
9205                     return;
9206                 }
9207             }
9208             
9209             var cellIndex = cell.dom.cellIndex;
9210             
9211             var ename = name == 'touchstart' ? 'click' : name;
9212             this.fireEvent("header" + ename, this, cellIndex, e);
9213             
9214             return;
9215         }
9216         
9217         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9218             cell = Roo.get(t).findParent('td', false, true);
9219             if (!cell) {
9220                 Roo.log("failed to find th in tbody?");
9221                 Roo.log(e.getTarget());
9222                 return;
9223             }
9224         }
9225         
9226         var row = cell.findParent('tr', false, true);
9227         var cellIndex = cell.dom.cellIndex;
9228         var rowIndex = row.dom.rowIndex - 1;
9229         
9230         if(row !== false){
9231             
9232             this.fireEvent("row" + name, this, rowIndex, e);
9233             
9234             if(cell !== false){
9235             
9236                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9237             }
9238         }
9239         
9240     },
9241     
9242     onMouseover : function(e, el)
9243     {
9244         var cell = Roo.get(el);
9245         
9246         if(!cell){
9247             return;
9248         }
9249         
9250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9251             cell = cell.findParent('td', false, true);
9252         }
9253         
9254         var row = cell.findParent('tr', false, true);
9255         var cellIndex = cell.dom.cellIndex;
9256         var rowIndex = row.dom.rowIndex - 1; // start from 0
9257         
9258         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9259         
9260     },
9261     
9262     onMouseout : function(e, el)
9263     {
9264         var cell = Roo.get(el);
9265         
9266         if(!cell){
9267             return;
9268         }
9269         
9270         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9271             cell = cell.findParent('td', false, true);
9272         }
9273         
9274         var row = cell.findParent('tr', false, true);
9275         var cellIndex = cell.dom.cellIndex;
9276         var rowIndex = row.dom.rowIndex - 1; // start from 0
9277         
9278         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9279         
9280     },
9281     
9282     onClick : function(e, el)
9283     {
9284         var cell = Roo.get(el);
9285         
9286         if(!cell || (!this.cellSelection && !this.rowSelection)){
9287             return;
9288         }
9289         
9290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9291             cell = cell.findParent('td', false, true);
9292         }
9293         
9294         if(!cell || typeof(cell) == 'undefined'){
9295             return;
9296         }
9297         
9298         var row = cell.findParent('tr', false, true);
9299         
9300         if(!row || typeof(row) == 'undefined'){
9301             return;
9302         }
9303         
9304         var cellIndex = cell.dom.cellIndex;
9305         var rowIndex = this.getRowIndex(row);
9306         
9307         // why??? - should these not be based on SelectionModel?
9308         //if(this.cellSelection){
9309             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9310         //}
9311         
9312         //if(this.rowSelection){
9313             this.fireEvent('rowclick', this, row, rowIndex, e);
9314         //}
9315          
9316     },
9317         
9318     onDblClick : function(e,el)
9319     {
9320         var cell = Roo.get(el);
9321         
9322         if(!cell || (!this.cellSelection && !this.rowSelection)){
9323             return;
9324         }
9325         
9326         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9327             cell = cell.findParent('td', false, true);
9328         }
9329         
9330         if(!cell || typeof(cell) == 'undefined'){
9331             return;
9332         }
9333         
9334         var row = cell.findParent('tr', false, true);
9335         
9336         if(!row || typeof(row) == 'undefined'){
9337             return;
9338         }
9339         
9340         var cellIndex = cell.dom.cellIndex;
9341         var rowIndex = this.getRowIndex(row);
9342         
9343         if(this.cellSelection){
9344             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9345         }
9346         
9347         if(this.rowSelection){
9348             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9349         }
9350     },
9351     findRowIndex : function(el)
9352     {
9353         var cell = Roo.get(el);
9354         if(!cell) {
9355             return false;
9356         }
9357         var row = cell.findParent('tr', false, true);
9358         
9359         if(!row || typeof(row) == 'undefined'){
9360             return false;
9361         }
9362         return this.getRowIndex(row);
9363     },
9364     sort : function(e,el)
9365     {
9366         var col = Roo.get(el);
9367         
9368         if(!col.hasClass('sortable')){
9369             return;
9370         }
9371         
9372         var sort = col.attr('sort');
9373         var dir = 'ASC';
9374         
9375         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9376             dir = 'DESC';
9377         }
9378         
9379         this.store.sortInfo = {field : sort, direction : dir};
9380         
9381         if (this.footer) {
9382             Roo.log("calling footer first");
9383             this.footer.onClick('first');
9384         } else {
9385         
9386             this.store.load({ params : { start : 0 } });
9387         }
9388     },
9389     
9390     renderHeader : function()
9391     {
9392         var header = {
9393             tag: 'thead',
9394             cn : []
9395         };
9396         
9397         var cm = this.cm;
9398         this.totalWidth = 0;
9399         
9400         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9401             
9402             var config = cm.config[i];
9403             
9404             var c = {
9405                 tag: 'th',
9406                 cls : 'x-hcol-' + i,
9407                 style : '',
9408                 
9409                 html: cm.getColumnHeader(i)
9410             };
9411             
9412             var tooltip = cm.getColumnTooltip(i);
9413             if (tooltip) {
9414                 c.tooltip = tooltip;
9415             }
9416             
9417             
9418             var hh = '';
9419             
9420             if(typeof(config.sortable) != 'undefined' && config.sortable){
9421                 c.cls += ' sortable';
9422                 c.html = '<i class="fa"></i>' + c.html;
9423             }
9424             
9425             // could use BS4 hidden-..-down 
9426             
9427             if(typeof(config.lgHeader) != 'undefined'){
9428                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9429             }
9430             
9431             if(typeof(config.mdHeader) != 'undefined'){
9432                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9433             }
9434             
9435             if(typeof(config.smHeader) != 'undefined'){
9436                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9437             }
9438             
9439             if(typeof(config.xsHeader) != 'undefined'){
9440                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9441             }
9442             
9443             if(hh.length){
9444                 c.html = hh;
9445             }
9446             
9447             if(typeof(config.tooltip) != 'undefined'){
9448                 c.tooltip = config.tooltip;
9449             }
9450             
9451             if(typeof(config.colspan) != 'undefined'){
9452                 c.colspan = config.colspan;
9453             }
9454             
9455             // hidden is handled by CSS now
9456             
9457             if(typeof(config.dataIndex) != 'undefined'){
9458                 c.sort = config.dataIndex;
9459             }
9460             
9461            
9462             
9463             if(typeof(config.align) != 'undefined' && config.align.length){
9464                 c.style += ' text-align:' + config.align + ';';
9465             }
9466             
9467             /* width is done in CSS
9468              *if(typeof(config.width) != 'undefined'){
9469                 c.style += ' width:' + config.width + 'px;';
9470                 this.totalWidth += config.width;
9471             } else {
9472                 this.totalWidth += 100; // assume minimum of 100 per column?
9473             }
9474             */
9475             
9476             if(typeof(config.cls) != 'undefined'){
9477                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9478             }
9479             // this is the bit that doesnt reall work at all...
9480             
9481             if (this.responsive) {
9482                  
9483             
9484                 ['xs','sm','md','lg'].map(function(size){
9485                     
9486                     if(typeof(config[size]) == 'undefined'){
9487                         return;
9488                     }
9489                      
9490                     if (!config[size]) { // 0 = hidden
9491                         // BS 4 '0' is treated as hide that column and below.
9492                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9493                         return;
9494                     }
9495                     
9496                     c.cls += ' col-' + size + '-' + config[size] + (
9497                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9498                     );
9499                     
9500                     
9501                 });
9502             }
9503             // at the end?
9504             
9505             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9506             
9507             
9508             
9509             
9510             header.cn.push(c)
9511         }
9512         
9513         return header;
9514     },
9515     
9516     renderBody : function()
9517     {
9518         var body = {
9519             tag: 'tbody',
9520             cn : [
9521                 {
9522                     tag: 'tr',
9523                     cn : [
9524                         {
9525                             tag : 'td',
9526                             colspan :  this.cm.getColumnCount()
9527                         }
9528                     ]
9529                 }
9530             ]
9531         };
9532         
9533         return body;
9534     },
9535     
9536     renderFooter : function()
9537     {
9538         var footer = {
9539             tag: 'tfoot',
9540             cn : [
9541                 {
9542                     tag: 'tr',
9543                     cn : [
9544                         {
9545                             tag : 'td',
9546                             colspan :  this.cm.getColumnCount()
9547                         }
9548                     ]
9549                 }
9550             ]
9551         };
9552         
9553         return footer;
9554     },
9555     
9556     
9557     
9558     onLoad : function()
9559     {
9560 //        Roo.log('ds onload');
9561         this.clear();
9562         
9563         var _this = this;
9564         var cm = this.cm;
9565         var ds = this.store;
9566         
9567         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9568             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9569             if (_this.store.sortInfo) {
9570                     
9571                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9572                     e.select('i', true).addClass(['fa-arrow-up']);
9573                 }
9574                 
9575                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9576                     e.select('i', true).addClass(['fa-arrow-down']);
9577                 }
9578             }
9579         });
9580         
9581         var tbody =  this.bodyEl;
9582               
9583         if(ds.getCount() > 0){
9584             ds.data.each(function(d,rowIndex){
9585                 var row =  this.renderRow(cm, ds, rowIndex);
9586                 
9587                 tbody.createChild(row);
9588                 
9589                 var _this = this;
9590                 
9591                 if(row.cellObjects.length){
9592                     Roo.each(row.cellObjects, function(r){
9593                         _this.renderCellObject(r);
9594                     })
9595                 }
9596                 
9597             }, this);
9598         }
9599         
9600         var tfoot = this.el.select('tfoot', true).first();
9601         
9602         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9603             
9604             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9605             
9606             var total = this.ds.getTotalCount();
9607             
9608             if(this.footer.pageSize < total){
9609                 this.mainFoot.show();
9610             }
9611         }
9612         
9613         Roo.each(this.el.select('tbody td', true).elements, function(e){
9614             e.on('mouseover', _this.onMouseover, _this);
9615         });
9616         
9617         Roo.each(this.el.select('tbody td', true).elements, function(e){
9618             e.on('mouseout', _this.onMouseout, _this);
9619         });
9620         this.fireEvent('rowsrendered', this);
9621         
9622         this.autoSize();
9623         
9624         this.initCSS(); /// resize cols
9625
9626         
9627     },
9628     
9629     
9630     onUpdate : function(ds,record)
9631     {
9632         this.refreshRow(record);
9633         this.autoSize();
9634     },
9635     
9636     onRemove : function(ds, record, index, isUpdate){
9637         if(isUpdate !== true){
9638             this.fireEvent("beforerowremoved", this, index, record);
9639         }
9640         var bt = this.bodyEl.dom;
9641         
9642         var rows = this.el.select('tbody > tr', true).elements;
9643         
9644         if(typeof(rows[index]) != 'undefined'){
9645             bt.removeChild(rows[index].dom);
9646         }
9647         
9648 //        if(bt.rows[index]){
9649 //            bt.removeChild(bt.rows[index]);
9650 //        }
9651         
9652         if(isUpdate !== true){
9653             //this.stripeRows(index);
9654             //this.syncRowHeights(index, index);
9655             //this.layout();
9656             this.fireEvent("rowremoved", this, index, record);
9657         }
9658     },
9659     
9660     onAdd : function(ds, records, rowIndex)
9661     {
9662         //Roo.log('on Add called');
9663         // - note this does not handle multiple adding very well..
9664         var bt = this.bodyEl.dom;
9665         for (var i =0 ; i < records.length;i++) {
9666             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9667             //Roo.log(records[i]);
9668             //Roo.log(this.store.getAt(rowIndex+i));
9669             this.insertRow(this.store, rowIndex + i, false);
9670             return;
9671         }
9672         
9673     },
9674     
9675     
9676     refreshRow : function(record){
9677         var ds = this.store, index;
9678         if(typeof record == 'number'){
9679             index = record;
9680             record = ds.getAt(index);
9681         }else{
9682             index = ds.indexOf(record);
9683             if (index < 0) {
9684                 return; // should not happen - but seems to 
9685             }
9686         }
9687         this.insertRow(ds, index, true);
9688         this.autoSize();
9689         this.onRemove(ds, record, index+1, true);
9690         this.autoSize();
9691         //this.syncRowHeights(index, index);
9692         //this.layout();
9693         this.fireEvent("rowupdated", this, index, record);
9694     },
9695     // private - called by RowSelection
9696     onRowSelect : function(rowIndex){
9697         var row = this.getRowDom(rowIndex);
9698         row.addClass(['bg-info','info']);
9699     },
9700     // private - called by RowSelection
9701     onRowDeselect : function(rowIndex)
9702     {
9703         if (rowIndex < 0) {
9704             return;
9705         }
9706         var row = this.getRowDom(rowIndex);
9707         row.removeClass(['bg-info','info']);
9708     },
9709       /**
9710      * Focuses the specified row.
9711      * @param {Number} row The row index
9712      */
9713     focusRow : function(row)
9714     {
9715         //Roo.log('GridView.focusRow');
9716         var x = this.bodyEl.dom.scrollLeft;
9717         this.focusCell(row, 0, false);
9718         this.bodyEl.dom.scrollLeft = x;
9719
9720     },
9721      /**
9722      * Focuses the specified cell.
9723      * @param {Number} row The row index
9724      * @param {Number} col The column index
9725      * @param {Boolean} hscroll false to disable horizontal scrolling
9726      */
9727     focusCell : function(row, col, hscroll)
9728     {
9729         //Roo.log('GridView.focusCell');
9730         var el = this.ensureVisible(row, col, hscroll);
9731         // not sure what focusEL achives = it's a <a> pos relative 
9732         //this.focusEl.alignTo(el, "tl-tl");
9733         //if(Roo.isGecko){
9734         //    this.focusEl.focus();
9735         //}else{
9736         //    this.focusEl.focus.defer(1, this.focusEl);
9737         //}
9738     },
9739     
9740      /**
9741      * Scrolls the specified cell into view
9742      * @param {Number} row The row index
9743      * @param {Number} col The column index
9744      * @param {Boolean} hscroll false to disable horizontal scrolling
9745      */
9746     ensureVisible : function(row, col, hscroll)
9747     {
9748         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9749         //return null; //disable for testing.
9750         if(typeof row != "number"){
9751             row = row.rowIndex;
9752         }
9753         if(row < 0 && row >= this.ds.getCount()){
9754             return  null;
9755         }
9756         col = (col !== undefined ? col : 0);
9757         var cm = this.cm;
9758         while(cm.isHidden(col)){
9759             col++;
9760         }
9761
9762         var el = this.getCellDom(row, col);
9763         if(!el){
9764             return null;
9765         }
9766         var c = this.bodyEl.dom;
9767
9768         var ctop = parseInt(el.offsetTop, 10);
9769         var cleft = parseInt(el.offsetLeft, 10);
9770         var cbot = ctop + el.offsetHeight;
9771         var cright = cleft + el.offsetWidth;
9772
9773         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9774         var ch = 0; //?? header is not withing the area?
9775         var stop = parseInt(c.scrollTop, 10);
9776         var sleft = parseInt(c.scrollLeft, 10);
9777         var sbot = stop + ch;
9778         var sright = sleft + c.clientWidth;
9779         /*
9780         Roo.log('GridView.ensureVisible:' +
9781                 ' ctop:' + ctop +
9782                 ' c.clientHeight:' + c.clientHeight +
9783                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9784                 ' stop:' + stop +
9785                 ' cbot:' + cbot +
9786                 ' sbot:' + sbot +
9787                 ' ch:' + ch  
9788                 );
9789         */
9790         if(ctop < stop){
9791             c.scrollTop = ctop;
9792             //Roo.log("set scrolltop to ctop DISABLE?");
9793         }else if(cbot > sbot){
9794             //Roo.log("set scrolltop to cbot-ch");
9795             c.scrollTop = cbot-ch;
9796         }
9797
9798         if(hscroll !== false){
9799             if(cleft < sleft){
9800                 c.scrollLeft = cleft;
9801             }else if(cright > sright){
9802                 c.scrollLeft = cright-c.clientWidth;
9803             }
9804         }
9805
9806         return el;
9807     },
9808     
9809     
9810     insertRow : function(dm, rowIndex, isUpdate){
9811         
9812         if(!isUpdate){
9813             this.fireEvent("beforerowsinserted", this, rowIndex);
9814         }
9815             //var s = this.getScrollState();
9816         var row = this.renderRow(this.cm, this.store, rowIndex);
9817         // insert before rowIndex..
9818         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9819         
9820         var _this = this;
9821                 
9822         if(row.cellObjects.length){
9823             Roo.each(row.cellObjects, function(r){
9824                 _this.renderCellObject(r);
9825             })
9826         }
9827             
9828         if(!isUpdate){
9829             this.fireEvent("rowsinserted", this, rowIndex);
9830             //this.syncRowHeights(firstRow, lastRow);
9831             //this.stripeRows(firstRow);
9832             //this.layout();
9833         }
9834         
9835     },
9836     
9837     
9838     getRowDom : function(rowIndex)
9839     {
9840         var rows = this.el.select('tbody > tr', true).elements;
9841         
9842         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9843         
9844     },
9845     getCellDom : function(rowIndex, colIndex)
9846     {
9847         var row = this.getRowDom(rowIndex);
9848         if (row === false) {
9849             return false;
9850         }
9851         var cols = row.select('td', true).elements;
9852         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9853         
9854     },
9855     
9856     // returns the object tree for a tr..
9857   
9858     
9859     renderRow : function(cm, ds, rowIndex) 
9860     {
9861         var d = ds.getAt(rowIndex);
9862         
9863         var row = {
9864             tag : 'tr',
9865             cls : 'x-row-' + rowIndex,
9866             cn : []
9867         };
9868             
9869         var cellObjects = [];
9870         
9871         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9872             var config = cm.config[i];
9873             
9874             var renderer = cm.getRenderer(i);
9875             var value = '';
9876             var id = false;
9877             
9878             if(typeof(renderer) !== 'undefined'){
9879                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9880             }
9881             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9882             // and are rendered into the cells after the row is rendered - using the id for the element.
9883             
9884             if(typeof(value) === 'object'){
9885                 id = Roo.id();
9886                 cellObjects.push({
9887                     container : id,
9888                     cfg : value 
9889                 })
9890             }
9891             
9892             var rowcfg = {
9893                 record: d,
9894                 rowIndex : rowIndex,
9895                 colIndex : i,
9896                 rowClass : ''
9897             };
9898
9899             this.fireEvent('rowclass', this, rowcfg);
9900             
9901             var td = {
9902                 tag: 'td',
9903                 // this might end up displaying HTML?
9904                 // this is too messy... - better to only do it on columsn you know are going to be too long
9905                 //tooltip : (typeof(value) === 'object') ? '' : value,
9906                 cls : rowcfg.rowClass + ' x-col-' + i,
9907                 style: '',
9908                 html: (typeof(value) === 'object') ? '' : value
9909             };
9910             
9911             if (id) {
9912                 td.id = id;
9913             }
9914             
9915             if(typeof(config.colspan) != 'undefined'){
9916                 td.colspan = config.colspan;
9917             }
9918             
9919             
9920             
9921             if(typeof(config.align) != 'undefined' && config.align.length){
9922                 td.style += ' text-align:' + config.align + ';';
9923             }
9924             if(typeof(config.valign) != 'undefined' && config.valign.length){
9925                 td.style += ' vertical-align:' + config.valign + ';';
9926             }
9927             /*
9928             if(typeof(config.width) != 'undefined'){
9929                 td.style += ' width:' +  config.width + 'px;';
9930             }
9931             */
9932             
9933             if(typeof(config.cursor) != 'undefined'){
9934                 td.style += ' cursor:' +  config.cursor + ';';
9935             }
9936             
9937             if(typeof(config.cls) != 'undefined'){
9938                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9939             }
9940             if (this.responsive) {
9941                 ['xs','sm','md','lg'].map(function(size){
9942                     
9943                     if(typeof(config[size]) == 'undefined'){
9944                         return;
9945                     }
9946                     
9947                     
9948                       
9949                     if (!config[size]) { // 0 = hidden
9950                         // BS 4 '0' is treated as hide that column and below.
9951                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9952                         return;
9953                     }
9954                     
9955                     td.cls += ' col-' + size + '-' + config[size] + (
9956                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9957                     );
9958                      
9959     
9960                 });
9961             }
9962             row.cn.push(td);
9963            
9964         }
9965         
9966         row.cellObjects = cellObjects;
9967         
9968         return row;
9969           
9970     },
9971     
9972     
9973     
9974     onBeforeLoad : function()
9975     {
9976         
9977     },
9978      /**
9979      * Remove all rows
9980      */
9981     clear : function()
9982     {
9983         this.el.select('tbody', true).first().dom.innerHTML = '';
9984     },
9985     /**
9986      * Show or hide a row.
9987      * @param {Number} rowIndex to show or hide
9988      * @param {Boolean} state hide
9989      */
9990     setRowVisibility : function(rowIndex, state)
9991     {
9992         var bt = this.bodyEl.dom;
9993         
9994         var rows = this.el.select('tbody > tr', true).elements;
9995         
9996         if(typeof(rows[rowIndex]) == 'undefined'){
9997             return;
9998         }
9999         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10000         
10001     },
10002     
10003     
10004     getSelectionModel : function(){
10005         if(!this.selModel){
10006             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10007         }
10008         return this.selModel;
10009     },
10010     /*
10011      * Render the Roo.bootstrap object from renderder
10012      */
10013     renderCellObject : function(r)
10014     {
10015         var _this = this;
10016         
10017         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10018         
10019         var t = r.cfg.render(r.container);
10020         
10021         if(r.cfg.cn){
10022             Roo.each(r.cfg.cn, function(c){
10023                 var child = {
10024                     container: t.getChildContainer(),
10025                     cfg: c
10026                 };
10027                 _this.renderCellObject(child);
10028             })
10029         }
10030     },
10031     /**
10032      * get the Row Index from a dom element.
10033      * @param {Roo.Element} row The row to look for
10034      * @returns {Number} the row
10035      */
10036     getRowIndex : function(row)
10037     {
10038         var rowIndex = -1;
10039         
10040         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10041             if(el != row){
10042                 return;
10043             }
10044             
10045             rowIndex = index;
10046         });
10047         
10048         return rowIndex;
10049     },
10050     /**
10051      * get the header TH element for columnIndex
10052      * @param {Number} columnIndex
10053      * @returns {Roo.Element}
10054      */
10055     getHeaderIndex: function(colIndex)
10056     {
10057         var cols = this.headEl.select('th', true).elements;
10058         return cols[colIndex]; 
10059     },
10060     /**
10061      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10062      * @param {domElement} cell to look for
10063      * @returns {Number} the column
10064      */
10065     getCellIndex : function(cell)
10066     {
10067         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10068         if(id){
10069             return parseInt(id[1], 10);
10070         }
10071         return 0;
10072     },
10073      /**
10074      * Returns the grid's underlying element = used by panel.Grid
10075      * @return {Element} The element
10076      */
10077     getGridEl : function(){
10078         return this.el;
10079     },
10080      /**
10081      * Forces a resize - used by panel.Grid
10082      * @return {Element} The element
10083      */
10084     autoSize : function()
10085     {
10086         //var ctr = Roo.get(this.container.dom.parentElement);
10087         var ctr = Roo.get(this.el.dom);
10088         
10089         var thd = this.getGridEl().select('thead',true).first();
10090         var tbd = this.getGridEl().select('tbody', true).first();
10091         var tfd = this.getGridEl().select('tfoot', true).first();
10092         
10093         var cw = ctr.getWidth();
10094         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10095         
10096         if (tbd) {
10097             
10098             tbd.setWidth(ctr.getWidth());
10099             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10100             // this needs fixing for various usage - currently only hydra job advers I think..
10101             //tdb.setHeight(
10102             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10103             //); 
10104             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10105             cw -= barsize;
10106         }
10107         cw = Math.max(cw, this.totalWidth);
10108         this.getGridEl().select('tbody tr',true).setWidth(cw);
10109         this.initCSS();
10110         
10111         // resize 'expandable coloumn?
10112         
10113         return; // we doe not have a view in this design..
10114         
10115     },
10116     onBodyScroll: function()
10117     {
10118         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10119         if(this.headEl){
10120             this.headEl.setStyle({
10121                 'position' : 'relative',
10122                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10123             });
10124         }
10125         
10126         if(this.lazyLoad){
10127             
10128             var scrollHeight = this.bodyEl.dom.scrollHeight;
10129             
10130             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10131             
10132             var height = this.bodyEl.getHeight();
10133             
10134             if(scrollHeight - height == scrollTop) {
10135                 
10136                 var total = this.ds.getTotalCount();
10137                 
10138                 if(this.footer.cursor + this.footer.pageSize < total){
10139                     
10140                     this.footer.ds.load({
10141                         params : {
10142                             start : this.footer.cursor + this.footer.pageSize,
10143                             limit : this.footer.pageSize
10144                         },
10145                         add : true
10146                     });
10147                 }
10148             }
10149             
10150         }
10151     },
10152     onColumnSplitterMoved : function(i, diff)
10153     {
10154         this.userResized = true;
10155         
10156         var cm = this.colModel;
10157         
10158         var w = this.getHeaderIndex(i).getWidth() + diff;
10159         
10160         
10161         cm.setColumnWidth(i, w, true);
10162         this.initCSS();
10163         //var cid = cm.getColumnId(i); << not used in this version?
10164        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10165         
10166         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10167         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10168         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10169 */
10170         //this.updateSplitters();
10171         //this.layout(); << ??
10172         this.fireEvent("columnresize", i, w);
10173     },
10174     onHeaderChange : function()
10175     {
10176         var header = this.renderHeader();
10177         var table = this.el.select('table', true).first();
10178         
10179         this.headEl.remove();
10180         this.headEl = table.createChild(header, this.bodyEl, false);
10181         
10182         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10183             e.on('click', this.sort, this);
10184         }, this);
10185         
10186         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10187             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10188         }
10189         
10190     },
10191     
10192     onHiddenChange : function(colModel, colIndex, hidden)
10193     {
10194         /*
10195         this.cm.setHidden()
10196         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10197         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10198         
10199         this.CSS.updateRule(thSelector, "display", "");
10200         this.CSS.updateRule(tdSelector, "display", "");
10201         
10202         if(hidden){
10203             this.CSS.updateRule(thSelector, "display", "none");
10204             this.CSS.updateRule(tdSelector, "display", "none");
10205         }
10206         */
10207         // onload calls initCSS()
10208         this.onHeaderChange();
10209         this.onLoad();
10210     },
10211     
10212     setColumnWidth: function(col_index, width)
10213     {
10214         // width = "md-2 xs-2..."
10215         if(!this.colModel.config[col_index]) {
10216             return;
10217         }
10218         
10219         var w = width.split(" ");
10220         
10221         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10222         
10223         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10224         
10225         
10226         for(var j = 0; j < w.length; j++) {
10227             
10228             if(!w[j]) {
10229                 continue;
10230             }
10231             
10232             var size_cls = w[j].split("-");
10233             
10234             if(!Number.isInteger(size_cls[1] * 1)) {
10235                 continue;
10236             }
10237             
10238             if(!this.colModel.config[col_index][size_cls[0]]) {
10239                 continue;
10240             }
10241             
10242             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10243                 continue;
10244             }
10245             
10246             h_row[0].classList.replace(
10247                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10248                 "col-"+size_cls[0]+"-"+size_cls[1]
10249             );
10250             
10251             for(var i = 0; i < rows.length; i++) {
10252                 
10253                 var size_cls = w[j].split("-");
10254                 
10255                 if(!Number.isInteger(size_cls[1] * 1)) {
10256                     continue;
10257                 }
10258                 
10259                 if(!this.colModel.config[col_index][size_cls[0]]) {
10260                     continue;
10261                 }
10262                 
10263                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10264                     continue;
10265                 }
10266                 
10267                 rows[i].classList.replace(
10268                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10269                     "col-"+size_cls[0]+"-"+size_cls[1]
10270                 );
10271             }
10272             
10273             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10274         }
10275     }
10276 });
10277
10278 // currently only used to find the split on drag.. 
10279 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10280
10281 /**
10282  * @depricated
10283 */
10284 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10285 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10286 /*
10287  * - LGPL
10288  *
10289  * table cell
10290  * 
10291  */
10292
10293 /**
10294  * @class Roo.bootstrap.TableCell
10295  * @extends Roo.bootstrap.Component
10296  * Bootstrap TableCell class
10297  * @cfg {String} html cell contain text
10298  * @cfg {String} cls cell class
10299  * @cfg {String} tag cell tag (td|th) default td
10300  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10301  * @cfg {String} align Aligns the content in a cell
10302  * @cfg {String} axis Categorizes cells
10303  * @cfg {String} bgcolor Specifies the background color of a cell
10304  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10305  * @cfg {Number} colspan Specifies the number of columns a cell should span
10306  * @cfg {String} headers Specifies one or more header cells a cell is related to
10307  * @cfg {Number} height Sets the height of a cell
10308  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10309  * @cfg {Number} rowspan Sets the number of rows a cell should span
10310  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10311  * @cfg {String} valign Vertical aligns the content in a cell
10312  * @cfg {Number} width Specifies the width of a cell
10313  * 
10314  * @constructor
10315  * Create a new TableCell
10316  * @param {Object} config The config object
10317  */
10318
10319 Roo.bootstrap.TableCell = function(config){
10320     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10321 };
10322
10323 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10324     
10325     html: false,
10326     cls: false,
10327     tag: false,
10328     abbr: false,
10329     align: false,
10330     axis: false,
10331     bgcolor: false,
10332     charoff: false,
10333     colspan: false,
10334     headers: false,
10335     height: false,
10336     nowrap: false,
10337     rowspan: false,
10338     scope: false,
10339     valign: false,
10340     width: false,
10341     
10342     
10343     getAutoCreate : function(){
10344         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10345         
10346         cfg = {
10347             tag: 'td'
10348         };
10349         
10350         if(this.tag){
10351             cfg.tag = this.tag;
10352         }
10353         
10354         if (this.html) {
10355             cfg.html=this.html
10356         }
10357         if (this.cls) {
10358             cfg.cls=this.cls
10359         }
10360         if (this.abbr) {
10361             cfg.abbr=this.abbr
10362         }
10363         if (this.align) {
10364             cfg.align=this.align
10365         }
10366         if (this.axis) {
10367             cfg.axis=this.axis
10368         }
10369         if (this.bgcolor) {
10370             cfg.bgcolor=this.bgcolor
10371         }
10372         if (this.charoff) {
10373             cfg.charoff=this.charoff
10374         }
10375         if (this.colspan) {
10376             cfg.colspan=this.colspan
10377         }
10378         if (this.headers) {
10379             cfg.headers=this.headers
10380         }
10381         if (this.height) {
10382             cfg.height=this.height
10383         }
10384         if (this.nowrap) {
10385             cfg.nowrap=this.nowrap
10386         }
10387         if (this.rowspan) {
10388             cfg.rowspan=this.rowspan
10389         }
10390         if (this.scope) {
10391             cfg.scope=this.scope
10392         }
10393         if (this.valign) {
10394             cfg.valign=this.valign
10395         }
10396         if (this.width) {
10397             cfg.width=this.width
10398         }
10399         
10400         
10401         return cfg;
10402     }
10403    
10404 });
10405
10406  
10407
10408  /*
10409  * - LGPL
10410  *
10411  * table row
10412  * 
10413  */
10414
10415 /**
10416  * @class Roo.bootstrap.TableRow
10417  * @extends Roo.bootstrap.Component
10418  * Bootstrap TableRow class
10419  * @cfg {String} cls row class
10420  * @cfg {String} align Aligns the content in a table row
10421  * @cfg {String} bgcolor Specifies a background color for a table row
10422  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10423  * @cfg {String} valign Vertical aligns the content in a table row
10424  * 
10425  * @constructor
10426  * Create a new TableRow
10427  * @param {Object} config The config object
10428  */
10429
10430 Roo.bootstrap.TableRow = function(config){
10431     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10432 };
10433
10434 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10435     
10436     cls: false,
10437     align: false,
10438     bgcolor: false,
10439     charoff: false,
10440     valign: false,
10441     
10442     getAutoCreate : function(){
10443         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10444         
10445         cfg = {
10446             tag: 'tr'
10447         };
10448             
10449         if(this.cls){
10450             cfg.cls = this.cls;
10451         }
10452         if(this.align){
10453             cfg.align = this.align;
10454         }
10455         if(this.bgcolor){
10456             cfg.bgcolor = this.bgcolor;
10457         }
10458         if(this.charoff){
10459             cfg.charoff = this.charoff;
10460         }
10461         if(this.valign){
10462             cfg.valign = this.valign;
10463         }
10464         
10465         return cfg;
10466     }
10467    
10468 });
10469
10470  
10471
10472  /*
10473  * - LGPL
10474  *
10475  * table body
10476  * 
10477  */
10478
10479 /**
10480  * @class Roo.bootstrap.TableBody
10481  * @extends Roo.bootstrap.Component
10482  * Bootstrap TableBody class
10483  * @cfg {String} cls element class
10484  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10485  * @cfg {String} align Aligns the content inside the element
10486  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10487  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10488  * 
10489  * @constructor
10490  * Create a new TableBody
10491  * @param {Object} config The config object
10492  */
10493
10494 Roo.bootstrap.TableBody = function(config){
10495     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10496 };
10497
10498 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10499     
10500     cls: false,
10501     tag: false,
10502     align: false,
10503     charoff: false,
10504     valign: false,
10505     
10506     getAutoCreate : function(){
10507         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10508         
10509         cfg = {
10510             tag: 'tbody'
10511         };
10512             
10513         if (this.cls) {
10514             cfg.cls=this.cls
10515         }
10516         if(this.tag){
10517             cfg.tag = this.tag;
10518         }
10519         
10520         if(this.align){
10521             cfg.align = this.align;
10522         }
10523         if(this.charoff){
10524             cfg.charoff = this.charoff;
10525         }
10526         if(this.valign){
10527             cfg.valign = this.valign;
10528         }
10529         
10530         return cfg;
10531     }
10532     
10533     
10534 //    initEvents : function()
10535 //    {
10536 //        
10537 //        if(!this.store){
10538 //            return;
10539 //        }
10540 //        
10541 //        this.store = Roo.factory(this.store, Roo.data);
10542 //        this.store.on('load', this.onLoad, this);
10543 //        
10544 //        this.store.load();
10545 //        
10546 //    },
10547 //    
10548 //    onLoad: function () 
10549 //    {   
10550 //        this.fireEvent('load', this);
10551 //    }
10552 //    
10553 //   
10554 });
10555
10556  
10557
10558  /*
10559  * Based on:
10560  * Ext JS Library 1.1.1
10561  * Copyright(c) 2006-2007, Ext JS, LLC.
10562  *
10563  * Originally Released Under LGPL - original licence link has changed is not relivant.
10564  *
10565  * Fork - LGPL
10566  * <script type="text/javascript">
10567  */
10568
10569 // as we use this in bootstrap.
10570 Roo.namespace('Roo.form');
10571  /**
10572  * @class Roo.form.Action
10573  * Internal Class used to handle form actions
10574  * @constructor
10575  * @param {Roo.form.BasicForm} el The form element or its id
10576  * @param {Object} config Configuration options
10577  */
10578
10579  
10580  
10581 // define the action interface
10582 Roo.form.Action = function(form, options){
10583     this.form = form;
10584     this.options = options || {};
10585 };
10586 /**
10587  * Client Validation Failed
10588  * @const 
10589  */
10590 Roo.form.Action.CLIENT_INVALID = 'client';
10591 /**
10592  * Server Validation Failed
10593  * @const 
10594  */
10595 Roo.form.Action.SERVER_INVALID = 'server';
10596  /**
10597  * Connect to Server Failed
10598  * @const 
10599  */
10600 Roo.form.Action.CONNECT_FAILURE = 'connect';
10601 /**
10602  * Reading Data from Server Failed
10603  * @const 
10604  */
10605 Roo.form.Action.LOAD_FAILURE = 'load';
10606
10607 Roo.form.Action.prototype = {
10608     type : 'default',
10609     failureType : undefined,
10610     response : undefined,
10611     result : undefined,
10612
10613     // interface method
10614     run : function(options){
10615
10616     },
10617
10618     // interface method
10619     success : function(response){
10620
10621     },
10622
10623     // interface method
10624     handleResponse : function(response){
10625
10626     },
10627
10628     // default connection failure
10629     failure : function(response){
10630         
10631         this.response = response;
10632         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10633         this.form.afterAction(this, false);
10634     },
10635
10636     processResponse : function(response){
10637         this.response = response;
10638         if(!response.responseText){
10639             return true;
10640         }
10641         this.result = this.handleResponse(response);
10642         return this.result;
10643     },
10644
10645     // utility functions used internally
10646     getUrl : function(appendParams){
10647         var url = this.options.url || this.form.url || this.form.el.dom.action;
10648         if(appendParams){
10649             var p = this.getParams();
10650             if(p){
10651                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10652             }
10653         }
10654         return url;
10655     },
10656
10657     getMethod : function(){
10658         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10659     },
10660
10661     getParams : function(){
10662         var bp = this.form.baseParams;
10663         var p = this.options.params;
10664         if(p){
10665             if(typeof p == "object"){
10666                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10667             }else if(typeof p == 'string' && bp){
10668                 p += '&' + Roo.urlEncode(bp);
10669             }
10670         }else if(bp){
10671             p = Roo.urlEncode(bp);
10672         }
10673         return p;
10674     },
10675
10676     createCallback : function(){
10677         return {
10678             success: this.success,
10679             failure: this.failure,
10680             scope: this,
10681             timeout: (this.form.timeout*1000),
10682             upload: this.form.fileUpload ? this.success : undefined
10683         };
10684     }
10685 };
10686
10687 Roo.form.Action.Submit = function(form, options){
10688     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10689 };
10690
10691 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10692     type : 'submit',
10693
10694     haveProgress : false,
10695     uploadComplete : false,
10696     
10697     // uploadProgress indicator.
10698     uploadProgress : function()
10699     {
10700         if (!this.form.progressUrl) {
10701             return;
10702         }
10703         
10704         if (!this.haveProgress) {
10705             Roo.MessageBox.progress("Uploading", "Uploading");
10706         }
10707         if (this.uploadComplete) {
10708            Roo.MessageBox.hide();
10709            return;
10710         }
10711         
10712         this.haveProgress = true;
10713    
10714         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10715         
10716         var c = new Roo.data.Connection();
10717         c.request({
10718             url : this.form.progressUrl,
10719             params: {
10720                 id : uid
10721             },
10722             method: 'GET',
10723             success : function(req){
10724                //console.log(data);
10725                 var rdata = false;
10726                 var edata;
10727                 try  {
10728                    rdata = Roo.decode(req.responseText)
10729                 } catch (e) {
10730                     Roo.log("Invalid data from server..");
10731                     Roo.log(edata);
10732                     return;
10733                 }
10734                 if (!rdata || !rdata.success) {
10735                     Roo.log(rdata);
10736                     Roo.MessageBox.alert(Roo.encode(rdata));
10737                     return;
10738                 }
10739                 var data = rdata.data;
10740                 
10741                 if (this.uploadComplete) {
10742                    Roo.MessageBox.hide();
10743                    return;
10744                 }
10745                    
10746                 if (data){
10747                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10748                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10749                     );
10750                 }
10751                 this.uploadProgress.defer(2000,this);
10752             },
10753        
10754             failure: function(data) {
10755                 Roo.log('progress url failed ');
10756                 Roo.log(data);
10757             },
10758             scope : this
10759         });
10760            
10761     },
10762     
10763     
10764     run : function()
10765     {
10766         // run get Values on the form, so it syncs any secondary forms.
10767         this.form.getValues();
10768         
10769         var o = this.options;
10770         var method = this.getMethod();
10771         var isPost = method == 'POST';
10772         if(o.clientValidation === false || this.form.isValid()){
10773             
10774             if (this.form.progressUrl) {
10775                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10776                     (new Date() * 1) + '' + Math.random());
10777                     
10778             } 
10779             
10780             
10781             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10782                 form:this.form.el.dom,
10783                 url:this.getUrl(!isPost),
10784                 method: method,
10785                 params:isPost ? this.getParams() : null,
10786                 isUpload: this.form.fileUpload,
10787                 formData : this.form.formData
10788             }));
10789             
10790             this.uploadProgress();
10791
10792         }else if (o.clientValidation !== false){ // client validation failed
10793             this.failureType = Roo.form.Action.CLIENT_INVALID;
10794             this.form.afterAction(this, false);
10795         }
10796     },
10797
10798     success : function(response)
10799     {
10800         this.uploadComplete= true;
10801         if (this.haveProgress) {
10802             Roo.MessageBox.hide();
10803         }
10804         
10805         
10806         var result = this.processResponse(response);
10807         if(result === true || result.success){
10808             this.form.afterAction(this, true);
10809             return;
10810         }
10811         if(result.errors){
10812             this.form.markInvalid(result.errors);
10813             this.failureType = Roo.form.Action.SERVER_INVALID;
10814         }
10815         this.form.afterAction(this, false);
10816     },
10817     failure : function(response)
10818     {
10819         this.uploadComplete= true;
10820         if (this.haveProgress) {
10821             Roo.MessageBox.hide();
10822         }
10823         
10824         this.response = response;
10825         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10826         this.form.afterAction(this, false);
10827     },
10828     
10829     handleResponse : function(response){
10830         if(this.form.errorReader){
10831             var rs = this.form.errorReader.read(response);
10832             var errors = [];
10833             if(rs.records){
10834                 for(var i = 0, len = rs.records.length; i < len; i++) {
10835                     var r = rs.records[i];
10836                     errors[i] = r.data;
10837                 }
10838             }
10839             if(errors.length < 1){
10840                 errors = null;
10841             }
10842             return {
10843                 success : rs.success,
10844                 errors : errors
10845             };
10846         }
10847         var ret = false;
10848         try {
10849             ret = Roo.decode(response.responseText);
10850         } catch (e) {
10851             ret = {
10852                 success: false,
10853                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10854                 errors : []
10855             };
10856         }
10857         return ret;
10858         
10859     }
10860 });
10861
10862
10863 Roo.form.Action.Load = function(form, options){
10864     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10865     this.reader = this.form.reader;
10866 };
10867
10868 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10869     type : 'load',
10870
10871     run : function(){
10872         
10873         Roo.Ajax.request(Roo.apply(
10874                 this.createCallback(), {
10875                     method:this.getMethod(),
10876                     url:this.getUrl(false),
10877                     params:this.getParams()
10878         }));
10879     },
10880
10881     success : function(response){
10882         
10883         var result = this.processResponse(response);
10884         if(result === true || !result.success || !result.data){
10885             this.failureType = Roo.form.Action.LOAD_FAILURE;
10886             this.form.afterAction(this, false);
10887             return;
10888         }
10889         this.form.clearInvalid();
10890         this.form.setValues(result.data);
10891         this.form.afterAction(this, true);
10892     },
10893
10894     handleResponse : function(response){
10895         if(this.form.reader){
10896             var rs = this.form.reader.read(response);
10897             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10898             return {
10899                 success : rs.success,
10900                 data : data
10901             };
10902         }
10903         return Roo.decode(response.responseText);
10904     }
10905 });
10906
10907 Roo.form.Action.ACTION_TYPES = {
10908     'load' : Roo.form.Action.Load,
10909     'submit' : Roo.form.Action.Submit
10910 };/*
10911  * - LGPL
10912  *
10913  * form
10914  *
10915  */
10916
10917 /**
10918  * @class Roo.bootstrap.Form
10919  * @extends Roo.bootstrap.Component
10920  * Bootstrap Form class
10921  * @cfg {String} method  GET | POST (default POST)
10922  * @cfg {String} labelAlign top | left (default top)
10923  * @cfg {String} align left  | right - for navbars
10924  * @cfg {Boolean} loadMask load mask when submit (default true)
10925
10926  *
10927  * @constructor
10928  * Create a new Form
10929  * @param {Object} config The config object
10930  */
10931
10932
10933 Roo.bootstrap.Form = function(config){
10934     
10935     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10936     
10937     Roo.bootstrap.Form.popover.apply();
10938     
10939     this.addEvents({
10940         /**
10941          * @event clientvalidation
10942          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10943          * @param {Form} this
10944          * @param {Boolean} valid true if the form has passed client-side validation
10945          */
10946         clientvalidation: true,
10947         /**
10948          * @event beforeaction
10949          * Fires before any action is performed. Return false to cancel the action.
10950          * @param {Form} this
10951          * @param {Action} action The action to be performed
10952          */
10953         beforeaction: true,
10954         /**
10955          * @event actionfailed
10956          * Fires when an action fails.
10957          * @param {Form} this
10958          * @param {Action} action The action that failed
10959          */
10960         actionfailed : true,
10961         /**
10962          * @event actioncomplete
10963          * Fires when an action is completed.
10964          * @param {Form} this
10965          * @param {Action} action The action that completed
10966          */
10967         actioncomplete : true
10968     });
10969 };
10970
10971 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10972
10973      /**
10974      * @cfg {String} method
10975      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10976      */
10977     method : 'POST',
10978     /**
10979      * @cfg {String} url
10980      * The URL to use for form actions if one isn't supplied in the action options.
10981      */
10982     /**
10983      * @cfg {Boolean} fileUpload
10984      * Set to true if this form is a file upload.
10985      */
10986
10987     /**
10988      * @cfg {Object} baseParams
10989      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10990      */
10991
10992     /**
10993      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10994      */
10995     timeout: 30,
10996     /**
10997      * @cfg {Sting} align (left|right) for navbar forms
10998      */
10999     align : 'left',
11000
11001     // private
11002     activeAction : null,
11003
11004     /**
11005      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11006      * element by passing it or its id or mask the form itself by passing in true.
11007      * @type Mixed
11008      */
11009     waitMsgTarget : false,
11010
11011     loadMask : true,
11012     
11013     /**
11014      * @cfg {Boolean} errorMask (true|false) default false
11015      */
11016     errorMask : false,
11017     
11018     /**
11019      * @cfg {Number} maskOffset Default 100
11020      */
11021     maskOffset : 100,
11022     
11023     /**
11024      * @cfg {Boolean} maskBody
11025      */
11026     maskBody : false,
11027
11028     getAutoCreate : function(){
11029
11030         var cfg = {
11031             tag: 'form',
11032             method : this.method || 'POST',
11033             id : this.id || Roo.id(),
11034             cls : ''
11035         };
11036         if (this.parent().xtype.match(/^Nav/)) {
11037             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11038
11039         }
11040
11041         if (this.labelAlign == 'left' ) {
11042             cfg.cls += ' form-horizontal';
11043         }
11044
11045
11046         return cfg;
11047     },
11048     initEvents : function()
11049     {
11050         this.el.on('submit', this.onSubmit, this);
11051         // this was added as random key presses on the form where triggering form submit.
11052         this.el.on('keypress', function(e) {
11053             if (e.getCharCode() != 13) {
11054                 return true;
11055             }
11056             // we might need to allow it for textareas.. and some other items.
11057             // check e.getTarget().
11058
11059             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11060                 return true;
11061             }
11062
11063             Roo.log("keypress blocked");
11064
11065             e.preventDefault();
11066             return false;
11067         });
11068         
11069     },
11070     // private
11071     onSubmit : function(e){
11072         e.stopEvent();
11073     },
11074
11075      /**
11076      * Returns true if client-side validation on the form is successful.
11077      * @return Boolean
11078      */
11079     isValid : function(){
11080         var items = this.getItems();
11081         var valid = true;
11082         var target = false;
11083         
11084         items.each(function(f){
11085             
11086             if(f.validate()){
11087                 return;
11088             }
11089             
11090             Roo.log('invalid field: ' + f.name);
11091             
11092             valid = false;
11093
11094             if(!target && f.el.isVisible(true)){
11095                 target = f;
11096             }
11097            
11098         });
11099         
11100         if(this.errorMask && !valid){
11101             Roo.bootstrap.Form.popover.mask(this, target);
11102         }
11103         
11104         return valid;
11105     },
11106     
11107     /**
11108      * Returns true if any fields in this form have changed since their original load.
11109      * @return Boolean
11110      */
11111     isDirty : function(){
11112         var dirty = false;
11113         var items = this.getItems();
11114         items.each(function(f){
11115            if(f.isDirty()){
11116                dirty = true;
11117                return false;
11118            }
11119            return true;
11120         });
11121         return dirty;
11122     },
11123      /**
11124      * Performs a predefined action (submit or load) or custom actions you define on this form.
11125      * @param {String} actionName The name of the action type
11126      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11127      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11128      * accept other config options):
11129      * <pre>
11130 Property          Type             Description
11131 ----------------  ---------------  ----------------------------------------------------------------------------------
11132 url               String           The url for the action (defaults to the form's url)
11133 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11134 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11135 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11136                                    validate the form on the client (defaults to false)
11137      * </pre>
11138      * @return {BasicForm} this
11139      */
11140     doAction : function(action, options){
11141         if(typeof action == 'string'){
11142             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11143         }
11144         if(this.fireEvent('beforeaction', this, action) !== false){
11145             this.beforeAction(action);
11146             action.run.defer(100, action);
11147         }
11148         return this;
11149     },
11150
11151     // private
11152     beforeAction : function(action){
11153         var o = action.options;
11154         
11155         if(this.loadMask){
11156             
11157             if(this.maskBody){
11158                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11159             } else {
11160                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11161             }
11162         }
11163         // not really supported yet.. ??
11164
11165         //if(this.waitMsgTarget === true){
11166         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11167         //}else if(this.waitMsgTarget){
11168         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11169         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11170         //}else {
11171         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11172        // }
11173
11174     },
11175
11176     // private
11177     afterAction : function(action, success){
11178         this.activeAction = null;
11179         var o = action.options;
11180
11181         if(this.loadMask){
11182             
11183             if(this.maskBody){
11184                 Roo.get(document.body).unmask();
11185             } else {
11186                 this.el.unmask();
11187             }
11188         }
11189         
11190         //if(this.waitMsgTarget === true){
11191 //            this.el.unmask();
11192         //}else if(this.waitMsgTarget){
11193         //    this.waitMsgTarget.unmask();
11194         //}else{
11195         //    Roo.MessageBox.updateProgress(1);
11196         //    Roo.MessageBox.hide();
11197        // }
11198         //
11199         if(success){
11200             if(o.reset){
11201                 this.reset();
11202             }
11203             Roo.callback(o.success, o.scope, [this, action]);
11204             this.fireEvent('actioncomplete', this, action);
11205
11206         }else{
11207
11208             // failure condition..
11209             // we have a scenario where updates need confirming.
11210             // eg. if a locking scenario exists..
11211             // we look for { errors : { needs_confirm : true }} in the response.
11212             if (
11213                 (typeof(action.result) != 'undefined')  &&
11214                 (typeof(action.result.errors) != 'undefined')  &&
11215                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11216            ){
11217                 var _t = this;
11218                 Roo.log("not supported yet");
11219                  /*
11220
11221                 Roo.MessageBox.confirm(
11222                     "Change requires confirmation",
11223                     action.result.errorMsg,
11224                     function(r) {
11225                         if (r != 'yes') {
11226                             return;
11227                         }
11228                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11229                     }
11230
11231                 );
11232                 */
11233
11234
11235                 return;
11236             }
11237
11238             Roo.callback(o.failure, o.scope, [this, action]);
11239             // show an error message if no failed handler is set..
11240             if (!this.hasListener('actionfailed')) {
11241                 Roo.log("need to add dialog support");
11242                 /*
11243                 Roo.MessageBox.alert("Error",
11244                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11245                         action.result.errorMsg :
11246                         "Saving Failed, please check your entries or try again"
11247                 );
11248                 */
11249             }
11250
11251             this.fireEvent('actionfailed', this, action);
11252         }
11253
11254     },
11255     /**
11256      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11257      * @param {String} id The value to search for
11258      * @return Field
11259      */
11260     findField : function(id){
11261         var items = this.getItems();
11262         var field = items.get(id);
11263         if(!field){
11264              items.each(function(f){
11265                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11266                     field = f;
11267                     return false;
11268                 }
11269                 return true;
11270             });
11271         }
11272         return field || null;
11273     },
11274      /**
11275      * Mark fields in this form invalid in bulk.
11276      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11277      * @return {BasicForm} this
11278      */
11279     markInvalid : function(errors){
11280         if(errors instanceof Array){
11281             for(var i = 0, len = errors.length; i < len; i++){
11282                 var fieldError = errors[i];
11283                 var f = this.findField(fieldError.id);
11284                 if(f){
11285                     f.markInvalid(fieldError.msg);
11286                 }
11287             }
11288         }else{
11289             var field, id;
11290             for(id in errors){
11291                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11292                     field.markInvalid(errors[id]);
11293                 }
11294             }
11295         }
11296         //Roo.each(this.childForms || [], function (f) {
11297         //    f.markInvalid(errors);
11298         //});
11299
11300         return this;
11301     },
11302
11303     /**
11304      * Set values for fields in this form in bulk.
11305      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11306      * @return {BasicForm} this
11307      */
11308     setValues : function(values){
11309         if(values instanceof Array){ // array of objects
11310             for(var i = 0, len = values.length; i < len; i++){
11311                 var v = values[i];
11312                 var f = this.findField(v.id);
11313                 if(f){
11314                     f.setValue(v.value);
11315                     if(this.trackResetOnLoad){
11316                         f.originalValue = f.getValue();
11317                     }
11318                 }
11319             }
11320         }else{ // object hash
11321             var field, id;
11322             for(id in values){
11323                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11324
11325                     if (field.setFromData &&
11326                         field.valueField &&
11327                         field.displayField &&
11328                         // combos' with local stores can
11329                         // be queried via setValue()
11330                         // to set their value..
11331                         (field.store && !field.store.isLocal)
11332                         ) {
11333                         // it's a combo
11334                         var sd = { };
11335                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11336                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11337                         field.setFromData(sd);
11338
11339                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11340                         
11341                         field.setFromData(values);
11342                         
11343                     } else {
11344                         field.setValue(values[id]);
11345                     }
11346
11347
11348                     if(this.trackResetOnLoad){
11349                         field.originalValue = field.getValue();
11350                     }
11351                 }
11352             }
11353         }
11354
11355         //Roo.each(this.childForms || [], function (f) {
11356         //    f.setValues(values);
11357         //});
11358
11359         return this;
11360     },
11361
11362     /**
11363      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11364      * they are returned as an array.
11365      * @param {Boolean} asString
11366      * @return {Object}
11367      */
11368     getValues : function(asString){
11369         //if (this.childForms) {
11370             // copy values from the child forms
11371         //    Roo.each(this.childForms, function (f) {
11372         //        this.setValues(f.getValues());
11373         //    }, this);
11374         //}
11375
11376
11377
11378         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11379         if(asString === true){
11380             return fs;
11381         }
11382         return Roo.urlDecode(fs);
11383     },
11384
11385     /**
11386      * Returns the fields in this form as an object with key/value pairs.
11387      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11388      * @return {Object}
11389      */
11390     getFieldValues : function(with_hidden)
11391     {
11392         var items = this.getItems();
11393         var ret = {};
11394         items.each(function(f){
11395             
11396             if (!f.getName()) {
11397                 return;
11398             }
11399             
11400             var v = f.getValue();
11401             
11402             if (f.inputType =='radio') {
11403                 if (typeof(ret[f.getName()]) == 'undefined') {
11404                     ret[f.getName()] = ''; // empty..
11405                 }
11406
11407                 if (!f.el.dom.checked) {
11408                     return;
11409
11410                 }
11411                 v = f.el.dom.value;
11412
11413             }
11414             
11415             if(f.xtype == 'MoneyField'){
11416                 ret[f.currencyName] = f.getCurrency();
11417             }
11418
11419             // not sure if this supported any more..
11420             if ((typeof(v) == 'object') && f.getRawValue) {
11421                 v = f.getRawValue() ; // dates..
11422             }
11423             // combo boxes where name != hiddenName...
11424             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11425                 ret[f.name] = f.getRawValue();
11426             }
11427             ret[f.getName()] = v;
11428         });
11429
11430         return ret;
11431     },
11432
11433     /**
11434      * Clears all invalid messages in this form.
11435      * @return {BasicForm} this
11436      */
11437     clearInvalid : function(){
11438         var items = this.getItems();
11439
11440         items.each(function(f){
11441            f.clearInvalid();
11442         });
11443
11444         return this;
11445     },
11446
11447     /**
11448      * Resets this form.
11449      * @return {BasicForm} this
11450      */
11451     reset : function(){
11452         var items = this.getItems();
11453         items.each(function(f){
11454             f.reset();
11455         });
11456
11457         Roo.each(this.childForms || [], function (f) {
11458             f.reset();
11459         });
11460
11461
11462         return this;
11463     },
11464     
11465     getItems : function()
11466     {
11467         var r=new Roo.util.MixedCollection(false, function(o){
11468             return o.id || (o.id = Roo.id());
11469         });
11470         var iter = function(el) {
11471             if (el.inputEl) {
11472                 r.add(el);
11473             }
11474             if (!el.items) {
11475                 return;
11476             }
11477             Roo.each(el.items,function(e) {
11478                 iter(e);
11479             });
11480         };
11481
11482         iter(this);
11483         return r;
11484     },
11485     
11486     hideFields : function(items)
11487     {
11488         Roo.each(items, function(i){
11489             
11490             var f = this.findField(i);
11491             
11492             if(!f){
11493                 return;
11494             }
11495             
11496             f.hide();
11497             
11498         }, this);
11499     },
11500     
11501     showFields : function(items)
11502     {
11503         Roo.each(items, function(i){
11504             
11505             var f = this.findField(i);
11506             
11507             if(!f){
11508                 return;
11509             }
11510             
11511             f.show();
11512             
11513         }, this);
11514     }
11515
11516 });
11517
11518 Roo.apply(Roo.bootstrap.Form, {
11519     
11520     popover : {
11521         
11522         padding : 5,
11523         
11524         isApplied : false,
11525         
11526         isMasked : false,
11527         
11528         form : false,
11529         
11530         target : false,
11531         
11532         toolTip : false,
11533         
11534         intervalID : false,
11535         
11536         maskEl : false,
11537         
11538         apply : function()
11539         {
11540             if(this.isApplied){
11541                 return;
11542             }
11543             
11544             this.maskEl = {
11545                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11546                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11547                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11548                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11549             };
11550             
11551             this.maskEl.top.enableDisplayMode("block");
11552             this.maskEl.left.enableDisplayMode("block");
11553             this.maskEl.bottom.enableDisplayMode("block");
11554             this.maskEl.right.enableDisplayMode("block");
11555             
11556             this.toolTip = new Roo.bootstrap.Tooltip({
11557                 cls : 'roo-form-error-popover',
11558                 alignment : {
11559                     'left' : ['r-l', [-2,0], 'right'],
11560                     'right' : ['l-r', [2,0], 'left'],
11561                     'bottom' : ['tl-bl', [0,2], 'top'],
11562                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11563                 }
11564             });
11565             
11566             this.toolTip.render(Roo.get(document.body));
11567
11568             this.toolTip.el.enableDisplayMode("block");
11569             
11570             Roo.get(document.body).on('click', function(){
11571                 this.unmask();
11572             }, this);
11573             
11574             Roo.get(document.body).on('touchstart', function(){
11575                 this.unmask();
11576             }, this);
11577             
11578             this.isApplied = true
11579         },
11580         
11581         mask : function(form, target)
11582         {
11583             this.form = form;
11584             
11585             this.target = target;
11586             
11587             if(!this.form.errorMask || !target.el){
11588                 return;
11589             }
11590             
11591             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11592             
11593             Roo.log(scrollable);
11594             
11595             var ot = this.target.el.calcOffsetsTo(scrollable);
11596             
11597             var scrollTo = ot[1] - this.form.maskOffset;
11598             
11599             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11600             
11601             scrollable.scrollTo('top', scrollTo);
11602             
11603             var box = this.target.el.getBox();
11604             Roo.log(box);
11605             var zIndex = Roo.bootstrap.Modal.zIndex++;
11606
11607             
11608             this.maskEl.top.setStyle('position', 'absolute');
11609             this.maskEl.top.setStyle('z-index', zIndex);
11610             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11611             this.maskEl.top.setLeft(0);
11612             this.maskEl.top.setTop(0);
11613             this.maskEl.top.show();
11614             
11615             this.maskEl.left.setStyle('position', 'absolute');
11616             this.maskEl.left.setStyle('z-index', zIndex);
11617             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11618             this.maskEl.left.setLeft(0);
11619             this.maskEl.left.setTop(box.y - this.padding);
11620             this.maskEl.left.show();
11621
11622             this.maskEl.bottom.setStyle('position', 'absolute');
11623             this.maskEl.bottom.setStyle('z-index', zIndex);
11624             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11625             this.maskEl.bottom.setLeft(0);
11626             this.maskEl.bottom.setTop(box.bottom + this.padding);
11627             this.maskEl.bottom.show();
11628
11629             this.maskEl.right.setStyle('position', 'absolute');
11630             this.maskEl.right.setStyle('z-index', zIndex);
11631             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11632             this.maskEl.right.setLeft(box.right + this.padding);
11633             this.maskEl.right.setTop(box.y - this.padding);
11634             this.maskEl.right.show();
11635
11636             this.toolTip.bindEl = this.target.el;
11637
11638             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11639
11640             var tip = this.target.blankText;
11641
11642             if(this.target.getValue() !== '' ) {
11643                 
11644                 if (this.target.invalidText.length) {
11645                     tip = this.target.invalidText;
11646                 } else if (this.target.regexText.length){
11647                     tip = this.target.regexText;
11648                 }
11649             }
11650
11651             this.toolTip.show(tip);
11652
11653             this.intervalID = window.setInterval(function() {
11654                 Roo.bootstrap.Form.popover.unmask();
11655             }, 10000);
11656
11657             window.onwheel = function(){ return false;};
11658             
11659             (function(){ this.isMasked = true; }).defer(500, this);
11660             
11661         },
11662         
11663         unmask : function()
11664         {
11665             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11666                 return;
11667             }
11668             
11669             this.maskEl.top.setStyle('position', 'absolute');
11670             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11671             this.maskEl.top.hide();
11672
11673             this.maskEl.left.setStyle('position', 'absolute');
11674             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11675             this.maskEl.left.hide();
11676
11677             this.maskEl.bottom.setStyle('position', 'absolute');
11678             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11679             this.maskEl.bottom.hide();
11680
11681             this.maskEl.right.setStyle('position', 'absolute');
11682             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11683             this.maskEl.right.hide();
11684             
11685             this.toolTip.hide();
11686             
11687             this.toolTip.el.hide();
11688             
11689             window.onwheel = function(){ return true;};
11690             
11691             if(this.intervalID){
11692                 window.clearInterval(this.intervalID);
11693                 this.intervalID = false;
11694             }
11695             
11696             this.isMasked = false;
11697             
11698         }
11699         
11700     }
11701     
11702 });
11703
11704 /*
11705  * Based on:
11706  * Ext JS Library 1.1.1
11707  * Copyright(c) 2006-2007, Ext JS, LLC.
11708  *
11709  * Originally Released Under LGPL - original licence link has changed is not relivant.
11710  *
11711  * Fork - LGPL
11712  * <script type="text/javascript">
11713  */
11714 /**
11715  * @class Roo.form.VTypes
11716  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11717  * @singleton
11718  */
11719 Roo.form.VTypes = function(){
11720     // closure these in so they are only created once.
11721     var alpha = /^[a-zA-Z_]+$/;
11722     var alphanum = /^[a-zA-Z0-9_]+$/;
11723     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11724     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11725
11726     // All these messages and functions are configurable
11727     return {
11728         /**
11729          * The function used to validate email addresses
11730          * @param {String} value The email address
11731          */
11732         'email' : function(v){
11733             return email.test(v);
11734         },
11735         /**
11736          * The error text to display when the email validation function returns false
11737          * @type String
11738          */
11739         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11740         /**
11741          * The keystroke filter mask to be applied on email input
11742          * @type RegExp
11743          */
11744         'emailMask' : /[a-z0-9_\.\-@]/i,
11745
11746         /**
11747          * The function used to validate URLs
11748          * @param {String} value The URL
11749          */
11750         'url' : function(v){
11751             return url.test(v);
11752         },
11753         /**
11754          * The error text to display when the url validation function returns false
11755          * @type String
11756          */
11757         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11758         
11759         /**
11760          * The function used to validate alpha values
11761          * @param {String} value The value
11762          */
11763         'alpha' : function(v){
11764             return alpha.test(v);
11765         },
11766         /**
11767          * The error text to display when the alpha validation function returns false
11768          * @type String
11769          */
11770         'alphaText' : 'This field should only contain letters and _',
11771         /**
11772          * The keystroke filter mask to be applied on alpha input
11773          * @type RegExp
11774          */
11775         'alphaMask' : /[a-z_]/i,
11776
11777         /**
11778          * The function used to validate alphanumeric values
11779          * @param {String} value The value
11780          */
11781         'alphanum' : function(v){
11782             return alphanum.test(v);
11783         },
11784         /**
11785          * The error text to display when the alphanumeric validation function returns false
11786          * @type String
11787          */
11788         'alphanumText' : 'This field should only contain letters, numbers and _',
11789         /**
11790          * The keystroke filter mask to be applied on alphanumeric input
11791          * @type RegExp
11792          */
11793         'alphanumMask' : /[a-z0-9_]/i
11794     };
11795 }();/*
11796  * - LGPL
11797  *
11798  * Input
11799  * 
11800  */
11801
11802 /**
11803  * @class Roo.bootstrap.Input
11804  * @extends Roo.bootstrap.Component
11805  * Bootstrap Input class
11806  * @cfg {Boolean} disabled is it disabled
11807  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11808  * @cfg {String} name name of the input
11809  * @cfg {string} fieldLabel - the label associated
11810  * @cfg {string} placeholder - placeholder to put in text.
11811  * @cfg {string}  before - input group add on before
11812  * @cfg {string} after - input group add on after
11813  * @cfg {string} size - (lg|sm) or leave empty..
11814  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11815  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11816  * @cfg {Number} md colspan out of 12 for computer-sized screens
11817  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11818  * @cfg {string} value default value of the input
11819  * @cfg {Number} labelWidth set the width of label 
11820  * @cfg {Number} labellg set the width of label (1-12)
11821  * @cfg {Number} labelmd set the width of label (1-12)
11822  * @cfg {Number} labelsm set the width of label (1-12)
11823  * @cfg {Number} labelxs set the width of label (1-12)
11824  * @cfg {String} labelAlign (top|left)
11825  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11826  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11827  * @cfg {String} indicatorpos (left|right) default left
11828  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11829  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11830  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11831
11832  * @cfg {String} align (left|center|right) Default left
11833  * @cfg {Boolean} forceFeedback (true|false) Default false
11834  * 
11835  * @constructor
11836  * Create a new Input
11837  * @param {Object} config The config object
11838  */
11839
11840 Roo.bootstrap.Input = function(config){
11841     
11842     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11843     
11844     this.addEvents({
11845         /**
11846          * @event focus
11847          * Fires when this field receives input focus.
11848          * @param {Roo.form.Field} this
11849          */
11850         focus : true,
11851         /**
11852          * @event blur
11853          * Fires when this field loses input focus.
11854          * @param {Roo.form.Field} this
11855          */
11856         blur : true,
11857         /**
11858          * @event specialkey
11859          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11860          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11861          * @param {Roo.form.Field} this
11862          * @param {Roo.EventObject} e The event object
11863          */
11864         specialkey : true,
11865         /**
11866          * @event change
11867          * Fires just before the field blurs if the field value has changed.
11868          * @param {Roo.form.Field} this
11869          * @param {Mixed} newValue The new value
11870          * @param {Mixed} oldValue The original value
11871          */
11872         change : true,
11873         /**
11874          * @event invalid
11875          * Fires after the field has been marked as invalid.
11876          * @param {Roo.form.Field} this
11877          * @param {String} msg The validation message
11878          */
11879         invalid : true,
11880         /**
11881          * @event valid
11882          * Fires after the field has been validated with no errors.
11883          * @param {Roo.form.Field} this
11884          */
11885         valid : true,
11886          /**
11887          * @event keyup
11888          * Fires after the key up
11889          * @param {Roo.form.Field} this
11890          * @param {Roo.EventObject}  e The event Object
11891          */
11892         keyup : true,
11893         /**
11894          * @event paste
11895          * Fires after the user pastes into input
11896          * @param {Roo.form.Field} this
11897          * @param {Roo.EventObject}  e The event Object
11898          */
11899         paste : true
11900     });
11901 };
11902
11903 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11904      /**
11905      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11906       automatic validation (defaults to "keyup").
11907      */
11908     validationEvent : "keyup",
11909      /**
11910      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11911      */
11912     validateOnBlur : true,
11913     /**
11914      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11915      */
11916     validationDelay : 250,
11917      /**
11918      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11919      */
11920     focusClass : "x-form-focus",  // not needed???
11921     
11922        
11923     /**
11924      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11925      */
11926     invalidClass : "has-warning",
11927     
11928     /**
11929      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11930      */
11931     validClass : "has-success",
11932     
11933     /**
11934      * @cfg {Boolean} hasFeedback (true|false) default true
11935      */
11936     hasFeedback : true,
11937     
11938     /**
11939      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11940      */
11941     invalidFeedbackClass : "glyphicon-warning-sign",
11942     
11943     /**
11944      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11945      */
11946     validFeedbackClass : "glyphicon-ok",
11947     
11948     /**
11949      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11950      */
11951     selectOnFocus : false,
11952     
11953      /**
11954      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11955      */
11956     maskRe : null,
11957        /**
11958      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11959      */
11960     vtype : null,
11961     
11962       /**
11963      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11964      */
11965     disableKeyFilter : false,
11966     
11967        /**
11968      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11969      */
11970     disabled : false,
11971      /**
11972      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11973      */
11974     allowBlank : true,
11975     /**
11976      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11977      */
11978     blankText : "Please complete this mandatory field",
11979     
11980      /**
11981      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11982      */
11983     minLength : 0,
11984     /**
11985      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11986      */
11987     maxLength : Number.MAX_VALUE,
11988     /**
11989      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11990      */
11991     minLengthText : "The minimum length for this field is {0}",
11992     /**
11993      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11994      */
11995     maxLengthText : "The maximum length for this field is {0}",
11996   
11997     
11998     /**
11999      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12000      * If available, this function will be called only after the basic validators all return true, and will be passed the
12001      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12002      */
12003     validator : null,
12004     /**
12005      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12006      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12007      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12008      */
12009     regex : null,
12010     /**
12011      * @cfg {String} regexText -- Depricated - use Invalid Text
12012      */
12013     regexText : "",
12014     
12015     /**
12016      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12017      */
12018     invalidText : "",
12019     
12020     
12021     
12022     autocomplete: false,
12023     
12024     
12025     fieldLabel : '',
12026     inputType : 'text',
12027     
12028     name : false,
12029     placeholder: false,
12030     before : false,
12031     after : false,
12032     size : false,
12033     hasFocus : false,
12034     preventMark: false,
12035     isFormField : true,
12036     value : '',
12037     labelWidth : 2,
12038     labelAlign : false,
12039     readOnly : false,
12040     align : false,
12041     formatedValue : false,
12042     forceFeedback : false,
12043     
12044     indicatorpos : 'left',
12045     
12046     labellg : 0,
12047     labelmd : 0,
12048     labelsm : 0,
12049     labelxs : 0,
12050     
12051     capture : '',
12052     accept : '',
12053     
12054     parentLabelAlign : function()
12055     {
12056         var parent = this;
12057         while (parent.parent()) {
12058             parent = parent.parent();
12059             if (typeof(parent.labelAlign) !='undefined') {
12060                 return parent.labelAlign;
12061             }
12062         }
12063         return 'left';
12064         
12065     },
12066     
12067     getAutoCreate : function()
12068     {
12069         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12070         
12071         var id = Roo.id();
12072         
12073         var cfg = {};
12074         
12075         if(this.inputType != 'hidden'){
12076             cfg.cls = 'form-group' //input-group
12077         }
12078         
12079         var input =  {
12080             tag: 'input',
12081             id : id,
12082             type : this.inputType,
12083             value : this.value,
12084             cls : 'form-control',
12085             placeholder : this.placeholder || '',
12086             autocomplete : this.autocomplete || 'new-password'
12087         };
12088         if (this.inputType == 'file') {
12089             input.style = 'overflow:hidden'; // why not in CSS?
12090         }
12091         
12092         if(this.capture.length){
12093             input.capture = this.capture;
12094         }
12095         
12096         if(this.accept.length){
12097             input.accept = this.accept + "/*";
12098         }
12099         
12100         if(this.align){
12101             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12102         }
12103         
12104         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12105             input.maxLength = this.maxLength;
12106         }
12107         
12108         if (this.disabled) {
12109             input.disabled=true;
12110         }
12111         
12112         if (this.readOnly) {
12113             input.readonly=true;
12114         }
12115         
12116         if (this.name) {
12117             input.name = this.name;
12118         }
12119         
12120         if (this.size) {
12121             input.cls += ' input-' + this.size;
12122         }
12123         
12124         var settings=this;
12125         ['xs','sm','md','lg'].map(function(size){
12126             if (settings[size]) {
12127                 cfg.cls += ' col-' + size + '-' + settings[size];
12128             }
12129         });
12130         
12131         var inputblock = input;
12132         
12133         var feedback = {
12134             tag: 'span',
12135             cls: 'glyphicon form-control-feedback'
12136         };
12137             
12138         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12139             
12140             inputblock = {
12141                 cls : 'has-feedback',
12142                 cn :  [
12143                     input,
12144                     feedback
12145                 ] 
12146             };  
12147         }
12148         
12149         if (this.before || this.after) {
12150             
12151             inputblock = {
12152                 cls : 'input-group',
12153                 cn :  [] 
12154             };
12155             
12156             if (this.before && typeof(this.before) == 'string') {
12157                 
12158                 inputblock.cn.push({
12159                     tag :'span',
12160                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12161                     html : this.before
12162                 });
12163             }
12164             if (this.before && typeof(this.before) == 'object') {
12165                 this.before = Roo.factory(this.before);
12166                 
12167                 inputblock.cn.push({
12168                     tag :'span',
12169                     cls : 'roo-input-before input-group-prepend   input-group-' +
12170                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12171                 });
12172             }
12173             
12174             inputblock.cn.push(input);
12175             
12176             if (this.after && typeof(this.after) == 'string') {
12177                 inputblock.cn.push({
12178                     tag :'span',
12179                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12180                     html : this.after
12181                 });
12182             }
12183             if (this.after && typeof(this.after) == 'object') {
12184                 this.after = Roo.factory(this.after);
12185                 
12186                 inputblock.cn.push({
12187                     tag :'span',
12188                     cls : 'roo-input-after input-group-append  input-group-' +
12189                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12190                 });
12191             }
12192             
12193             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12194                 inputblock.cls += ' has-feedback';
12195                 inputblock.cn.push(feedback);
12196             }
12197         };
12198         var indicator = {
12199             tag : 'i',
12200             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12201             tooltip : 'This field is required'
12202         };
12203         if (this.allowBlank ) {
12204             indicator.style = this.allowBlank ? ' display:none' : '';
12205         }
12206         if (align ==='left' && this.fieldLabel.length) {
12207             
12208             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12209             
12210             cfg.cn = [
12211                 indicator,
12212                 {
12213                     tag: 'label',
12214                     'for' :  id,
12215                     cls : 'control-label col-form-label',
12216                     html : this.fieldLabel
12217
12218                 },
12219                 {
12220                     cls : "", 
12221                     cn: [
12222                         inputblock
12223                     ]
12224                 }
12225             ];
12226             
12227             var labelCfg = cfg.cn[1];
12228             var contentCfg = cfg.cn[2];
12229             
12230             if(this.indicatorpos == 'right'){
12231                 cfg.cn = [
12232                     {
12233                         tag: 'label',
12234                         'for' :  id,
12235                         cls : 'control-label col-form-label',
12236                         cn : [
12237                             {
12238                                 tag : 'span',
12239                                 html : this.fieldLabel
12240                             },
12241                             indicator
12242                         ]
12243                     },
12244                     {
12245                         cls : "",
12246                         cn: [
12247                             inputblock
12248                         ]
12249                     }
12250
12251                 ];
12252                 
12253                 labelCfg = cfg.cn[0];
12254                 contentCfg = cfg.cn[1];
12255             
12256             }
12257             
12258             if(this.labelWidth > 12){
12259                 labelCfg.style = "width: " + this.labelWidth + 'px';
12260             }
12261             
12262             if(this.labelWidth < 13 && this.labelmd == 0){
12263                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12264             }
12265             
12266             if(this.labellg > 0){
12267                 labelCfg.cls += ' col-lg-' + this.labellg;
12268                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12269             }
12270             
12271             if(this.labelmd > 0){
12272                 labelCfg.cls += ' col-md-' + this.labelmd;
12273                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12274             }
12275             
12276             if(this.labelsm > 0){
12277                 labelCfg.cls += ' col-sm-' + this.labelsm;
12278                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12279             }
12280             
12281             if(this.labelxs > 0){
12282                 labelCfg.cls += ' col-xs-' + this.labelxs;
12283                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12284             }
12285             
12286             
12287         } else if ( this.fieldLabel.length) {
12288                 
12289             
12290             
12291             cfg.cn = [
12292                 {
12293                     tag : 'i',
12294                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12295                     tooltip : 'This field is required',
12296                     style : this.allowBlank ? ' display:none' : '' 
12297                 },
12298                 {
12299                     tag: 'label',
12300                    //cls : 'input-group-addon',
12301                     html : this.fieldLabel
12302
12303                 },
12304
12305                inputblock
12306
12307            ];
12308            
12309            if(this.indicatorpos == 'right'){
12310        
12311                 cfg.cn = [
12312                     {
12313                         tag: 'label',
12314                        //cls : 'input-group-addon',
12315                         html : this.fieldLabel
12316
12317                     },
12318                     {
12319                         tag : 'i',
12320                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12321                         tooltip : 'This field is required',
12322                         style : this.allowBlank ? ' display:none' : '' 
12323                     },
12324
12325                    inputblock
12326
12327                ];
12328
12329             }
12330
12331         } else {
12332             
12333             cfg.cn = [
12334
12335                     inputblock
12336
12337             ];
12338                 
12339                 
12340         };
12341         
12342         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12343            cfg.cls += ' navbar-form';
12344         }
12345         
12346         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12347             // on BS4 we do this only if not form 
12348             cfg.cls += ' navbar-form';
12349             cfg.tag = 'li';
12350         }
12351         
12352         return cfg;
12353         
12354     },
12355     /**
12356      * return the real input element.
12357      */
12358     inputEl: function ()
12359     {
12360         return this.el.select('input.form-control',true).first();
12361     },
12362     
12363     tooltipEl : function()
12364     {
12365         return this.inputEl();
12366     },
12367     
12368     indicatorEl : function()
12369     {
12370         if (Roo.bootstrap.version == 4) {
12371             return false; // not enabled in v4 yet.
12372         }
12373         
12374         var indicator = this.el.select('i.roo-required-indicator',true).first();
12375         
12376         if(!indicator){
12377             return false;
12378         }
12379         
12380         return indicator;
12381         
12382     },
12383     
12384     setDisabled : function(v)
12385     {
12386         var i  = this.inputEl().dom;
12387         if (!v) {
12388             i.removeAttribute('disabled');
12389             return;
12390             
12391         }
12392         i.setAttribute('disabled','true');
12393     },
12394     initEvents : function()
12395     {
12396           
12397         this.inputEl().on("keydown" , this.fireKey,  this);
12398         this.inputEl().on("focus", this.onFocus,  this);
12399         this.inputEl().on("blur", this.onBlur,  this);
12400         
12401         this.inputEl().relayEvent('keyup', this);
12402         this.inputEl().relayEvent('paste', this);
12403         
12404         this.indicator = this.indicatorEl();
12405         
12406         if(this.indicator){
12407             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12408         }
12409  
12410         // reference to original value for reset
12411         this.originalValue = this.getValue();
12412         //Roo.form.TextField.superclass.initEvents.call(this);
12413         if(this.validationEvent == 'keyup'){
12414             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12415             this.inputEl().on('keyup', this.filterValidation, this);
12416         }
12417         else if(this.validationEvent !== false){
12418             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12419         }
12420         
12421         if(this.selectOnFocus){
12422             this.on("focus", this.preFocus, this);
12423             
12424         }
12425         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12426             this.inputEl().on("keypress", this.filterKeys, this);
12427         } else {
12428             this.inputEl().relayEvent('keypress', this);
12429         }
12430        /* if(this.grow){
12431             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12432             this.el.on("click", this.autoSize,  this);
12433         }
12434         */
12435         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12436             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12437         }
12438         
12439         if (typeof(this.before) == 'object') {
12440             this.before.render(this.el.select('.roo-input-before',true).first());
12441         }
12442         if (typeof(this.after) == 'object') {
12443             this.after.render(this.el.select('.roo-input-after',true).first());
12444         }
12445         
12446         this.inputEl().on('change', this.onChange, this);
12447         
12448     },
12449     filterValidation : function(e){
12450         if(!e.isNavKeyPress()){
12451             this.validationTask.delay(this.validationDelay);
12452         }
12453     },
12454      /**
12455      * Validates the field value
12456      * @return {Boolean} True if the value is valid, else false
12457      */
12458     validate : function(){
12459         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12460         if(this.disabled || this.validateValue(this.getRawValue())){
12461             this.markValid();
12462             return true;
12463         }
12464         
12465         this.markInvalid();
12466         return false;
12467     },
12468     
12469     
12470     /**
12471      * Validates a value according to the field's validation rules and marks the field as invalid
12472      * if the validation fails
12473      * @param {Mixed} value The value to validate
12474      * @return {Boolean} True if the value is valid, else false
12475      */
12476     validateValue : function(value)
12477     {
12478         if(this.getVisibilityEl().hasClass('hidden')){
12479             return true;
12480         }
12481         
12482         if(value.length < 1)  { // if it's blank
12483             if(this.allowBlank){
12484                 return true;
12485             }
12486             return false;
12487         }
12488         
12489         if(value.length < this.minLength){
12490             return false;
12491         }
12492         if(value.length > this.maxLength){
12493             return false;
12494         }
12495         if(this.vtype){
12496             var vt = Roo.form.VTypes;
12497             if(!vt[this.vtype](value, this)){
12498                 return false;
12499             }
12500         }
12501         if(typeof this.validator == "function"){
12502             var msg = this.validator(value);
12503             if(msg !== true){
12504                 return false;
12505             }
12506             if (typeof(msg) == 'string') {
12507                 this.invalidText = msg;
12508             }
12509         }
12510         
12511         if(this.regex && !this.regex.test(value)){
12512             return false;
12513         }
12514         
12515         return true;
12516     },
12517     
12518      // private
12519     fireKey : function(e){
12520         //Roo.log('field ' + e.getKey());
12521         if(e.isNavKeyPress()){
12522             this.fireEvent("specialkey", this, e);
12523         }
12524     },
12525     focus : function (selectText){
12526         if(this.rendered){
12527             this.inputEl().focus();
12528             if(selectText === true){
12529                 this.inputEl().dom.select();
12530             }
12531         }
12532         return this;
12533     } ,
12534     
12535     onFocus : function(){
12536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12537            // this.el.addClass(this.focusClass);
12538         }
12539         if(!this.hasFocus){
12540             this.hasFocus = true;
12541             this.startValue = this.getValue();
12542             this.fireEvent("focus", this);
12543         }
12544     },
12545     
12546     beforeBlur : Roo.emptyFn,
12547
12548     
12549     // private
12550     onBlur : function(){
12551         this.beforeBlur();
12552         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12553             //this.el.removeClass(this.focusClass);
12554         }
12555         this.hasFocus = false;
12556         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12557             this.validate();
12558         }
12559         var v = this.getValue();
12560         if(String(v) !== String(this.startValue)){
12561             this.fireEvent('change', this, v, this.startValue);
12562         }
12563         this.fireEvent("blur", this);
12564     },
12565     
12566     onChange : function(e)
12567     {
12568         var v = this.getValue();
12569         if(String(v) !== String(this.startValue)){
12570             this.fireEvent('change', this, v, this.startValue);
12571         }
12572         
12573     },
12574     
12575     /**
12576      * Resets the current field value to the originally loaded value and clears any validation messages
12577      */
12578     reset : function(){
12579         this.setValue(this.originalValue);
12580         this.validate();
12581     },
12582      /**
12583      * Returns the name of the field
12584      * @return {Mixed} name The name field
12585      */
12586     getName: function(){
12587         return this.name;
12588     },
12589      /**
12590      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12591      * @return {Mixed} value The field value
12592      */
12593     getValue : function(){
12594         
12595         var v = this.inputEl().getValue();
12596         
12597         return v;
12598     },
12599     /**
12600      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12601      * @return {Mixed} value The field value
12602      */
12603     getRawValue : function(){
12604         var v = this.inputEl().getValue();
12605         
12606         return v;
12607     },
12608     
12609     /**
12610      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12611      * @param {Mixed} value The value to set
12612      */
12613     setRawValue : function(v){
12614         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12615     },
12616     
12617     selectText : function(start, end){
12618         var v = this.getRawValue();
12619         if(v.length > 0){
12620             start = start === undefined ? 0 : start;
12621             end = end === undefined ? v.length : end;
12622             var d = this.inputEl().dom;
12623             if(d.setSelectionRange){
12624                 d.setSelectionRange(start, end);
12625             }else if(d.createTextRange){
12626                 var range = d.createTextRange();
12627                 range.moveStart("character", start);
12628                 range.moveEnd("character", v.length-end);
12629                 range.select();
12630             }
12631         }
12632     },
12633     
12634     /**
12635      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12636      * @param {Mixed} value The value to set
12637      */
12638     setValue : function(v){
12639         this.value = v;
12640         if(this.rendered){
12641             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12642             this.validate();
12643         }
12644     },
12645     
12646     /*
12647     processValue : function(value){
12648         if(this.stripCharsRe){
12649             var newValue = value.replace(this.stripCharsRe, '');
12650             if(newValue !== value){
12651                 this.setRawValue(newValue);
12652                 return newValue;
12653             }
12654         }
12655         return value;
12656     },
12657   */
12658     preFocus : function(){
12659         
12660         if(this.selectOnFocus){
12661             this.inputEl().dom.select();
12662         }
12663     },
12664     filterKeys : function(e){
12665         var k = e.getKey();
12666         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12667             return;
12668         }
12669         var c = e.getCharCode(), cc = String.fromCharCode(c);
12670         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12671             return;
12672         }
12673         if(!this.maskRe.test(cc)){
12674             e.stopEvent();
12675         }
12676     },
12677      /**
12678      * Clear any invalid styles/messages for this field
12679      */
12680     clearInvalid : function(){
12681         
12682         if(!this.el || this.preventMark){ // not rendered
12683             return;
12684         }
12685         
12686         
12687         this.el.removeClass([this.invalidClass, 'is-invalid']);
12688         
12689         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12690             
12691             var feedback = this.el.select('.form-control-feedback', true).first();
12692             
12693             if(feedback){
12694                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12695             }
12696             
12697         }
12698         
12699         if(this.indicator){
12700             this.indicator.removeClass('visible');
12701             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12702         }
12703         
12704         this.fireEvent('valid', this);
12705     },
12706     
12707      /**
12708      * Mark this field as valid
12709      */
12710     markValid : function()
12711     {
12712         if(!this.el  || this.preventMark){ // not rendered...
12713             return;
12714         }
12715         
12716         this.el.removeClass([this.invalidClass, this.validClass]);
12717         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12718
12719         var feedback = this.el.select('.form-control-feedback', true).first();
12720             
12721         if(feedback){
12722             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12723         }
12724         
12725         if(this.indicator){
12726             this.indicator.removeClass('visible');
12727             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12728         }
12729         
12730         if(this.disabled){
12731             return;
12732         }
12733         
12734            
12735         if(this.allowBlank && !this.getRawValue().length){
12736             return;
12737         }
12738         if (Roo.bootstrap.version == 3) {
12739             this.el.addClass(this.validClass);
12740         } else {
12741             this.inputEl().addClass('is-valid');
12742         }
12743
12744         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12745             
12746             var feedback = this.el.select('.form-control-feedback', true).first();
12747             
12748             if(feedback){
12749                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12750                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12751             }
12752             
12753         }
12754         
12755         this.fireEvent('valid', this);
12756     },
12757     
12758      /**
12759      * Mark this field as invalid
12760      * @param {String} msg The validation message
12761      */
12762     markInvalid : function(msg)
12763     {
12764         if(!this.el  || this.preventMark){ // not rendered
12765             return;
12766         }
12767         
12768         this.el.removeClass([this.invalidClass, this.validClass]);
12769         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12770         
12771         var feedback = this.el.select('.form-control-feedback', true).first();
12772             
12773         if(feedback){
12774             this.el.select('.form-control-feedback', true).first().removeClass(
12775                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12776         }
12777
12778         if(this.disabled){
12779             return;
12780         }
12781         
12782         if(this.allowBlank && !this.getRawValue().length){
12783             return;
12784         }
12785         
12786         if(this.indicator){
12787             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12788             this.indicator.addClass('visible');
12789         }
12790         if (Roo.bootstrap.version == 3) {
12791             this.el.addClass(this.invalidClass);
12792         } else {
12793             this.inputEl().addClass('is-invalid');
12794         }
12795         
12796         
12797         
12798         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12799             
12800             var feedback = this.el.select('.form-control-feedback', true).first();
12801             
12802             if(feedback){
12803                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12804                 
12805                 if(this.getValue().length || this.forceFeedback){
12806                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12807                 }
12808                 
12809             }
12810             
12811         }
12812         
12813         this.fireEvent('invalid', this, msg);
12814     },
12815     // private
12816     SafariOnKeyDown : function(event)
12817     {
12818         // this is a workaround for a password hang bug on chrome/ webkit.
12819         if (this.inputEl().dom.type != 'password') {
12820             return;
12821         }
12822         
12823         var isSelectAll = false;
12824         
12825         if(this.inputEl().dom.selectionEnd > 0){
12826             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12827         }
12828         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12829             event.preventDefault();
12830             this.setValue('');
12831             return;
12832         }
12833         
12834         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12835             
12836             event.preventDefault();
12837             // this is very hacky as keydown always get's upper case.
12838             //
12839             var cc = String.fromCharCode(event.getCharCode());
12840             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12841             
12842         }
12843     },
12844     adjustWidth : function(tag, w){
12845         tag = tag.toLowerCase();
12846         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12847             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12848                 if(tag == 'input'){
12849                     return w + 2;
12850                 }
12851                 if(tag == 'textarea'){
12852                     return w-2;
12853                 }
12854             }else if(Roo.isOpera){
12855                 if(tag == 'input'){
12856                     return w + 2;
12857                 }
12858                 if(tag == 'textarea'){
12859                     return w-2;
12860                 }
12861             }
12862         }
12863         return w;
12864     },
12865     
12866     setFieldLabel : function(v)
12867     {
12868         if(!this.rendered){
12869             return;
12870         }
12871         
12872         if(this.indicatorEl()){
12873             var ar = this.el.select('label > span',true);
12874             
12875             if (ar.elements.length) {
12876                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12877                 this.fieldLabel = v;
12878                 return;
12879             }
12880             
12881             var br = this.el.select('label',true);
12882             
12883             if(br.elements.length) {
12884                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12885                 this.fieldLabel = v;
12886                 return;
12887             }
12888             
12889             Roo.log('Cannot Found any of label > span || label in input');
12890             return;
12891         }
12892         
12893         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12894         this.fieldLabel = v;
12895         
12896         
12897     }
12898 });
12899
12900  
12901 /*
12902  * - LGPL
12903  *
12904  * Input
12905  * 
12906  */
12907
12908 /**
12909  * @class Roo.bootstrap.TextArea
12910  * @extends Roo.bootstrap.Input
12911  * Bootstrap TextArea class
12912  * @cfg {Number} cols Specifies the visible width of a text area
12913  * @cfg {Number} rows Specifies the visible number of lines in a text area
12914  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12915  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12916  * @cfg {string} html text
12917  * 
12918  * @constructor
12919  * Create a new TextArea
12920  * @param {Object} config The config object
12921  */
12922
12923 Roo.bootstrap.TextArea = function(config){
12924     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12925    
12926 };
12927
12928 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12929      
12930     cols : false,
12931     rows : 5,
12932     readOnly : false,
12933     warp : 'soft',
12934     resize : false,
12935     value: false,
12936     html: false,
12937     
12938     getAutoCreate : function(){
12939         
12940         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12941         
12942         var id = Roo.id();
12943         
12944         var cfg = {};
12945         
12946         if(this.inputType != 'hidden'){
12947             cfg.cls = 'form-group' //input-group
12948         }
12949         
12950         var input =  {
12951             tag: 'textarea',
12952             id : id,
12953             warp : this.warp,
12954             rows : this.rows,
12955             value : this.value || '',
12956             html: this.html || '',
12957             cls : 'form-control',
12958             placeholder : this.placeholder || '' 
12959             
12960         };
12961         
12962         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12963             input.maxLength = this.maxLength;
12964         }
12965         
12966         if(this.resize){
12967             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12968         }
12969         
12970         if(this.cols){
12971             input.cols = this.cols;
12972         }
12973         
12974         if (this.readOnly) {
12975             input.readonly = true;
12976         }
12977         
12978         if (this.name) {
12979             input.name = this.name;
12980         }
12981         
12982         if (this.size) {
12983             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12984         }
12985         
12986         var settings=this;
12987         ['xs','sm','md','lg'].map(function(size){
12988             if (settings[size]) {
12989                 cfg.cls += ' col-' + size + '-' + settings[size];
12990             }
12991         });
12992         
12993         var inputblock = input;
12994         
12995         if(this.hasFeedback && !this.allowBlank){
12996             
12997             var feedback = {
12998                 tag: 'span',
12999                 cls: 'glyphicon form-control-feedback'
13000             };
13001
13002             inputblock = {
13003                 cls : 'has-feedback',
13004                 cn :  [
13005                     input,
13006                     feedback
13007                 ] 
13008             };  
13009         }
13010         
13011         
13012         if (this.before || this.after) {
13013             
13014             inputblock = {
13015                 cls : 'input-group',
13016                 cn :  [] 
13017             };
13018             if (this.before) {
13019                 inputblock.cn.push({
13020                     tag :'span',
13021                     cls : 'input-group-addon',
13022                     html : this.before
13023                 });
13024             }
13025             
13026             inputblock.cn.push(input);
13027             
13028             if(this.hasFeedback && !this.allowBlank){
13029                 inputblock.cls += ' has-feedback';
13030                 inputblock.cn.push(feedback);
13031             }
13032             
13033             if (this.after) {
13034                 inputblock.cn.push({
13035                     tag :'span',
13036                     cls : 'input-group-addon',
13037                     html : this.after
13038                 });
13039             }
13040             
13041         }
13042         
13043         if (align ==='left' && this.fieldLabel.length) {
13044             cfg.cn = [
13045                 {
13046                     tag: 'label',
13047                     'for' :  id,
13048                     cls : 'control-label',
13049                     html : this.fieldLabel
13050                 },
13051                 {
13052                     cls : "",
13053                     cn: [
13054                         inputblock
13055                     ]
13056                 }
13057
13058             ];
13059             
13060             if(this.labelWidth > 12){
13061                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13062             }
13063
13064             if(this.labelWidth < 13 && this.labelmd == 0){
13065                 this.labelmd = this.labelWidth;
13066             }
13067
13068             if(this.labellg > 0){
13069                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13070                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13071             }
13072
13073             if(this.labelmd > 0){
13074                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13075                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13076             }
13077
13078             if(this.labelsm > 0){
13079                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13080                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13081             }
13082
13083             if(this.labelxs > 0){
13084                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13085                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13086             }
13087             
13088         } else if ( this.fieldLabel.length) {
13089             cfg.cn = [
13090
13091                {
13092                    tag: 'label',
13093                    //cls : 'input-group-addon',
13094                    html : this.fieldLabel
13095
13096                },
13097
13098                inputblock
13099
13100            ];
13101
13102         } else {
13103
13104             cfg.cn = [
13105
13106                 inputblock
13107
13108             ];
13109                 
13110         }
13111         
13112         if (this.disabled) {
13113             input.disabled=true;
13114         }
13115         
13116         return cfg;
13117         
13118     },
13119     /**
13120      * return the real textarea element.
13121      */
13122     inputEl: function ()
13123     {
13124         return this.el.select('textarea.form-control',true).first();
13125     },
13126     
13127     /**
13128      * Clear any invalid styles/messages for this field
13129      */
13130     clearInvalid : function()
13131     {
13132         
13133         if(!this.el || this.preventMark){ // not rendered
13134             return;
13135         }
13136         
13137         var label = this.el.select('label', true).first();
13138         var icon = this.el.select('i.fa-star', true).first();
13139         
13140         if(label && icon){
13141             icon.remove();
13142         }
13143         this.el.removeClass( this.validClass);
13144         this.inputEl().removeClass('is-invalid');
13145          
13146         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13147             
13148             var feedback = this.el.select('.form-control-feedback', true).first();
13149             
13150             if(feedback){
13151                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13152             }
13153             
13154         }
13155         
13156         this.fireEvent('valid', this);
13157     },
13158     
13159      /**
13160      * Mark this field as valid
13161      */
13162     markValid : function()
13163     {
13164         if(!this.el  || this.preventMark){ // not rendered
13165             return;
13166         }
13167         
13168         this.el.removeClass([this.invalidClass, this.validClass]);
13169         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13170         
13171         var feedback = this.el.select('.form-control-feedback', true).first();
13172             
13173         if(feedback){
13174             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13175         }
13176
13177         if(this.disabled || this.allowBlank){
13178             return;
13179         }
13180         
13181         var label = this.el.select('label', true).first();
13182         var icon = this.el.select('i.fa-star', true).first();
13183         
13184         if(label && icon){
13185             icon.remove();
13186         }
13187         if (Roo.bootstrap.version == 3) {
13188             this.el.addClass(this.validClass);
13189         } else {
13190             this.inputEl().addClass('is-valid');
13191         }
13192         
13193         
13194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13195             
13196             var feedback = this.el.select('.form-control-feedback', true).first();
13197             
13198             if(feedback){
13199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13200                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13201             }
13202             
13203         }
13204         
13205         this.fireEvent('valid', this);
13206     },
13207     
13208      /**
13209      * Mark this field as invalid
13210      * @param {String} msg The validation message
13211      */
13212     markInvalid : function(msg)
13213     {
13214         if(!this.el  || this.preventMark){ // not rendered
13215             return;
13216         }
13217         
13218         this.el.removeClass([this.invalidClass, this.validClass]);
13219         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13220         
13221         var feedback = this.el.select('.form-control-feedback', true).first();
13222             
13223         if(feedback){
13224             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13225         }
13226
13227         if(this.disabled || this.allowBlank){
13228             return;
13229         }
13230         
13231         var label = this.el.select('label', true).first();
13232         var icon = this.el.select('i.fa-star', true).first();
13233         
13234         if(!this.getValue().length && label && !icon){
13235             this.el.createChild({
13236                 tag : 'i',
13237                 cls : 'text-danger fa fa-lg fa-star',
13238                 tooltip : 'This field is required',
13239                 style : 'margin-right:5px;'
13240             }, label, true);
13241         }
13242         
13243         if (Roo.bootstrap.version == 3) {
13244             this.el.addClass(this.invalidClass);
13245         } else {
13246             this.inputEl().addClass('is-invalid');
13247         }
13248         
13249         // fixme ... this may be depricated need to test..
13250         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13251             
13252             var feedback = this.el.select('.form-control-feedback', true).first();
13253             
13254             if(feedback){
13255                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13256                 
13257                 if(this.getValue().length || this.forceFeedback){
13258                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13259                 }
13260                 
13261             }
13262             
13263         }
13264         
13265         this.fireEvent('invalid', this, msg);
13266     }
13267 });
13268
13269  
13270 /*
13271  * - LGPL
13272  *
13273  * trigger field - base class for combo..
13274  * 
13275  */
13276  
13277 /**
13278  * @class Roo.bootstrap.TriggerField
13279  * @extends Roo.bootstrap.Input
13280  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13281  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13282  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13283  * for which you can provide a custom implementation.  For example:
13284  * <pre><code>
13285 var trigger = new Roo.bootstrap.TriggerField();
13286 trigger.onTriggerClick = myTriggerFn;
13287 trigger.applyTo('my-field');
13288 </code></pre>
13289  *
13290  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13291  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13292  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13293  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13294  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13295
13296  * @constructor
13297  * Create a new TriggerField.
13298  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13299  * to the base TextField)
13300  */
13301 Roo.bootstrap.TriggerField = function(config){
13302     this.mimicing = false;
13303     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13304 };
13305
13306 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13307     /**
13308      * @cfg {String} triggerClass A CSS class to apply to the trigger
13309      */
13310      /**
13311      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13312      */
13313     hideTrigger:false,
13314
13315     /**
13316      * @cfg {Boolean} removable (true|false) special filter default false
13317      */
13318     removable : false,
13319     
13320     /** @cfg {Boolean} grow @hide */
13321     /** @cfg {Number} growMin @hide */
13322     /** @cfg {Number} growMax @hide */
13323
13324     /**
13325      * @hide 
13326      * @method
13327      */
13328     autoSize: Roo.emptyFn,
13329     // private
13330     monitorTab : true,
13331     // private
13332     deferHeight : true,
13333
13334     
13335     actionMode : 'wrap',
13336     
13337     caret : false,
13338     
13339     
13340     getAutoCreate : function(){
13341        
13342         var align = this.labelAlign || this.parentLabelAlign();
13343         
13344         var id = Roo.id();
13345         
13346         var cfg = {
13347             cls: 'form-group' //input-group
13348         };
13349         
13350         
13351         var input =  {
13352             tag: 'input',
13353             id : id,
13354             type : this.inputType,
13355             cls : 'form-control',
13356             autocomplete: 'new-password',
13357             placeholder : this.placeholder || '' 
13358             
13359         };
13360         if (this.name) {
13361             input.name = this.name;
13362         }
13363         if (this.size) {
13364             input.cls += ' input-' + this.size;
13365         }
13366         
13367         if (this.disabled) {
13368             input.disabled=true;
13369         }
13370         
13371         var inputblock = input;
13372         
13373         if(this.hasFeedback && !this.allowBlank){
13374             
13375             var feedback = {
13376                 tag: 'span',
13377                 cls: 'glyphicon form-control-feedback'
13378             };
13379             
13380             if(this.removable && !this.editable  ){
13381                 inputblock = {
13382                     cls : 'has-feedback',
13383                     cn :  [
13384                         inputblock,
13385                         {
13386                             tag: 'button',
13387                             html : 'x',
13388                             cls : 'roo-combo-removable-btn close'
13389                         },
13390                         feedback
13391                     ] 
13392                 };
13393             } else {
13394                 inputblock = {
13395                     cls : 'has-feedback',
13396                     cn :  [
13397                         inputblock,
13398                         feedback
13399                     ] 
13400                 };
13401             }
13402
13403         } else {
13404             if(this.removable && !this.editable ){
13405                 inputblock = {
13406                     cls : 'roo-removable',
13407                     cn :  [
13408                         inputblock,
13409                         {
13410                             tag: 'button',
13411                             html : 'x',
13412                             cls : 'roo-combo-removable-btn close'
13413                         }
13414                     ] 
13415                 };
13416             }
13417         }
13418         
13419         if (this.before || this.after) {
13420             
13421             inputblock = {
13422                 cls : 'input-group',
13423                 cn :  [] 
13424             };
13425             if (this.before) {
13426                 inputblock.cn.push({
13427                     tag :'span',
13428                     cls : 'input-group-addon input-group-prepend input-group-text',
13429                     html : this.before
13430                 });
13431             }
13432             
13433             inputblock.cn.push(input);
13434             
13435             if(this.hasFeedback && !this.allowBlank){
13436                 inputblock.cls += ' has-feedback';
13437                 inputblock.cn.push(feedback);
13438             }
13439             
13440             if (this.after) {
13441                 inputblock.cn.push({
13442                     tag :'span',
13443                     cls : 'input-group-addon input-group-append input-group-text',
13444                     html : this.after
13445                 });
13446             }
13447             
13448         };
13449         
13450       
13451         
13452         var ibwrap = inputblock;
13453         
13454         if(this.multiple){
13455             ibwrap = {
13456                 tag: 'ul',
13457                 cls: 'roo-select2-choices',
13458                 cn:[
13459                     {
13460                         tag: 'li',
13461                         cls: 'roo-select2-search-field',
13462                         cn: [
13463
13464                             inputblock
13465                         ]
13466                     }
13467                 ]
13468             };
13469                 
13470         }
13471         
13472         var combobox = {
13473             cls: 'roo-select2-container input-group',
13474             cn: [
13475                  {
13476                     tag: 'input',
13477                     type : 'hidden',
13478                     cls: 'form-hidden-field'
13479                 },
13480                 ibwrap
13481             ]
13482         };
13483         
13484         if(!this.multiple && this.showToggleBtn){
13485             
13486             var caret = {
13487                         tag: 'span',
13488                         cls: 'caret'
13489              };
13490             if (this.caret != false) {
13491                 caret = {
13492                      tag: 'i',
13493                      cls: 'fa fa-' + this.caret
13494                 };
13495                 
13496             }
13497             
13498             combobox.cn.push({
13499                 tag :'span',
13500                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13501                 cn : [
13502                     Roo.bootstrap.version == 3 ? caret : '',
13503                     {
13504                         tag: 'span',
13505                         cls: 'combobox-clear',
13506                         cn  : [
13507                             {
13508                                 tag : 'i',
13509                                 cls: 'icon-remove'
13510                             }
13511                         ]
13512                     }
13513                 ]
13514
13515             })
13516         }
13517         
13518         if(this.multiple){
13519             combobox.cls += ' roo-select2-container-multi';
13520         }
13521          var indicator = {
13522             tag : 'i',
13523             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13524             tooltip : 'This field is required'
13525         };
13526         if (Roo.bootstrap.version == 4) {
13527             indicator = {
13528                 tag : 'i',
13529                 style : 'display:none'
13530             };
13531         }
13532         
13533         
13534         if (align ==='left' && this.fieldLabel.length) {
13535             
13536             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13537
13538             cfg.cn = [
13539                 indicator,
13540                 {
13541                     tag: 'label',
13542                     'for' :  id,
13543                     cls : 'control-label',
13544                     html : this.fieldLabel
13545
13546                 },
13547                 {
13548                     cls : "", 
13549                     cn: [
13550                         combobox
13551                     ]
13552                 }
13553
13554             ];
13555             
13556             var labelCfg = cfg.cn[1];
13557             var contentCfg = cfg.cn[2];
13558             
13559             if(this.indicatorpos == 'right'){
13560                 cfg.cn = [
13561                     {
13562                         tag: 'label',
13563                         'for' :  id,
13564                         cls : 'control-label',
13565                         cn : [
13566                             {
13567                                 tag : 'span',
13568                                 html : this.fieldLabel
13569                             },
13570                             indicator
13571                         ]
13572                     },
13573                     {
13574                         cls : "", 
13575                         cn: [
13576                             combobox
13577                         ]
13578                     }
13579
13580                 ];
13581                 
13582                 labelCfg = cfg.cn[0];
13583                 contentCfg = cfg.cn[1];
13584             }
13585             
13586             if(this.labelWidth > 12){
13587                 labelCfg.style = "width: " + this.labelWidth + 'px';
13588             }
13589             
13590             if(this.labelWidth < 13 && this.labelmd == 0){
13591                 this.labelmd = this.labelWidth;
13592             }
13593             
13594             if(this.labellg > 0){
13595                 labelCfg.cls += ' col-lg-' + this.labellg;
13596                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13597             }
13598             
13599             if(this.labelmd > 0){
13600                 labelCfg.cls += ' col-md-' + this.labelmd;
13601                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13602             }
13603             
13604             if(this.labelsm > 0){
13605                 labelCfg.cls += ' col-sm-' + this.labelsm;
13606                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13607             }
13608             
13609             if(this.labelxs > 0){
13610                 labelCfg.cls += ' col-xs-' + this.labelxs;
13611                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13612             }
13613             
13614         } else if ( this.fieldLabel.length) {
13615 //                Roo.log(" label");
13616             cfg.cn = [
13617                 indicator,
13618                {
13619                    tag: 'label',
13620                    //cls : 'input-group-addon',
13621                    html : this.fieldLabel
13622
13623                },
13624
13625                combobox
13626
13627             ];
13628             
13629             if(this.indicatorpos == 'right'){
13630                 
13631                 cfg.cn = [
13632                     {
13633                        tag: 'label',
13634                        cn : [
13635                            {
13636                                tag : 'span',
13637                                html : this.fieldLabel
13638                            },
13639                            indicator
13640                        ]
13641
13642                     },
13643                     combobox
13644
13645                 ];
13646
13647             }
13648
13649         } else {
13650             
13651 //                Roo.log(" no label && no align");
13652                 cfg = combobox
13653                      
13654                 
13655         }
13656         
13657         var settings=this;
13658         ['xs','sm','md','lg'].map(function(size){
13659             if (settings[size]) {
13660                 cfg.cls += ' col-' + size + '-' + settings[size];
13661             }
13662         });
13663         
13664         return cfg;
13665         
13666     },
13667     
13668     
13669     
13670     // private
13671     onResize : function(w, h){
13672 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13673 //        if(typeof w == 'number'){
13674 //            var x = w - this.trigger.getWidth();
13675 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13676 //            this.trigger.setStyle('left', x+'px');
13677 //        }
13678     },
13679
13680     // private
13681     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13682
13683     // private
13684     getResizeEl : function(){
13685         return this.inputEl();
13686     },
13687
13688     // private
13689     getPositionEl : function(){
13690         return this.inputEl();
13691     },
13692
13693     // private
13694     alignErrorIcon : function(){
13695         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13696     },
13697
13698     // private
13699     initEvents : function(){
13700         
13701         this.createList();
13702         
13703         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13704         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13705         if(!this.multiple && this.showToggleBtn){
13706             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13707             if(this.hideTrigger){
13708                 this.trigger.setDisplayed(false);
13709             }
13710             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13711         }
13712         
13713         if(this.multiple){
13714             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13715         }
13716         
13717         if(this.removable && !this.editable && !this.tickable){
13718             var close = this.closeTriggerEl();
13719             
13720             if(close){
13721                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13722                 close.on('click', this.removeBtnClick, this, close);
13723             }
13724         }
13725         
13726         //this.trigger.addClassOnOver('x-form-trigger-over');
13727         //this.trigger.addClassOnClick('x-form-trigger-click');
13728         
13729         //if(!this.width){
13730         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13731         //}
13732     },
13733     
13734     closeTriggerEl : function()
13735     {
13736         var close = this.el.select('.roo-combo-removable-btn', true).first();
13737         return close ? close : false;
13738     },
13739     
13740     removeBtnClick : function(e, h, el)
13741     {
13742         e.preventDefault();
13743         
13744         if(this.fireEvent("remove", this) !== false){
13745             this.reset();
13746             this.fireEvent("afterremove", this)
13747         }
13748     },
13749     
13750     createList : function()
13751     {
13752         this.list = Roo.get(document.body).createChild({
13753             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13754             cls: 'typeahead typeahead-long dropdown-menu shadow',
13755             style: 'display:none'
13756         });
13757         
13758         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13759         
13760     },
13761
13762     // private
13763     initTrigger : function(){
13764        
13765     },
13766
13767     // private
13768     onDestroy : function(){
13769         if(this.trigger){
13770             this.trigger.removeAllListeners();
13771           //  this.trigger.remove();
13772         }
13773         //if(this.wrap){
13774         //    this.wrap.remove();
13775         //}
13776         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13777     },
13778
13779     // private
13780     onFocus : function(){
13781         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13782         /*
13783         if(!this.mimicing){
13784             this.wrap.addClass('x-trigger-wrap-focus');
13785             this.mimicing = true;
13786             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13787             if(this.monitorTab){
13788                 this.el.on("keydown", this.checkTab, this);
13789             }
13790         }
13791         */
13792     },
13793
13794     // private
13795     checkTab : function(e){
13796         if(e.getKey() == e.TAB){
13797             this.triggerBlur();
13798         }
13799     },
13800
13801     // private
13802     onBlur : function(){
13803         // do nothing
13804     },
13805
13806     // private
13807     mimicBlur : function(e, t){
13808         /*
13809         if(!this.wrap.contains(t) && this.validateBlur()){
13810             this.triggerBlur();
13811         }
13812         */
13813     },
13814
13815     // private
13816     triggerBlur : function(){
13817         this.mimicing = false;
13818         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13819         if(this.monitorTab){
13820             this.el.un("keydown", this.checkTab, this);
13821         }
13822         //this.wrap.removeClass('x-trigger-wrap-focus');
13823         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13824     },
13825
13826     // private
13827     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13828     validateBlur : function(e, t){
13829         return true;
13830     },
13831
13832     // private
13833     onDisable : function(){
13834         this.inputEl().dom.disabled = true;
13835         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13836         //if(this.wrap){
13837         //    this.wrap.addClass('x-item-disabled');
13838         //}
13839     },
13840
13841     // private
13842     onEnable : function(){
13843         this.inputEl().dom.disabled = false;
13844         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13845         //if(this.wrap){
13846         //    this.el.removeClass('x-item-disabled');
13847         //}
13848     },
13849
13850     // private
13851     onShow : function(){
13852         var ae = this.getActionEl();
13853         
13854         if(ae){
13855             ae.dom.style.display = '';
13856             ae.dom.style.visibility = 'visible';
13857         }
13858     },
13859
13860     // private
13861     
13862     onHide : function(){
13863         var ae = this.getActionEl();
13864         ae.dom.style.display = 'none';
13865     },
13866
13867     /**
13868      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13869      * by an implementing function.
13870      * @method
13871      * @param {EventObject} e
13872      */
13873     onTriggerClick : Roo.emptyFn
13874 });
13875  
13876 /*
13877 * Licence: LGPL
13878 */
13879
13880 /**
13881  * @class Roo.bootstrap.CardUploader
13882  * @extends Roo.bootstrap.Button
13883  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13884  * @cfg {Number} errorTimeout default 3000
13885  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13886  * @cfg {Array}  html The button text.
13887
13888  *
13889  * @constructor
13890  * Create a new CardUploader
13891  * @param {Object} config The config object
13892  */
13893
13894 Roo.bootstrap.CardUploader = function(config){
13895     
13896  
13897     
13898     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13899     
13900     
13901     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13902         return r.data.id
13903      });
13904     
13905      this.addEvents({
13906          // raw events
13907         /**
13908          * @event preview
13909          * When a image is clicked on - and needs to display a slideshow or similar..
13910          * @param {Roo.bootstrap.Card} this
13911          * @param {Object} The image information data 
13912          *
13913          */
13914         'preview' : true,
13915          /**
13916          * @event download
13917          * When a the download link is clicked
13918          * @param {Roo.bootstrap.Card} this
13919          * @param {Object} The image information data  contains 
13920          */
13921         'download' : true
13922         
13923     });
13924 };
13925  
13926 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13927     
13928      
13929     errorTimeout : 3000,
13930      
13931     images : false,
13932    
13933     fileCollection : false,
13934     allowBlank : true,
13935     
13936     getAutoCreate : function()
13937     {
13938         
13939         var cfg =  {
13940             cls :'form-group' ,
13941             cn : [
13942                
13943                 {
13944                     tag: 'label',
13945                    //cls : 'input-group-addon',
13946                     html : this.fieldLabel
13947
13948                 },
13949
13950                 {
13951                     tag: 'input',
13952                     type : 'hidden',
13953                     name : this.name,
13954                     value : this.value,
13955                     cls : 'd-none  form-control'
13956                 },
13957                 
13958                 {
13959                     tag: 'input',
13960                     multiple : 'multiple',
13961                     type : 'file',
13962                     cls : 'd-none  roo-card-upload-selector'
13963                 },
13964                 
13965                 {
13966                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13967                 },
13968                 {
13969                     cls : 'card-columns roo-card-uploader-container'
13970                 }
13971
13972             ]
13973         };
13974            
13975          
13976         return cfg;
13977     },
13978     
13979     getChildContainer : function() /// what children are added to.
13980     {
13981         return this.containerEl;
13982     },
13983    
13984     getButtonContainer : function() /// what children are added to.
13985     {
13986         return this.el.select(".roo-card-uploader-button-container").first();
13987     },
13988    
13989     initEvents : function()
13990     {
13991         
13992         Roo.bootstrap.Input.prototype.initEvents.call(this);
13993         
13994         var t = this;
13995         this.addxtype({
13996             xns: Roo.bootstrap,
13997
13998             xtype : 'Button',
13999             container_method : 'getButtonContainer' ,            
14000             html :  this.html, // fix changable?
14001             cls : 'w-100 ',
14002             listeners : {
14003                 'click' : function(btn, e) {
14004                     t.onClick(e);
14005                 }
14006             }
14007         });
14008         
14009         
14010         
14011         
14012         this.urlAPI = (window.createObjectURL && window) || 
14013                                 (window.URL && URL.revokeObjectURL && URL) || 
14014                                 (window.webkitURL && webkitURL);
14015                         
14016          
14017          
14018          
14019         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14020         
14021         this.selectorEl.on('change', this.onFileSelected, this);
14022         if (this.images) {
14023             var t = this;
14024             this.images.forEach(function(img) {
14025                 t.addCard(img)
14026             });
14027             this.images = false;
14028         }
14029         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14030          
14031        
14032     },
14033     
14034    
14035     onClick : function(e)
14036     {
14037         e.preventDefault();
14038          
14039         this.selectorEl.dom.click();
14040          
14041     },
14042     
14043     onFileSelected : function(e)
14044     {
14045         e.preventDefault();
14046         
14047         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14048             return;
14049         }
14050         
14051         Roo.each(this.selectorEl.dom.files, function(file){    
14052             this.addFile(file);
14053         }, this);
14054          
14055     },
14056     
14057       
14058     
14059       
14060     
14061     addFile : function(file)
14062     {
14063            
14064         if(typeof(file) === 'string'){
14065             throw "Add file by name?"; // should not happen
14066             return;
14067         }
14068         
14069         if(!file || !this.urlAPI){
14070             return;
14071         }
14072         
14073         // file;
14074         // file.type;
14075         
14076         var _this = this;
14077         
14078         
14079         var url = _this.urlAPI.createObjectURL( file);
14080            
14081         this.addCard({
14082             id : Roo.bootstrap.CardUploader.ID--,
14083             is_uploaded : false,
14084             src : url,
14085             srcfile : file,
14086             title : file.name,
14087             mimetype : file.type,
14088             preview : false,
14089             is_deleted : 0
14090         });
14091         
14092     },
14093     
14094     /**
14095      * addCard - add an Attachment to the uploader
14096      * @param data - the data about the image to upload
14097      *
14098      * {
14099           id : 123
14100           title : "Title of file",
14101           is_uploaded : false,
14102           src : "http://.....",
14103           srcfile : { the File upload object },
14104           mimetype : file.type,
14105           preview : false,
14106           is_deleted : 0
14107           .. any other data...
14108         }
14109      *
14110      * 
14111     */
14112     
14113     addCard : function (data)
14114     {
14115         // hidden input element?
14116         // if the file is not an image...
14117         //then we need to use something other that and header_image
14118         var t = this;
14119         //   remove.....
14120         var footer = [
14121             {
14122                 xns : Roo.bootstrap,
14123                 xtype : 'CardFooter',
14124                  items: [
14125                     {
14126                         xns : Roo.bootstrap,
14127                         xtype : 'Element',
14128                         cls : 'd-flex',
14129                         items : [
14130                             
14131                             {
14132                                 xns : Roo.bootstrap,
14133                                 xtype : 'Button',
14134                                 html : String.format("<small>{0}</small>", data.title),
14135                                 cls : 'col-10 text-left',
14136                                 size: 'sm',
14137                                 weight: 'link',
14138                                 fa : 'download',
14139                                 listeners : {
14140                                     click : function() {
14141                                      
14142                                         t.fireEvent( "download", t, data );
14143                                     }
14144                                 }
14145                             },
14146                           
14147                             {
14148                                 xns : Roo.bootstrap,
14149                                 xtype : 'Button',
14150                                 style: 'max-height: 28px; ',
14151                                 size : 'sm',
14152                                 weight: 'danger',
14153                                 cls : 'col-2',
14154                                 fa : 'times',
14155                                 listeners : {
14156                                     click : function() {
14157                                         t.removeCard(data.id)
14158                                     }
14159                                 }
14160                             }
14161                         ]
14162                     }
14163                     
14164                 ] 
14165             }
14166             
14167         ];
14168         
14169         var cn = this.addxtype(
14170             {
14171                  
14172                 xns : Roo.bootstrap,
14173                 xtype : 'Card',
14174                 closeable : true,
14175                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14176                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14177                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14178                 data : data,
14179                 html : false,
14180                  
14181                 items : footer,
14182                 initEvents : function() {
14183                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14184                     var card = this;
14185                     this.imgEl = this.el.select('.card-img-top').first();
14186                     if (this.imgEl) {
14187                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14188                         this.imgEl.set({ 'pointer' : 'cursor' });
14189                                   
14190                     }
14191                     this.getCardFooter().addClass('p-1');
14192                     
14193                   
14194                 }
14195                 
14196             }
14197         );
14198         // dont' really need ot update items.
14199         // this.items.push(cn);
14200         this.fileCollection.add(cn);
14201         
14202         if (!data.srcfile) {
14203             this.updateInput();
14204             return;
14205         }
14206             
14207         var _t = this;
14208         var reader = new FileReader();
14209         reader.addEventListener("load", function() {  
14210             data.srcdata =  reader.result;
14211             _t.updateInput();
14212         });
14213         reader.readAsDataURL(data.srcfile);
14214         
14215         
14216         
14217     },
14218     removeCard : function(id)
14219     {
14220         
14221         var card  = this.fileCollection.get(id);
14222         card.data.is_deleted = 1;
14223         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14224         //this.fileCollection.remove(card);
14225         //this.items = this.items.filter(function(e) { return e != card });
14226         // dont' really need ot update items.
14227         card.el.dom.parentNode.removeChild(card.el.dom);
14228         this.updateInput();
14229
14230         
14231     },
14232     reset: function()
14233     {
14234         this.fileCollection.each(function(card) {
14235             if (card.el.dom && card.el.dom.parentNode) {
14236                 card.el.dom.parentNode.removeChild(card.el.dom);
14237             }
14238         });
14239         this.fileCollection.clear();
14240         this.updateInput();
14241     },
14242     
14243     updateInput : function()
14244     {
14245          var data = [];
14246         this.fileCollection.each(function(e) {
14247             data.push(e.data);
14248             
14249         });
14250         this.inputEl().dom.value = JSON.stringify(data);
14251         
14252         
14253         
14254     }
14255     
14256     
14257 });
14258
14259
14260 Roo.bootstrap.CardUploader.ID = -1;/*
14261  * Based on:
14262  * Ext JS Library 1.1.1
14263  * Copyright(c) 2006-2007, Ext JS, LLC.
14264  *
14265  * Originally Released Under LGPL - original licence link has changed is not relivant.
14266  *
14267  * Fork - LGPL
14268  * <script type="text/javascript">
14269  */
14270
14271
14272 /**
14273  * @class Roo.data.SortTypes
14274  * @singleton
14275  * Defines the default sorting (casting?) comparison functions used when sorting data.
14276  */
14277 Roo.data.SortTypes = {
14278     /**
14279      * Default sort that does nothing
14280      * @param {Mixed} s The value being converted
14281      * @return {Mixed} The comparison value
14282      */
14283     none : function(s){
14284         return s;
14285     },
14286     
14287     /**
14288      * The regular expression used to strip tags
14289      * @type {RegExp}
14290      * @property
14291      */
14292     stripTagsRE : /<\/?[^>]+>/gi,
14293     
14294     /**
14295      * Strips all HTML tags to sort on text only
14296      * @param {Mixed} s The value being converted
14297      * @return {String} The comparison value
14298      */
14299     asText : function(s){
14300         return String(s).replace(this.stripTagsRE, "");
14301     },
14302     
14303     /**
14304      * Strips all HTML tags to sort on text only - Case insensitive
14305      * @param {Mixed} s The value being converted
14306      * @return {String} The comparison value
14307      */
14308     asUCText : function(s){
14309         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14310     },
14311     
14312     /**
14313      * Case insensitive string
14314      * @param {Mixed} s The value being converted
14315      * @return {String} The comparison value
14316      */
14317     asUCString : function(s) {
14318         return String(s).toUpperCase();
14319     },
14320     
14321     /**
14322      * Date sorting
14323      * @param {Mixed} s The value being converted
14324      * @return {Number} The comparison value
14325      */
14326     asDate : function(s) {
14327         if(!s){
14328             return 0;
14329         }
14330         if(s instanceof Date){
14331             return s.getTime();
14332         }
14333         return Date.parse(String(s));
14334     },
14335     
14336     /**
14337      * Float sorting
14338      * @param {Mixed} s The value being converted
14339      * @return {Float} The comparison value
14340      */
14341     asFloat : function(s) {
14342         var val = parseFloat(String(s).replace(/,/g, ""));
14343         if(isNaN(val)) {
14344             val = 0;
14345         }
14346         return val;
14347     },
14348     
14349     /**
14350      * Integer sorting
14351      * @param {Mixed} s The value being converted
14352      * @return {Number} The comparison value
14353      */
14354     asInt : function(s) {
14355         var val = parseInt(String(s).replace(/,/g, ""));
14356         if(isNaN(val)) {
14357             val = 0;
14358         }
14359         return val;
14360     }
14361 };/*
14362  * Based on:
14363  * Ext JS Library 1.1.1
14364  * Copyright(c) 2006-2007, Ext JS, LLC.
14365  *
14366  * Originally Released Under LGPL - original licence link has changed is not relivant.
14367  *
14368  * Fork - LGPL
14369  * <script type="text/javascript">
14370  */
14371
14372 /**
14373 * @class Roo.data.Record
14374  * Instances of this class encapsulate both record <em>definition</em> information, and record
14375  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14376  * to access Records cached in an {@link Roo.data.Store} object.<br>
14377  * <p>
14378  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14379  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14380  * objects.<br>
14381  * <p>
14382  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14383  * @constructor
14384  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14385  * {@link #create}. The parameters are the same.
14386  * @param {Array} data An associative Array of data values keyed by the field name.
14387  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14388  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14389  * not specified an integer id is generated.
14390  */
14391 Roo.data.Record = function(data, id){
14392     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14393     this.data = data;
14394 };
14395
14396 /**
14397  * Generate a constructor for a specific record layout.
14398  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14399  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14400  * Each field definition object may contain the following properties: <ul>
14401  * <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,
14402  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14403  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14404  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14405  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14406  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14407  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14408  * this may be omitted.</p></li>
14409  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14410  * <ul><li>auto (Default, implies no conversion)</li>
14411  * <li>string</li>
14412  * <li>int</li>
14413  * <li>float</li>
14414  * <li>boolean</li>
14415  * <li>date</li></ul></p></li>
14416  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14417  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14418  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14419  * by the Reader into an object that will be stored in the Record. It is passed the
14420  * following parameters:<ul>
14421  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14422  * </ul></p></li>
14423  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14424  * </ul>
14425  * <br>usage:<br><pre><code>
14426 var TopicRecord = Roo.data.Record.create(
14427     {name: 'title', mapping: 'topic_title'},
14428     {name: 'author', mapping: 'username'},
14429     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14430     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14431     {name: 'lastPoster', mapping: 'user2'},
14432     {name: 'excerpt', mapping: 'post_text'}
14433 );
14434
14435 var myNewRecord = new TopicRecord({
14436     title: 'Do my job please',
14437     author: 'noobie',
14438     totalPosts: 1,
14439     lastPost: new Date(),
14440     lastPoster: 'Animal',
14441     excerpt: 'No way dude!'
14442 });
14443 myStore.add(myNewRecord);
14444 </code></pre>
14445  * @method create
14446  * @static
14447  */
14448 Roo.data.Record.create = function(o){
14449     var f = function(){
14450         f.superclass.constructor.apply(this, arguments);
14451     };
14452     Roo.extend(f, Roo.data.Record);
14453     var p = f.prototype;
14454     p.fields = new Roo.util.MixedCollection(false, function(field){
14455         return field.name;
14456     });
14457     for(var i = 0, len = o.length; i < len; i++){
14458         p.fields.add(new Roo.data.Field(o[i]));
14459     }
14460     f.getField = function(name){
14461         return p.fields.get(name);  
14462     };
14463     return f;
14464 };
14465
14466 Roo.data.Record.AUTO_ID = 1000;
14467 Roo.data.Record.EDIT = 'edit';
14468 Roo.data.Record.REJECT = 'reject';
14469 Roo.data.Record.COMMIT = 'commit';
14470
14471 Roo.data.Record.prototype = {
14472     /**
14473      * Readonly flag - true if this record has been modified.
14474      * @type Boolean
14475      */
14476     dirty : false,
14477     editing : false,
14478     error: null,
14479     modified: null,
14480
14481     // private
14482     join : function(store){
14483         this.store = store;
14484     },
14485
14486     /**
14487      * Set the named field to the specified value.
14488      * @param {String} name The name of the field to set.
14489      * @param {Object} value The value to set the field to.
14490      */
14491     set : function(name, value){
14492         if(this.data[name] == value){
14493             return;
14494         }
14495         this.dirty = true;
14496         if(!this.modified){
14497             this.modified = {};
14498         }
14499         if(typeof this.modified[name] == 'undefined'){
14500             this.modified[name] = this.data[name];
14501         }
14502         this.data[name] = value;
14503         if(!this.editing && this.store){
14504             this.store.afterEdit(this);
14505         }       
14506     },
14507
14508     /**
14509      * Get the value of the named field.
14510      * @param {String} name The name of the field to get the value of.
14511      * @return {Object} The value of the field.
14512      */
14513     get : function(name){
14514         return this.data[name]; 
14515     },
14516
14517     // private
14518     beginEdit : function(){
14519         this.editing = true;
14520         this.modified = {}; 
14521     },
14522
14523     // private
14524     cancelEdit : function(){
14525         this.editing = false;
14526         delete this.modified;
14527     },
14528
14529     // private
14530     endEdit : function(){
14531         this.editing = false;
14532         if(this.dirty && this.store){
14533             this.store.afterEdit(this);
14534         }
14535     },
14536
14537     /**
14538      * Usually called by the {@link Roo.data.Store} which owns the Record.
14539      * Rejects all changes made to the Record since either creation, or the last commit operation.
14540      * Modified fields are reverted to their original values.
14541      * <p>
14542      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14543      * of reject operations.
14544      */
14545     reject : function(){
14546         var m = this.modified;
14547         for(var n in m){
14548             if(typeof m[n] != "function"){
14549                 this.data[n] = m[n];
14550             }
14551         }
14552         this.dirty = false;
14553         delete this.modified;
14554         this.editing = false;
14555         if(this.store){
14556             this.store.afterReject(this);
14557         }
14558     },
14559
14560     /**
14561      * Usually called by the {@link Roo.data.Store} which owns the Record.
14562      * Commits all changes made to the Record since either creation, or the last commit operation.
14563      * <p>
14564      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14565      * of commit operations.
14566      */
14567     commit : function(){
14568         this.dirty = false;
14569         delete this.modified;
14570         this.editing = false;
14571         if(this.store){
14572             this.store.afterCommit(this);
14573         }
14574     },
14575
14576     // private
14577     hasError : function(){
14578         return this.error != null;
14579     },
14580
14581     // private
14582     clearError : function(){
14583         this.error = null;
14584     },
14585
14586     /**
14587      * Creates a copy of this record.
14588      * @param {String} id (optional) A new record id if you don't want to use this record's id
14589      * @return {Record}
14590      */
14591     copy : function(newId) {
14592         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14593     }
14594 };/*
14595  * Based on:
14596  * Ext JS Library 1.1.1
14597  * Copyright(c) 2006-2007, Ext JS, LLC.
14598  *
14599  * Originally Released Under LGPL - original licence link has changed is not relivant.
14600  *
14601  * Fork - LGPL
14602  * <script type="text/javascript">
14603  */
14604
14605
14606
14607 /**
14608  * @class Roo.data.Store
14609  * @extends Roo.util.Observable
14610  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14611  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14612  * <p>
14613  * 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
14614  * has no knowledge of the format of the data returned by the Proxy.<br>
14615  * <p>
14616  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14617  * instances from the data object. These records are cached and made available through accessor functions.
14618  * @constructor
14619  * Creates a new Store.
14620  * @param {Object} config A config object containing the objects needed for the Store to access data,
14621  * and read the data into Records.
14622  */
14623 Roo.data.Store = function(config){
14624     this.data = new Roo.util.MixedCollection(false);
14625     this.data.getKey = function(o){
14626         return o.id;
14627     };
14628     this.baseParams = {};
14629     // private
14630     this.paramNames = {
14631         "start" : "start",
14632         "limit" : "limit",
14633         "sort" : "sort",
14634         "dir" : "dir",
14635         "multisort" : "_multisort"
14636     };
14637
14638     if(config && config.data){
14639         this.inlineData = config.data;
14640         delete config.data;
14641     }
14642
14643     Roo.apply(this, config);
14644     
14645     if(this.reader){ // reader passed
14646         this.reader = Roo.factory(this.reader, Roo.data);
14647         this.reader.xmodule = this.xmodule || false;
14648         if(!this.recordType){
14649             this.recordType = this.reader.recordType;
14650         }
14651         if(this.reader.onMetaChange){
14652             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14653         }
14654     }
14655
14656     if(this.recordType){
14657         this.fields = this.recordType.prototype.fields;
14658     }
14659     this.modified = [];
14660
14661     this.addEvents({
14662         /**
14663          * @event datachanged
14664          * Fires when the data cache has changed, and a widget which is using this Store
14665          * as a Record cache should refresh its view.
14666          * @param {Store} this
14667          */
14668         datachanged : true,
14669         /**
14670          * @event metachange
14671          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14672          * @param {Store} this
14673          * @param {Object} meta The JSON metadata
14674          */
14675         metachange : true,
14676         /**
14677          * @event add
14678          * Fires when Records have been added to the Store
14679          * @param {Store} this
14680          * @param {Roo.data.Record[]} records The array of Records added
14681          * @param {Number} index The index at which the record(s) were added
14682          */
14683         add : true,
14684         /**
14685          * @event remove
14686          * Fires when a Record has been removed from the Store
14687          * @param {Store} this
14688          * @param {Roo.data.Record} record The Record that was removed
14689          * @param {Number} index The index at which the record was removed
14690          */
14691         remove : true,
14692         /**
14693          * @event update
14694          * Fires when a Record has been updated
14695          * @param {Store} this
14696          * @param {Roo.data.Record} record The Record that was updated
14697          * @param {String} operation The update operation being performed.  Value may be one of:
14698          * <pre><code>
14699  Roo.data.Record.EDIT
14700  Roo.data.Record.REJECT
14701  Roo.data.Record.COMMIT
14702          * </code></pre>
14703          */
14704         update : true,
14705         /**
14706          * @event clear
14707          * Fires when the data cache has been cleared.
14708          * @param {Store} this
14709          */
14710         clear : true,
14711         /**
14712          * @event beforeload
14713          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14714          * the load action will be canceled.
14715          * @param {Store} this
14716          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717          */
14718         beforeload : true,
14719         /**
14720          * @event beforeloadadd
14721          * Fires after a new set of Records has been loaded.
14722          * @param {Store} this
14723          * @param {Roo.data.Record[]} records The Records that were loaded
14724          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14725          */
14726         beforeloadadd : true,
14727         /**
14728          * @event load
14729          * Fires after a new set of Records has been loaded, before they are added to the store.
14730          * @param {Store} this
14731          * @param {Roo.data.Record[]} records The Records that were loaded
14732          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14733          * @params {Object} return from reader
14734          */
14735         load : true,
14736         /**
14737          * @event loadexception
14738          * Fires if an exception occurs in the Proxy during loading.
14739          * Called with the signature of the Proxy's "loadexception" event.
14740          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14741          * 
14742          * @param {Proxy} 
14743          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14744          * @param {Object} load options 
14745          * @param {Object} jsonData from your request (normally this contains the Exception)
14746          */
14747         loadexception : true
14748     });
14749     
14750     if(this.proxy){
14751         this.proxy = Roo.factory(this.proxy, Roo.data);
14752         this.proxy.xmodule = this.xmodule || false;
14753         this.relayEvents(this.proxy,  ["loadexception"]);
14754     }
14755     this.sortToggle = {};
14756     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14757
14758     Roo.data.Store.superclass.constructor.call(this);
14759
14760     if(this.inlineData){
14761         this.loadData(this.inlineData);
14762         delete this.inlineData;
14763     }
14764 };
14765
14766 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14767      /**
14768     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14769     * without a remote query - used by combo/forms at present.
14770     */
14771     
14772     /**
14773     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14774     */
14775     /**
14776     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14777     */
14778     /**
14779     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14780     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14781     */
14782     /**
14783     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14784     * on any HTTP request
14785     */
14786     /**
14787     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14788     */
14789     /**
14790     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14791     */
14792     multiSort: false,
14793     /**
14794     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14795     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14796     */
14797     remoteSort : false,
14798
14799     /**
14800     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14801      * loaded or when a record is removed. (defaults to false).
14802     */
14803     pruneModifiedRecords : false,
14804
14805     // private
14806     lastOptions : null,
14807
14808     /**
14809      * Add Records to the Store and fires the add event.
14810      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14811      */
14812     add : function(records){
14813         records = [].concat(records);
14814         for(var i = 0, len = records.length; i < len; i++){
14815             records[i].join(this);
14816         }
14817         var index = this.data.length;
14818         this.data.addAll(records);
14819         this.fireEvent("add", this, records, index);
14820     },
14821
14822     /**
14823      * Remove a Record from the Store and fires the remove event.
14824      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14825      */
14826     remove : function(record){
14827         var index = this.data.indexOf(record);
14828         this.data.removeAt(index);
14829  
14830         if(this.pruneModifiedRecords){
14831             this.modified.remove(record);
14832         }
14833         this.fireEvent("remove", this, record, index);
14834     },
14835
14836     /**
14837      * Remove all Records from the Store and fires the clear event.
14838      */
14839     removeAll : function(){
14840         this.data.clear();
14841         if(this.pruneModifiedRecords){
14842             this.modified = [];
14843         }
14844         this.fireEvent("clear", this);
14845     },
14846
14847     /**
14848      * Inserts Records to the Store at the given index and fires the add event.
14849      * @param {Number} index The start index at which to insert the passed Records.
14850      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14851      */
14852     insert : function(index, records){
14853         records = [].concat(records);
14854         for(var i = 0, len = records.length; i < len; i++){
14855             this.data.insert(index, records[i]);
14856             records[i].join(this);
14857         }
14858         this.fireEvent("add", this, records, index);
14859     },
14860
14861     /**
14862      * Get the index within the cache of the passed Record.
14863      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14864      * @return {Number} The index of the passed Record. Returns -1 if not found.
14865      */
14866     indexOf : function(record){
14867         return this.data.indexOf(record);
14868     },
14869
14870     /**
14871      * Get the index within the cache of the Record with the passed id.
14872      * @param {String} id The id of the Record to find.
14873      * @return {Number} The index of the Record. Returns -1 if not found.
14874      */
14875     indexOfId : function(id){
14876         return this.data.indexOfKey(id);
14877     },
14878
14879     /**
14880      * Get the Record with the specified id.
14881      * @param {String} id The id of the Record to find.
14882      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14883      */
14884     getById : function(id){
14885         return this.data.key(id);
14886     },
14887
14888     /**
14889      * Get the Record at the specified index.
14890      * @param {Number} index The index of the Record to find.
14891      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14892      */
14893     getAt : function(index){
14894         return this.data.itemAt(index);
14895     },
14896
14897     /**
14898      * Returns a range of Records between specified indices.
14899      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14900      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14901      * @return {Roo.data.Record[]} An array of Records
14902      */
14903     getRange : function(start, end){
14904         return this.data.getRange(start, end);
14905     },
14906
14907     // private
14908     storeOptions : function(o){
14909         o = Roo.apply({}, o);
14910         delete o.callback;
14911         delete o.scope;
14912         this.lastOptions = o;
14913     },
14914
14915     /**
14916      * Loads the Record cache from the configured Proxy using the configured Reader.
14917      * <p>
14918      * If using remote paging, then the first load call must specify the <em>start</em>
14919      * and <em>limit</em> properties in the options.params property to establish the initial
14920      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14921      * <p>
14922      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14923      * and this call will return before the new data has been loaded. Perform any post-processing
14924      * in a callback function, or in a "load" event handler.</strong>
14925      * <p>
14926      * @param {Object} options An object containing properties which control loading options:<ul>
14927      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14928      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14929      * passed the following arguments:<ul>
14930      * <li>r : Roo.data.Record[]</li>
14931      * <li>options: Options object from the load call</li>
14932      * <li>success: Boolean success indicator</li></ul></li>
14933      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14934      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14935      * </ul>
14936      */
14937     load : function(options){
14938         options = options || {};
14939         if(this.fireEvent("beforeload", this, options) !== false){
14940             this.storeOptions(options);
14941             var p = Roo.apply(options.params || {}, this.baseParams);
14942             // if meta was not loaded from remote source.. try requesting it.
14943             if (!this.reader.metaFromRemote) {
14944                 p._requestMeta = 1;
14945             }
14946             if(this.sortInfo && this.remoteSort){
14947                 var pn = this.paramNames;
14948                 p[pn["sort"]] = this.sortInfo.field;
14949                 p[pn["dir"]] = this.sortInfo.direction;
14950             }
14951             if (this.multiSort) {
14952                 var pn = this.paramNames;
14953                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14954             }
14955             
14956             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14957         }
14958     },
14959
14960     /**
14961      * Reloads the Record cache from the configured Proxy using the configured Reader and
14962      * the options from the last load operation performed.
14963      * @param {Object} options (optional) An object containing properties which may override the options
14964      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14965      * the most recently used options are reused).
14966      */
14967     reload : function(options){
14968         this.load(Roo.applyIf(options||{}, this.lastOptions));
14969     },
14970
14971     // private
14972     // Called as a callback by the Reader during a load operation.
14973     loadRecords : function(o, options, success){
14974         if(!o || success === false){
14975             if(success !== false){
14976                 this.fireEvent("load", this, [], options, o);
14977             }
14978             if(options.callback){
14979                 options.callback.call(options.scope || this, [], options, false);
14980             }
14981             return;
14982         }
14983         // if data returned failure - throw an exception.
14984         if (o.success === false) {
14985             // show a message if no listener is registered.
14986             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14987                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14988             }
14989             // loadmask wil be hooked into this..
14990             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14991             return;
14992         }
14993         var r = o.records, t = o.totalRecords || r.length;
14994         
14995         this.fireEvent("beforeloadadd", this, r, options, o);
14996         
14997         if(!options || options.add !== true){
14998             if(this.pruneModifiedRecords){
14999                 this.modified = [];
15000             }
15001             for(var i = 0, len = r.length; i < len; i++){
15002                 r[i].join(this);
15003             }
15004             if(this.snapshot){
15005                 this.data = this.snapshot;
15006                 delete this.snapshot;
15007             }
15008             this.data.clear();
15009             this.data.addAll(r);
15010             this.totalLength = t;
15011             this.applySort();
15012             this.fireEvent("datachanged", this);
15013         }else{
15014             this.totalLength = Math.max(t, this.data.length+r.length);
15015             this.add(r);
15016         }
15017         
15018         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15019                 
15020             var e = new Roo.data.Record({});
15021
15022             e.set(this.parent.displayField, this.parent.emptyTitle);
15023             e.set(this.parent.valueField, '');
15024
15025             this.insert(0, e);
15026         }
15027             
15028         this.fireEvent("load", this, r, options, o);
15029         if(options.callback){
15030             options.callback.call(options.scope || this, r, options, true);
15031         }
15032     },
15033
15034
15035     /**
15036      * Loads data from a passed data block. A Reader which understands the format of the data
15037      * must have been configured in the constructor.
15038      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15039      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15040      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15041      */
15042     loadData : function(o, append){
15043         var r = this.reader.readRecords(o);
15044         this.loadRecords(r, {add: append}, true);
15045     },
15046     
15047      /**
15048      * using 'cn' the nested child reader read the child array into it's child stores.
15049      * @param {Object} rec The record with a 'children array
15050      */
15051     loadDataFromChildren : function(rec)
15052     {
15053         this.loadData(this.reader.toLoadData(rec));
15054     },
15055     
15056
15057     /**
15058      * Gets the number of cached records.
15059      * <p>
15060      * <em>If using paging, this may not be the total size of the dataset. If the data object
15061      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15062      * the data set size</em>
15063      */
15064     getCount : function(){
15065         return this.data.length || 0;
15066     },
15067
15068     /**
15069      * Gets the total number of records in the dataset as returned by the server.
15070      * <p>
15071      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15072      * the dataset size</em>
15073      */
15074     getTotalCount : function(){
15075         return this.totalLength || 0;
15076     },
15077
15078     /**
15079      * Returns the sort state of the Store as an object with two properties:
15080      * <pre><code>
15081  field {String} The name of the field by which the Records are sorted
15082  direction {String} The sort order, "ASC" or "DESC"
15083      * </code></pre>
15084      */
15085     getSortState : function(){
15086         return this.sortInfo;
15087     },
15088
15089     // private
15090     applySort : function(){
15091         if(this.sortInfo && !this.remoteSort){
15092             var s = this.sortInfo, f = s.field;
15093             var st = this.fields.get(f).sortType;
15094             var fn = function(r1, r2){
15095                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15096                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15097             };
15098             this.data.sort(s.direction, fn);
15099             if(this.snapshot && this.snapshot != this.data){
15100                 this.snapshot.sort(s.direction, fn);
15101             }
15102         }
15103     },
15104
15105     /**
15106      * Sets the default sort column and order to be used by the next load operation.
15107      * @param {String} fieldName The name of the field to sort by.
15108      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15109      */
15110     setDefaultSort : function(field, dir){
15111         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15112     },
15113
15114     /**
15115      * Sort the Records.
15116      * If remote sorting is used, the sort is performed on the server, and the cache is
15117      * reloaded. If local sorting is used, the cache is sorted internally.
15118      * @param {String} fieldName The name of the field to sort by.
15119      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15120      */
15121     sort : function(fieldName, dir){
15122         var f = this.fields.get(fieldName);
15123         if(!dir){
15124             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15125             
15126             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15127                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15128             }else{
15129                 dir = f.sortDir;
15130             }
15131         }
15132         this.sortToggle[f.name] = dir;
15133         this.sortInfo = {field: f.name, direction: dir};
15134         if(!this.remoteSort){
15135             this.applySort();
15136             this.fireEvent("datachanged", this);
15137         }else{
15138             this.load(this.lastOptions);
15139         }
15140     },
15141
15142     /**
15143      * Calls the specified function for each of the Records in the cache.
15144      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15145      * Returning <em>false</em> aborts and exits the iteration.
15146      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15147      */
15148     each : function(fn, scope){
15149         this.data.each(fn, scope);
15150     },
15151
15152     /**
15153      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15154      * (e.g., during paging).
15155      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15156      */
15157     getModifiedRecords : function(){
15158         return this.modified;
15159     },
15160
15161     // private
15162     createFilterFn : function(property, value, anyMatch){
15163         if(!value.exec){ // not a regex
15164             value = String(value);
15165             if(value.length == 0){
15166                 return false;
15167             }
15168             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15169         }
15170         return function(r){
15171             return value.test(r.data[property]);
15172         };
15173     },
15174
15175     /**
15176      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15177      * @param {String} property A field on your records
15178      * @param {Number} start The record index to start at (defaults to 0)
15179      * @param {Number} end The last record index to include (defaults to length - 1)
15180      * @return {Number} The sum
15181      */
15182     sum : function(property, start, end){
15183         var rs = this.data.items, v = 0;
15184         start = start || 0;
15185         end = (end || end === 0) ? end : rs.length-1;
15186
15187         for(var i = start; i <= end; i++){
15188             v += (rs[i].data[property] || 0);
15189         }
15190         return v;
15191     },
15192
15193     /**
15194      * Filter the records by a specified property.
15195      * @param {String} field A field on your records
15196      * @param {String/RegExp} value Either a string that the field
15197      * should start with or a RegExp to test against the field
15198      * @param {Boolean} anyMatch True to match any part not just the beginning
15199      */
15200     filter : function(property, value, anyMatch){
15201         var fn = this.createFilterFn(property, value, anyMatch);
15202         return fn ? this.filterBy(fn) : this.clearFilter();
15203     },
15204
15205     /**
15206      * Filter by a function. The specified function will be called with each
15207      * record in this data source. If the function returns true the record is included,
15208      * otherwise it is filtered.
15209      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15210      * @param {Object} scope (optional) The scope of the function (defaults to this)
15211      */
15212     filterBy : function(fn, scope){
15213         this.snapshot = this.snapshot || this.data;
15214         this.data = this.queryBy(fn, scope||this);
15215         this.fireEvent("datachanged", this);
15216     },
15217
15218     /**
15219      * Query the records by a specified property.
15220      * @param {String} field A field on your records
15221      * @param {String/RegExp} value Either a string that the field
15222      * should start with or a RegExp to test against the field
15223      * @param {Boolean} anyMatch True to match any part not just the beginning
15224      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15225      */
15226     query : function(property, value, anyMatch){
15227         var fn = this.createFilterFn(property, value, anyMatch);
15228         return fn ? this.queryBy(fn) : this.data.clone();
15229     },
15230
15231     /**
15232      * Query by a function. The specified function will be called with each
15233      * record in this data source. If the function returns true the record is included
15234      * in the results.
15235      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15236      * @param {Object} scope (optional) The scope of the function (defaults to this)
15237       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15238      **/
15239     queryBy : function(fn, scope){
15240         var data = this.snapshot || this.data;
15241         return data.filterBy(fn, scope||this);
15242     },
15243
15244     /**
15245      * Collects unique values for a particular dataIndex from this store.
15246      * @param {String} dataIndex The property to collect
15247      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15248      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15249      * @return {Array} An array of the unique values
15250      **/
15251     collect : function(dataIndex, allowNull, bypassFilter){
15252         var d = (bypassFilter === true && this.snapshot) ?
15253                 this.snapshot.items : this.data.items;
15254         var v, sv, r = [], l = {};
15255         for(var i = 0, len = d.length; i < len; i++){
15256             v = d[i].data[dataIndex];
15257             sv = String(v);
15258             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15259                 l[sv] = true;
15260                 r[r.length] = v;
15261             }
15262         }
15263         return r;
15264     },
15265
15266     /**
15267      * Revert to a view of the Record cache with no filtering applied.
15268      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15269      */
15270     clearFilter : function(suppressEvent){
15271         if(this.snapshot && this.snapshot != this.data){
15272             this.data = this.snapshot;
15273             delete this.snapshot;
15274             if(suppressEvent !== true){
15275                 this.fireEvent("datachanged", this);
15276             }
15277         }
15278     },
15279
15280     // private
15281     afterEdit : function(record){
15282         if(this.modified.indexOf(record) == -1){
15283             this.modified.push(record);
15284         }
15285         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15286     },
15287     
15288     // private
15289     afterReject : function(record){
15290         this.modified.remove(record);
15291         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15292     },
15293
15294     // private
15295     afterCommit : function(record){
15296         this.modified.remove(record);
15297         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15298     },
15299
15300     /**
15301      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15302      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15303      */
15304     commitChanges : function(){
15305         var m = this.modified.slice(0);
15306         this.modified = [];
15307         for(var i = 0, len = m.length; i < len; i++){
15308             m[i].commit();
15309         }
15310     },
15311
15312     /**
15313      * Cancel outstanding changes on all changed records.
15314      */
15315     rejectChanges : function(){
15316         var m = this.modified.slice(0);
15317         this.modified = [];
15318         for(var i = 0, len = m.length; i < len; i++){
15319             m[i].reject();
15320         }
15321     },
15322
15323     onMetaChange : function(meta, rtype, o){
15324         this.recordType = rtype;
15325         this.fields = rtype.prototype.fields;
15326         delete this.snapshot;
15327         this.sortInfo = meta.sortInfo || this.sortInfo;
15328         this.modified = [];
15329         this.fireEvent('metachange', this, this.reader.meta);
15330     },
15331     
15332     moveIndex : function(data, type)
15333     {
15334         var index = this.indexOf(data);
15335         
15336         var newIndex = index + type;
15337         
15338         this.remove(data);
15339         
15340         this.insert(newIndex, data);
15341         
15342     }
15343 });/*
15344  * Based on:
15345  * Ext JS Library 1.1.1
15346  * Copyright(c) 2006-2007, Ext JS, LLC.
15347  *
15348  * Originally Released Under LGPL - original licence link has changed is not relivant.
15349  *
15350  * Fork - LGPL
15351  * <script type="text/javascript">
15352  */
15353
15354 /**
15355  * @class Roo.data.SimpleStore
15356  * @extends Roo.data.Store
15357  * Small helper class to make creating Stores from Array data easier.
15358  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15359  * @cfg {Array} fields An array of field definition objects, or field name strings.
15360  * @cfg {Object} an existing reader (eg. copied from another store)
15361  * @cfg {Array} data The multi-dimensional array of data
15362  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15363  * @cfg {Roo.data.Reader} reader  [not-required] 
15364  * @constructor
15365  * @param {Object} config
15366  */
15367 Roo.data.SimpleStore = function(config)
15368 {
15369     Roo.data.SimpleStore.superclass.constructor.call(this, {
15370         isLocal : true,
15371         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15372                 id: config.id
15373             },
15374             Roo.data.Record.create(config.fields)
15375         ),
15376         proxy : new Roo.data.MemoryProxy(config.data)
15377     });
15378     this.load();
15379 };
15380 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15381  * Based on:
15382  * Ext JS Library 1.1.1
15383  * Copyright(c) 2006-2007, Ext JS, LLC.
15384  *
15385  * Originally Released Under LGPL - original licence link has changed is not relivant.
15386  *
15387  * Fork - LGPL
15388  * <script type="text/javascript">
15389  */
15390
15391 /**
15392 /**
15393  * @extends Roo.data.Store
15394  * @class Roo.data.JsonStore
15395  * Small helper class to make creating Stores for JSON data easier. <br/>
15396 <pre><code>
15397 var store = new Roo.data.JsonStore({
15398     url: 'get-images.php',
15399     root: 'images',
15400     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15401 });
15402 </code></pre>
15403  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15404  * JsonReader and HttpProxy (unless inline data is provided).</b>
15405  * @cfg {Array} fields An array of field definition objects, or field name strings.
15406  * @constructor
15407  * @param {Object} config
15408  */
15409 Roo.data.JsonStore = function(c){
15410     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15411         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15412         reader: new Roo.data.JsonReader(c, c.fields)
15413     }));
15414 };
15415 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15416  * Based on:
15417  * Ext JS Library 1.1.1
15418  * Copyright(c) 2006-2007, Ext JS, LLC.
15419  *
15420  * Originally Released Under LGPL - original licence link has changed is not relivant.
15421  *
15422  * Fork - LGPL
15423  * <script type="text/javascript">
15424  */
15425
15426  
15427 Roo.data.Field = function(config){
15428     if(typeof config == "string"){
15429         config = {name: config};
15430     }
15431     Roo.apply(this, config);
15432     
15433     if(!this.type){
15434         this.type = "auto";
15435     }
15436     
15437     var st = Roo.data.SortTypes;
15438     // named sortTypes are supported, here we look them up
15439     if(typeof this.sortType == "string"){
15440         this.sortType = st[this.sortType];
15441     }
15442     
15443     // set default sortType for strings and dates
15444     if(!this.sortType){
15445         switch(this.type){
15446             case "string":
15447                 this.sortType = st.asUCString;
15448                 break;
15449             case "date":
15450                 this.sortType = st.asDate;
15451                 break;
15452             default:
15453                 this.sortType = st.none;
15454         }
15455     }
15456
15457     // define once
15458     var stripRe = /[\$,%]/g;
15459
15460     // prebuilt conversion function for this field, instead of
15461     // switching every time we're reading a value
15462     if(!this.convert){
15463         var cv, dateFormat = this.dateFormat;
15464         switch(this.type){
15465             case "":
15466             case "auto":
15467             case undefined:
15468                 cv = function(v){ return v; };
15469                 break;
15470             case "string":
15471                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15472                 break;
15473             case "int":
15474                 cv = function(v){
15475                     return v !== undefined && v !== null && v !== '' ?
15476                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15477                     };
15478                 break;
15479             case "float":
15480                 cv = function(v){
15481                     return v !== undefined && v !== null && v !== '' ?
15482                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15483                     };
15484                 break;
15485             case "bool":
15486             case "boolean":
15487                 cv = function(v){ return v === true || v === "true" || v == 1; };
15488                 break;
15489             case "date":
15490                 cv = function(v){
15491                     if(!v){
15492                         return '';
15493                     }
15494                     if(v instanceof Date){
15495                         return v;
15496                     }
15497                     if(dateFormat){
15498                         if(dateFormat == "timestamp"){
15499                             return new Date(v*1000);
15500                         }
15501                         return Date.parseDate(v, dateFormat);
15502                     }
15503                     var parsed = Date.parse(v);
15504                     return parsed ? new Date(parsed) : null;
15505                 };
15506              break;
15507             
15508         }
15509         this.convert = cv;
15510     }
15511 };
15512
15513 Roo.data.Field.prototype = {
15514     dateFormat: null,
15515     defaultValue: "",
15516     mapping: null,
15517     sortType : null,
15518     sortDir : "ASC"
15519 };/*
15520  * Based on:
15521  * Ext JS Library 1.1.1
15522  * Copyright(c) 2006-2007, Ext JS, LLC.
15523  *
15524  * Originally Released Under LGPL - original licence link has changed is not relivant.
15525  *
15526  * Fork - LGPL
15527  * <script type="text/javascript">
15528  */
15529  
15530 // Base class for reading structured data from a data source.  This class is intended to be
15531 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15532
15533 /**
15534  * @class Roo.data.DataReader
15535  * @abstract
15536  * Base class for reading structured data from a data source.  This class is intended to be
15537  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15538  */
15539
15540 Roo.data.DataReader = function(meta, recordType){
15541     
15542     this.meta = meta;
15543     
15544     this.recordType = recordType instanceof Array ? 
15545         Roo.data.Record.create(recordType) : recordType;
15546 };
15547
15548 Roo.data.DataReader.prototype = {
15549     
15550     
15551     readerType : 'Data',
15552      /**
15553      * Create an empty record
15554      * @param {Object} data (optional) - overlay some values
15555      * @return {Roo.data.Record} record created.
15556      */
15557     newRow :  function(d) {
15558         var da =  {};
15559         this.recordType.prototype.fields.each(function(c) {
15560             switch( c.type) {
15561                 case 'int' : da[c.name] = 0; break;
15562                 case 'date' : da[c.name] = new Date(); break;
15563                 case 'float' : da[c.name] = 0.0; break;
15564                 case 'boolean' : da[c.name] = false; break;
15565                 default : da[c.name] = ""; break;
15566             }
15567             
15568         });
15569         return new this.recordType(Roo.apply(da, d));
15570     }
15571     
15572     
15573 };/*
15574  * Based on:
15575  * Ext JS Library 1.1.1
15576  * Copyright(c) 2006-2007, Ext JS, LLC.
15577  *
15578  * Originally Released Under LGPL - original licence link has changed is not relivant.
15579  *
15580  * Fork - LGPL
15581  * <script type="text/javascript">
15582  */
15583
15584 /**
15585  * @class Roo.data.DataProxy
15586  * @extends Roo.data.Observable
15587  * @abstract
15588  * This class is an abstract base class for implementations which provide retrieval of
15589  * unformatted data objects.<br>
15590  * <p>
15591  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15592  * (of the appropriate type which knows how to parse the data object) to provide a block of
15593  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15594  * <p>
15595  * Custom implementations must implement the load method as described in
15596  * {@link Roo.data.HttpProxy#load}.
15597  */
15598 Roo.data.DataProxy = function(){
15599     this.addEvents({
15600         /**
15601          * @event beforeload
15602          * Fires before a network request is made to retrieve a data object.
15603          * @param {Object} This DataProxy object.
15604          * @param {Object} params The params parameter to the load function.
15605          */
15606         beforeload : true,
15607         /**
15608          * @event load
15609          * Fires before the load method's callback is called.
15610          * @param {Object} This DataProxy object.
15611          * @param {Object} o The data object.
15612          * @param {Object} arg The callback argument object passed to the load function.
15613          */
15614         load : true,
15615         /**
15616          * @event loadexception
15617          * Fires if an Exception occurs during data retrieval.
15618          * @param {Object} This DataProxy object.
15619          * @param {Object} o The data object.
15620          * @param {Object} arg The callback argument object passed to the load function.
15621          * @param {Object} e The Exception.
15622          */
15623         loadexception : true
15624     });
15625     Roo.data.DataProxy.superclass.constructor.call(this);
15626 };
15627
15628 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15629
15630     /**
15631      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15632      */
15633 /*
15634  * Based on:
15635  * Ext JS Library 1.1.1
15636  * Copyright(c) 2006-2007, Ext JS, LLC.
15637  *
15638  * Originally Released Under LGPL - original licence link has changed is not relivant.
15639  *
15640  * Fork - LGPL
15641  * <script type="text/javascript">
15642  */
15643 /**
15644  * @class Roo.data.MemoryProxy
15645  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15646  * to the Reader when its load method is called.
15647  * @constructor
15648  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15649  */
15650 Roo.data.MemoryProxy = function(data){
15651     if (data.data) {
15652         data = data.data;
15653     }
15654     Roo.data.MemoryProxy.superclass.constructor.call(this);
15655     this.data = data;
15656 };
15657
15658 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15659     
15660     /**
15661      * Load data from the requested source (in this case an in-memory
15662      * data object passed to the constructor), read the data object into
15663      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15664      * process that block using the passed callback.
15665      * @param {Object} params This parameter is not used by the MemoryProxy class.
15666      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15667      * object into a block of Roo.data.Records.
15668      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15669      * The function must be passed <ul>
15670      * <li>The Record block object</li>
15671      * <li>The "arg" argument from the load function</li>
15672      * <li>A boolean success indicator</li>
15673      * </ul>
15674      * @param {Object} scope The scope in which to call the callback
15675      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15676      */
15677     load : function(params, reader, callback, scope, arg){
15678         params = params || {};
15679         var result;
15680         try {
15681             result = reader.readRecords(params.data ? params.data :this.data);
15682         }catch(e){
15683             this.fireEvent("loadexception", this, arg, null, e);
15684             callback.call(scope, null, arg, false);
15685             return;
15686         }
15687         callback.call(scope, result, arg, true);
15688     },
15689     
15690     // private
15691     update : function(params, records){
15692         
15693     }
15694 });/*
15695  * Based on:
15696  * Ext JS Library 1.1.1
15697  * Copyright(c) 2006-2007, Ext JS, LLC.
15698  *
15699  * Originally Released Under LGPL - original licence link has changed is not relivant.
15700  *
15701  * Fork - LGPL
15702  * <script type="text/javascript">
15703  */
15704 /**
15705  * @class Roo.data.HttpProxy
15706  * @extends Roo.data.DataProxy
15707  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15708  * configured to reference a certain URL.<br><br>
15709  * <p>
15710  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15711  * from which the running page was served.<br><br>
15712  * <p>
15713  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15714  * <p>
15715  * Be aware that to enable the browser to parse an XML document, the server must set
15716  * the Content-Type header in the HTTP response to "text/xml".
15717  * @constructor
15718  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15719  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15720  * will be used to make the request.
15721  */
15722 Roo.data.HttpProxy = function(conn){
15723     Roo.data.HttpProxy.superclass.constructor.call(this);
15724     // is conn a conn config or a real conn?
15725     this.conn = conn;
15726     this.useAjax = !conn || !conn.events;
15727   
15728 };
15729
15730 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15731     // thse are take from connection...
15732     
15733     /**
15734      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15735      */
15736     /**
15737      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15738      * extra parameters to each request made by this object. (defaults to undefined)
15739      */
15740     /**
15741      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15742      *  to each request made by this object. (defaults to undefined)
15743      */
15744     /**
15745      * @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)
15746      */
15747     /**
15748      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15749      */
15750      /**
15751      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15752      * @type Boolean
15753      */
15754   
15755
15756     /**
15757      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15758      * @type Boolean
15759      */
15760     /**
15761      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15762      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15763      * a finer-grained basis than the DataProxy events.
15764      */
15765     getConnection : function(){
15766         return this.useAjax ? Roo.Ajax : this.conn;
15767     },
15768
15769     /**
15770      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15771      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15772      * process that block using the passed callback.
15773      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15774      * for the request to the remote server.
15775      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15776      * object into a block of Roo.data.Records.
15777      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15778      * The function must be passed <ul>
15779      * <li>The Record block object</li>
15780      * <li>The "arg" argument from the load function</li>
15781      * <li>A boolean success indicator</li>
15782      * </ul>
15783      * @param {Object} scope The scope in which to call the callback
15784      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15785      */
15786     load : function(params, reader, callback, scope, arg){
15787         if(this.fireEvent("beforeload", this, params) !== false){
15788             var  o = {
15789                 params : params || {},
15790                 request: {
15791                     callback : callback,
15792                     scope : scope,
15793                     arg : arg
15794                 },
15795                 reader: reader,
15796                 callback : this.loadResponse,
15797                 scope: this
15798             };
15799             if(this.useAjax){
15800                 Roo.applyIf(o, this.conn);
15801                 if(this.activeRequest){
15802                     Roo.Ajax.abort(this.activeRequest);
15803                 }
15804                 this.activeRequest = Roo.Ajax.request(o);
15805             }else{
15806                 this.conn.request(o);
15807             }
15808         }else{
15809             callback.call(scope||this, null, arg, false);
15810         }
15811     },
15812
15813     // private
15814     loadResponse : function(o, success, response){
15815         delete this.activeRequest;
15816         if(!success){
15817             this.fireEvent("loadexception", this, o, response);
15818             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15819             return;
15820         }
15821         var result;
15822         try {
15823             result = o.reader.read(response);
15824         }catch(e){
15825             this.fireEvent("loadexception", this, o, response, e);
15826             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15827             return;
15828         }
15829         
15830         this.fireEvent("load", this, o, o.request.arg);
15831         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15832     },
15833
15834     // private
15835     update : function(dataSet){
15836
15837     },
15838
15839     // private
15840     updateResponse : function(dataSet){
15841
15842     }
15843 });/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853
15854 /**
15855  * @class Roo.data.ScriptTagProxy
15856  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15857  * other than the originating domain of the running page.<br><br>
15858  * <p>
15859  * <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
15860  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15861  * <p>
15862  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15863  * source code that is used as the source inside a &lt;script> tag.<br><br>
15864  * <p>
15865  * In order for the browser to process the returned data, the server must wrap the data object
15866  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15867  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15868  * depending on whether the callback name was passed:
15869  * <p>
15870  * <pre><code>
15871 boolean scriptTag = false;
15872 String cb = request.getParameter("callback");
15873 if (cb != null) {
15874     scriptTag = true;
15875     response.setContentType("text/javascript");
15876 } else {
15877     response.setContentType("application/x-json");
15878 }
15879 Writer out = response.getWriter();
15880 if (scriptTag) {
15881     out.write(cb + "(");
15882 }
15883 out.print(dataBlock.toJsonString());
15884 if (scriptTag) {
15885     out.write(");");
15886 }
15887 </pre></code>
15888  *
15889  * @constructor
15890  * @param {Object} config A configuration object.
15891  */
15892 Roo.data.ScriptTagProxy = function(config){
15893     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15894     Roo.apply(this, config);
15895     this.head = document.getElementsByTagName("head")[0];
15896 };
15897
15898 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15899
15900 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15901     /**
15902      * @cfg {String} url The URL from which to request the data object.
15903      */
15904     /**
15905      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15906      */
15907     timeout : 30000,
15908     /**
15909      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15910      * the server the name of the callback function set up by the load call to process the returned data object.
15911      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15912      * javascript output which calls this named function passing the data object as its only parameter.
15913      */
15914     callbackParam : "callback",
15915     /**
15916      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15917      * name to the request.
15918      */
15919     nocache : true,
15920
15921     /**
15922      * Load data from the configured URL, read the data object into
15923      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15924      * process that block using the passed callback.
15925      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15926      * for the request to the remote server.
15927      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15928      * object into a block of Roo.data.Records.
15929      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15930      * The function must be passed <ul>
15931      * <li>The Record block object</li>
15932      * <li>The "arg" argument from the load function</li>
15933      * <li>A boolean success indicator</li>
15934      * </ul>
15935      * @param {Object} scope The scope in which to call the callback
15936      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15937      */
15938     load : function(params, reader, callback, scope, arg){
15939         if(this.fireEvent("beforeload", this, params) !== false){
15940
15941             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15942
15943             var url = this.url;
15944             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15945             if(this.nocache){
15946                 url += "&_dc=" + (new Date().getTime());
15947             }
15948             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15949             var trans = {
15950                 id : transId,
15951                 cb : "stcCallback"+transId,
15952                 scriptId : "stcScript"+transId,
15953                 params : params,
15954                 arg : arg,
15955                 url : url,
15956                 callback : callback,
15957                 scope : scope,
15958                 reader : reader
15959             };
15960             var conn = this;
15961
15962             window[trans.cb] = function(o){
15963                 conn.handleResponse(o, trans);
15964             };
15965
15966             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15967
15968             if(this.autoAbort !== false){
15969                 this.abort();
15970             }
15971
15972             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15973
15974             var script = document.createElement("script");
15975             script.setAttribute("src", url);
15976             script.setAttribute("type", "text/javascript");
15977             script.setAttribute("id", trans.scriptId);
15978             this.head.appendChild(script);
15979
15980             this.trans = trans;
15981         }else{
15982             callback.call(scope||this, null, arg, false);
15983         }
15984     },
15985
15986     // private
15987     isLoading : function(){
15988         return this.trans ? true : false;
15989     },
15990
15991     /**
15992      * Abort the current server request.
15993      */
15994     abort : function(){
15995         if(this.isLoading()){
15996             this.destroyTrans(this.trans);
15997         }
15998     },
15999
16000     // private
16001     destroyTrans : function(trans, isLoaded){
16002         this.head.removeChild(document.getElementById(trans.scriptId));
16003         clearTimeout(trans.timeoutId);
16004         if(isLoaded){
16005             window[trans.cb] = undefined;
16006             try{
16007                 delete window[trans.cb];
16008             }catch(e){}
16009         }else{
16010             // if hasn't been loaded, wait for load to remove it to prevent script error
16011             window[trans.cb] = function(){
16012                 window[trans.cb] = undefined;
16013                 try{
16014                     delete window[trans.cb];
16015                 }catch(e){}
16016             };
16017         }
16018     },
16019
16020     // private
16021     handleResponse : function(o, trans){
16022         this.trans = false;
16023         this.destroyTrans(trans, true);
16024         var result;
16025         try {
16026             result = trans.reader.readRecords(o);
16027         }catch(e){
16028             this.fireEvent("loadexception", this, o, trans.arg, e);
16029             trans.callback.call(trans.scope||window, null, trans.arg, false);
16030             return;
16031         }
16032         this.fireEvent("load", this, o, trans.arg);
16033         trans.callback.call(trans.scope||window, result, trans.arg, true);
16034     },
16035
16036     // private
16037     handleFailure : function(trans){
16038         this.trans = false;
16039         this.destroyTrans(trans, false);
16040         this.fireEvent("loadexception", this, null, trans.arg);
16041         trans.callback.call(trans.scope||window, null, trans.arg, false);
16042     }
16043 });/*
16044  * Based on:
16045  * Ext JS Library 1.1.1
16046  * Copyright(c) 2006-2007, Ext JS, LLC.
16047  *
16048  * Originally Released Under LGPL - original licence link has changed is not relivant.
16049  *
16050  * Fork - LGPL
16051  * <script type="text/javascript">
16052  */
16053
16054 /**
16055  * @class Roo.data.JsonReader
16056  * @extends Roo.data.DataReader
16057  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16058  * based on mappings in a provided Roo.data.Record constructor.
16059  * 
16060  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16061  * in the reply previously. 
16062  * 
16063  * <p>
16064  * Example code:
16065  * <pre><code>
16066 var RecordDef = Roo.data.Record.create([
16067     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16068     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16069 ]);
16070 var myReader = new Roo.data.JsonReader({
16071     totalProperty: "results",    // The property which contains the total dataset size (optional)
16072     root: "rows",                // The property which contains an Array of row objects
16073     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16074 }, RecordDef);
16075 </code></pre>
16076  * <p>
16077  * This would consume a JSON file like this:
16078  * <pre><code>
16079 { 'results': 2, 'rows': [
16080     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16081     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16082 }
16083 </code></pre>
16084  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16085  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16086  * paged from the remote server.
16087  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16088  * @cfg {String} root name of the property which contains the Array of row objects.
16089  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16090  * @cfg {Array} fields Array of field definition objects
16091  * @constructor
16092  * Create a new JsonReader
16093  * @param {Object} meta Metadata configuration options
16094  * @param {Object} recordType Either an Array of field definition objects,
16095  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16096  */
16097 Roo.data.JsonReader = function(meta, recordType){
16098     
16099     meta = meta || {};
16100     // set some defaults:
16101     Roo.applyIf(meta, {
16102         totalProperty: 'total',
16103         successProperty : 'success',
16104         root : 'data',
16105         id : 'id'
16106     });
16107     
16108     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16109 };
16110 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16111     
16112     readerType : 'Json',
16113     
16114     /**
16115      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16116      * Used by Store query builder to append _requestMeta to params.
16117      * 
16118      */
16119     metaFromRemote : false,
16120     /**
16121      * This method is only used by a DataProxy which has retrieved data from a remote server.
16122      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16123      * @return {Object} data A data block which is used by an Roo.data.Store object as
16124      * a cache of Roo.data.Records.
16125      */
16126     read : function(response){
16127         var json = response.responseText;
16128        
16129         var o = /* eval:var:o */ eval("("+json+")");
16130         if(!o) {
16131             throw {message: "JsonReader.read: Json object not found"};
16132         }
16133         
16134         if(o.metaData){
16135             
16136             delete this.ef;
16137             this.metaFromRemote = true;
16138             this.meta = o.metaData;
16139             this.recordType = Roo.data.Record.create(o.metaData.fields);
16140             this.onMetaChange(this.meta, this.recordType, o);
16141         }
16142         return this.readRecords(o);
16143     },
16144
16145     // private function a store will implement
16146     onMetaChange : function(meta, recordType, o){
16147
16148     },
16149
16150     /**
16151          * @ignore
16152          */
16153     simpleAccess: function(obj, subsc) {
16154         return obj[subsc];
16155     },
16156
16157         /**
16158          * @ignore
16159          */
16160     getJsonAccessor: function(){
16161         var re = /[\[\.]/;
16162         return function(expr) {
16163             try {
16164                 return(re.test(expr))
16165                     ? new Function("obj", "return obj." + expr)
16166                     : function(obj){
16167                         return obj[expr];
16168                     };
16169             } catch(e){}
16170             return Roo.emptyFn;
16171         };
16172     }(),
16173
16174     /**
16175      * Create a data block containing Roo.data.Records from an XML document.
16176      * @param {Object} o An object which contains an Array of row objects in the property specified
16177      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16178      * which contains the total size of the dataset.
16179      * @return {Object} data A data block which is used by an Roo.data.Store object as
16180      * a cache of Roo.data.Records.
16181      */
16182     readRecords : function(o){
16183         /**
16184          * After any data loads, the raw JSON data is available for further custom processing.
16185          * @type Object
16186          */
16187         this.o = o;
16188         var s = this.meta, Record = this.recordType,
16189             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16190
16191 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16192         if (!this.ef) {
16193             if(s.totalProperty) {
16194                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16195                 }
16196                 if(s.successProperty) {
16197                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16198                 }
16199                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16200                 if (s.id) {
16201                         var g = this.getJsonAccessor(s.id);
16202                         this.getId = function(rec) {
16203                                 var r = g(rec);  
16204                                 return (r === undefined || r === "") ? null : r;
16205                         };
16206                 } else {
16207                         this.getId = function(){return null;};
16208                 }
16209             this.ef = [];
16210             for(var jj = 0; jj < fl; jj++){
16211                 f = fi[jj];
16212                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16213                 this.ef[jj] = this.getJsonAccessor(map);
16214             }
16215         }
16216
16217         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16218         if(s.totalProperty){
16219             var vt = parseInt(this.getTotal(o), 10);
16220             if(!isNaN(vt)){
16221                 totalRecords = vt;
16222             }
16223         }
16224         if(s.successProperty){
16225             var vs = this.getSuccess(o);
16226             if(vs === false || vs === 'false'){
16227                 success = false;
16228             }
16229         }
16230         var records = [];
16231         for(var i = 0; i < c; i++){
16232                 var n = root[i];
16233             var values = {};
16234             var id = this.getId(n);
16235             for(var j = 0; j < fl; j++){
16236                 f = fi[j];
16237             var v = this.ef[j](n);
16238             if (!f.convert) {
16239                 Roo.log('missing convert for ' + f.name);
16240                 Roo.log(f);
16241                 continue;
16242             }
16243             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16244             }
16245             var record = new Record(values, id);
16246             record.json = n;
16247             records[i] = record;
16248         }
16249         return {
16250             raw : o,
16251             success : success,
16252             records : records,
16253             totalRecords : totalRecords
16254         };
16255     },
16256     // used when loading children.. @see loadDataFromChildren
16257     toLoadData: function(rec)
16258     {
16259         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16260         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16261         return { data : data, total : data.length };
16262         
16263     }
16264 });/*
16265  * Based on:
16266  * Ext JS Library 1.1.1
16267  * Copyright(c) 2006-2007, Ext JS, LLC.
16268  *
16269  * Originally Released Under LGPL - original licence link has changed is not relivant.
16270  *
16271  * Fork - LGPL
16272  * <script type="text/javascript">
16273  */
16274
16275 /**
16276  * @class Roo.data.ArrayReader
16277  * @extends Roo.data.DataReader
16278  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16279  * Each element of that Array represents a row of data fields. The
16280  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16281  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16282  * <p>
16283  * Example code:.
16284  * <pre><code>
16285 var RecordDef = Roo.data.Record.create([
16286     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16287     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16288 ]);
16289 var myReader = new Roo.data.ArrayReader({
16290     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16291 }, RecordDef);
16292 </code></pre>
16293  * <p>
16294  * This would consume an Array like this:
16295  * <pre><code>
16296 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16297   </code></pre>
16298  
16299  * @constructor
16300  * Create a new JsonReader
16301  * @param {Object} meta Metadata configuration options.
16302  * @param {Object|Array} recordType Either an Array of field definition objects
16303  * 
16304  * @cfg {Array} fields Array of field definition objects
16305  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16306  * as specified to {@link Roo.data.Record#create},
16307  * or an {@link Roo.data.Record} object
16308  *
16309  * 
16310  * created using {@link Roo.data.Record#create}.
16311  */
16312 Roo.data.ArrayReader = function(meta, recordType)
16313 {    
16314     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16315 };
16316
16317 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16318     
16319       /**
16320      * Create a data block containing Roo.data.Records from an XML document.
16321      * @param {Object} o An Array of row objects which represents the dataset.
16322      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16323      * a cache of Roo.data.Records.
16324      */
16325     readRecords : function(o)
16326     {
16327         var sid = this.meta ? this.meta.id : null;
16328         var recordType = this.recordType, fields = recordType.prototype.fields;
16329         var records = [];
16330         var root = o;
16331         for(var i = 0; i < root.length; i++){
16332             var n = root[i];
16333             var values = {};
16334             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16335             for(var j = 0, jlen = fields.length; j < jlen; j++){
16336                 var f = fields.items[j];
16337                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16338                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16339                 v = f.convert(v);
16340                 values[f.name] = v;
16341             }
16342             var record = new recordType(values, id);
16343             record.json = n;
16344             records[records.length] = record;
16345         }
16346         return {
16347             records : records,
16348             totalRecords : records.length
16349         };
16350     },
16351     // used when loading children.. @see loadDataFromChildren
16352     toLoadData: function(rec)
16353     {
16354         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16355         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16356         
16357     }
16358     
16359     
16360 });/*
16361  * - LGPL
16362  * * 
16363  */
16364
16365 /**
16366  * @class Roo.bootstrap.ComboBox
16367  * @extends Roo.bootstrap.TriggerField
16368  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16369  * @cfg {Boolean} append (true|false) default false
16370  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16371  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16372  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16373  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16374  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16375  * @cfg {Boolean} animate default true
16376  * @cfg {Boolean} emptyResultText only for touch device
16377  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16378  * @cfg {String} emptyTitle default ''
16379  * @cfg {Number} width fixed with? experimental
16380  * @constructor
16381  * Create a new ComboBox.
16382  * @param {Object} config Configuration options
16383  */
16384 Roo.bootstrap.ComboBox = function(config){
16385     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16386     this.addEvents({
16387         /**
16388          * @event expand
16389          * Fires when the dropdown list is expanded
16390         * @param {Roo.bootstrap.ComboBox} combo This combo box
16391         */
16392         'expand' : true,
16393         /**
16394          * @event collapse
16395          * Fires when the dropdown list is collapsed
16396         * @param {Roo.bootstrap.ComboBox} combo This combo box
16397         */
16398         'collapse' : true,
16399         /**
16400          * @event beforeselect
16401          * Fires before a list item is selected. Return false to cancel the selection.
16402         * @param {Roo.bootstrap.ComboBox} combo This combo box
16403         * @param {Roo.data.Record} record The data record returned from the underlying store
16404         * @param {Number} index The index of the selected item in the dropdown list
16405         */
16406         'beforeselect' : true,
16407         /**
16408          * @event select
16409          * Fires when a list item is selected
16410         * @param {Roo.bootstrap.ComboBox} combo This combo box
16411         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16412         * @param {Number} index The index of the selected item in the dropdown list
16413         */
16414         'select' : true,
16415         /**
16416          * @event beforequery
16417          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16418          * The event object passed has these properties:
16419         * @param {Roo.bootstrap.ComboBox} combo This combo box
16420         * @param {String} query The query
16421         * @param {Boolean} forceAll true to force "all" query
16422         * @param {Boolean} cancel true to cancel the query
16423         * @param {Object} e The query event object
16424         */
16425         'beforequery': true,
16426          /**
16427          * @event add
16428          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16429         * @param {Roo.bootstrap.ComboBox} combo This combo box
16430         */
16431         'add' : true,
16432         /**
16433          * @event edit
16434          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16435         * @param {Roo.bootstrap.ComboBox} combo This combo box
16436         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16437         */
16438         'edit' : true,
16439         /**
16440          * @event remove
16441          * Fires when the remove value from the combobox array
16442         * @param {Roo.bootstrap.ComboBox} combo This combo box
16443         */
16444         'remove' : true,
16445         /**
16446          * @event afterremove
16447          * Fires when the remove value from the combobox array
16448         * @param {Roo.bootstrap.ComboBox} combo This combo box
16449         */
16450         'afterremove' : true,
16451         /**
16452          * @event specialfilter
16453          * Fires when specialfilter
16454             * @param {Roo.bootstrap.ComboBox} combo This combo box
16455             */
16456         'specialfilter' : true,
16457         /**
16458          * @event tick
16459          * Fires when tick the element
16460             * @param {Roo.bootstrap.ComboBox} combo This combo box
16461             */
16462         'tick' : true,
16463         /**
16464          * @event touchviewdisplay
16465          * Fires when touch view require special display (default is using displayField)
16466             * @param {Roo.bootstrap.ComboBox} combo This combo box
16467             * @param {Object} cfg set html .
16468             */
16469         'touchviewdisplay' : true
16470         
16471     });
16472     
16473     this.item = [];
16474     this.tickItems = [];
16475     
16476     this.selectedIndex = -1;
16477     if(this.mode == 'local'){
16478         if(config.queryDelay === undefined){
16479             this.queryDelay = 10;
16480         }
16481         if(config.minChars === undefined){
16482             this.minChars = 0;
16483         }
16484     }
16485 };
16486
16487 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16488      
16489     /**
16490      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16491      * rendering into an Roo.Editor, defaults to false)
16492      */
16493     /**
16494      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16495      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16496      */
16497     /**
16498      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16499      */
16500     /**
16501      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16502      * the dropdown list (defaults to undefined, with no header element)
16503      */
16504
16505      /**
16506      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16507      */
16508      
16509      /**
16510      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16511      */
16512     listWidth: undefined,
16513     /**
16514      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16515      * mode = 'remote' or 'text' if mode = 'local')
16516      */
16517     displayField: undefined,
16518     
16519     /**
16520      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16521      * mode = 'remote' or 'value' if mode = 'local'). 
16522      * Note: use of a valueField requires the user make a selection
16523      * in order for a value to be mapped.
16524      */
16525     valueField: undefined,
16526     /**
16527      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16528      */
16529     modalTitle : '',
16530     
16531     /**
16532      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16533      * field's data value (defaults to the underlying DOM element's name)
16534      */
16535     hiddenName: undefined,
16536     /**
16537      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16538      */
16539     listClass: '',
16540     /**
16541      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16542      */
16543     selectedClass: 'active',
16544     
16545     /**
16546      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16547      */
16548     shadow:'sides',
16549     /**
16550      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16551      * anchor positions (defaults to 'tl-bl')
16552      */
16553     listAlign: 'tl-bl?',
16554     /**
16555      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16556      */
16557     maxHeight: 300,
16558     /**
16559      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16560      * query specified by the allQuery config option (defaults to 'query')
16561      */
16562     triggerAction: 'query',
16563     /**
16564      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16565      * (defaults to 4, does not apply if editable = false)
16566      */
16567     minChars : 4,
16568     /**
16569      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16570      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16571      */
16572     typeAhead: false,
16573     /**
16574      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16575      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16576      */
16577     queryDelay: 500,
16578     /**
16579      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16580      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16581      */
16582     pageSize: 0,
16583     /**
16584      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16585      * when editable = true (defaults to false)
16586      */
16587     selectOnFocus:false,
16588     /**
16589      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16590      */
16591     queryParam: 'query',
16592     /**
16593      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16594      * when mode = 'remote' (defaults to 'Loading...')
16595      */
16596     loadingText: 'Loading...',
16597     /**
16598      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16599      */
16600     resizable: false,
16601     /**
16602      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16603      */
16604     handleHeight : 8,
16605     /**
16606      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16607      * traditional select (defaults to true)
16608      */
16609     editable: true,
16610     /**
16611      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16612      */
16613     allQuery: '',
16614     /**
16615      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16616      */
16617     mode: 'remote',
16618     /**
16619      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16620      * listWidth has a higher value)
16621      */
16622     minListWidth : 70,
16623     /**
16624      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16625      * allow the user to set arbitrary text into the field (defaults to false)
16626      */
16627     forceSelection:false,
16628     /**
16629      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16630      * if typeAhead = true (defaults to 250)
16631      */
16632     typeAheadDelay : 250,
16633     /**
16634      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16635      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16636      */
16637     valueNotFoundText : undefined,
16638     /**
16639      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16640      */
16641     blockFocus : false,
16642     
16643     /**
16644      * @cfg {Boolean} disableClear Disable showing of clear button.
16645      */
16646     disableClear : false,
16647     /**
16648      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16649      */
16650     alwaysQuery : false,
16651     
16652     /**
16653      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16654      */
16655     multiple : false,
16656     
16657     /**
16658      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16659      */
16660     invalidClass : "has-warning",
16661     
16662     /**
16663      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16664      */
16665     validClass : "has-success",
16666     
16667     /**
16668      * @cfg {Boolean} specialFilter (true|false) special filter default false
16669      */
16670     specialFilter : false,
16671     
16672     /**
16673      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16674      */
16675     mobileTouchView : true,
16676     
16677     /**
16678      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16679      */
16680     useNativeIOS : false,
16681     
16682     /**
16683      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16684      */
16685     mobile_restrict_height : false,
16686     
16687     ios_options : false,
16688     
16689     //private
16690     addicon : false,
16691     editicon: false,
16692     
16693     page: 0,
16694     hasQuery: false,
16695     append: false,
16696     loadNext: false,
16697     autoFocus : true,
16698     tickable : false,
16699     btnPosition : 'right',
16700     triggerList : true,
16701     showToggleBtn : true,
16702     animate : true,
16703     emptyResultText: 'Empty',
16704     triggerText : 'Select',
16705     emptyTitle : '',
16706     width : false,
16707     
16708     // element that contains real text value.. (when hidden is used..)
16709     
16710     getAutoCreate : function()
16711     {   
16712         var cfg = false;
16713         //render
16714         /*
16715          * Render classic select for iso
16716          */
16717         
16718         if(Roo.isIOS && this.useNativeIOS){
16719             cfg = this.getAutoCreateNativeIOS();
16720             return cfg;
16721         }
16722         
16723         /*
16724          * Touch Devices
16725          */
16726         
16727         if(Roo.isTouch && this.mobileTouchView){
16728             cfg = this.getAutoCreateTouchView();
16729             return cfg;;
16730         }
16731         
16732         /*
16733          *  Normal ComboBox
16734          */
16735         if(!this.tickable){
16736             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16737             return cfg;
16738         }
16739         
16740         /*
16741          *  ComboBox with tickable selections
16742          */
16743              
16744         var align = this.labelAlign || this.parentLabelAlign();
16745         
16746         cfg = {
16747             cls : 'form-group roo-combobox-tickable' //input-group
16748         };
16749         
16750         var btn_text_select = '';
16751         var btn_text_done = '';
16752         var btn_text_cancel = '';
16753         
16754         if (this.btn_text_show) {
16755             btn_text_select = 'Select';
16756             btn_text_done = 'Done';
16757             btn_text_cancel = 'Cancel'; 
16758         }
16759         
16760         var buttons = {
16761             tag : 'div',
16762             cls : 'tickable-buttons',
16763             cn : [
16764                 {
16765                     tag : 'button',
16766                     type : 'button',
16767                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16768                     //html : this.triggerText
16769                     html: btn_text_select
16770                 },
16771                 {
16772                     tag : 'button',
16773                     type : 'button',
16774                     name : 'ok',
16775                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16776                     //html : 'Done'
16777                     html: btn_text_done
16778                 },
16779                 {
16780                     tag : 'button',
16781                     type : 'button',
16782                     name : 'cancel',
16783                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16784                     //html : 'Cancel'
16785                     html: btn_text_cancel
16786                 }
16787             ]
16788         };
16789         
16790         if(this.editable){
16791             buttons.cn.unshift({
16792                 tag: 'input',
16793                 cls: 'roo-select2-search-field-input'
16794             });
16795         }
16796         
16797         var _this = this;
16798         
16799         Roo.each(buttons.cn, function(c){
16800             if (_this.size) {
16801                 c.cls += ' btn-' + _this.size;
16802             }
16803
16804             if (_this.disabled) {
16805                 c.disabled = true;
16806             }
16807         });
16808         
16809         var box = {
16810             tag: 'div',
16811             style : 'display: contents',
16812             cn: [
16813                 {
16814                     tag: 'input',
16815                     type : 'hidden',
16816                     cls: 'form-hidden-field'
16817                 },
16818                 {
16819                     tag: 'ul',
16820                     cls: 'roo-select2-choices',
16821                     cn:[
16822                         {
16823                             tag: 'li',
16824                             cls: 'roo-select2-search-field',
16825                             cn: [
16826                                 buttons
16827                             ]
16828                         }
16829                     ]
16830                 }
16831             ]
16832         };
16833         
16834         var combobox = {
16835             cls: 'roo-select2-container input-group roo-select2-container-multi',
16836             cn: [
16837                 
16838                 box
16839 //                {
16840 //                    tag: 'ul',
16841 //                    cls: 'typeahead typeahead-long dropdown-menu',
16842 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16843 //                }
16844             ]
16845         };
16846         
16847         if(this.hasFeedback && !this.allowBlank){
16848             
16849             var feedback = {
16850                 tag: 'span',
16851                 cls: 'glyphicon form-control-feedback'
16852             };
16853
16854             combobox.cn.push(feedback);
16855         }
16856         
16857         
16858         
16859         var indicator = {
16860             tag : 'i',
16861             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16862             tooltip : 'This field is required'
16863         };
16864         if (Roo.bootstrap.version == 4) {
16865             indicator = {
16866                 tag : 'i',
16867                 style : 'display:none'
16868             };
16869         }
16870         if (align ==='left' && this.fieldLabel.length) {
16871             
16872             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16873             
16874             cfg.cn = [
16875                 indicator,
16876                 {
16877                     tag: 'label',
16878                     'for' :  id,
16879                     cls : 'control-label col-form-label',
16880                     html : this.fieldLabel
16881
16882                 },
16883                 {
16884                     cls : "", 
16885                     cn: [
16886                         combobox
16887                     ]
16888                 }
16889
16890             ];
16891             
16892             var labelCfg = cfg.cn[1];
16893             var contentCfg = cfg.cn[2];
16894             
16895
16896             if(this.indicatorpos == 'right'){
16897                 
16898                 cfg.cn = [
16899                     {
16900                         tag: 'label',
16901                         'for' :  id,
16902                         cls : 'control-label col-form-label',
16903                         cn : [
16904                             {
16905                                 tag : 'span',
16906                                 html : this.fieldLabel
16907                             },
16908                             indicator
16909                         ]
16910                     },
16911                     {
16912                         cls : "",
16913                         cn: [
16914                             combobox
16915                         ]
16916                     }
16917
16918                 ];
16919                 
16920                 
16921                 
16922                 labelCfg = cfg.cn[0];
16923                 contentCfg = cfg.cn[1];
16924             
16925             }
16926             
16927             if(this.labelWidth > 12){
16928                 labelCfg.style = "width: " + this.labelWidth + 'px';
16929             }
16930             if(this.width * 1 > 0){
16931                 contentCfg.style = "width: " + this.width + 'px';
16932             }
16933             if(this.labelWidth < 13 && this.labelmd == 0){
16934                 this.labelmd = this.labelWidth;
16935             }
16936             
16937             if(this.labellg > 0){
16938                 labelCfg.cls += ' col-lg-' + this.labellg;
16939                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16940             }
16941             
16942             if(this.labelmd > 0){
16943                 labelCfg.cls += ' col-md-' + this.labelmd;
16944                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16945             }
16946             
16947             if(this.labelsm > 0){
16948                 labelCfg.cls += ' col-sm-' + this.labelsm;
16949                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16950             }
16951             
16952             if(this.labelxs > 0){
16953                 labelCfg.cls += ' col-xs-' + this.labelxs;
16954                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16955             }
16956                 
16957                 
16958         } else if ( this.fieldLabel.length) {
16959 //                Roo.log(" label");
16960                  cfg.cn = [
16961                    indicator,
16962                     {
16963                         tag: 'label',
16964                         //cls : 'input-group-addon',
16965                         html : this.fieldLabel
16966                     },
16967                     combobox
16968                 ];
16969                 
16970                 if(this.indicatorpos == 'right'){
16971                     cfg.cn = [
16972                         {
16973                             tag: 'label',
16974                             //cls : 'input-group-addon',
16975                             html : this.fieldLabel
16976                         },
16977                         indicator,
16978                         combobox
16979                     ];
16980                     
16981                 }
16982
16983         } else {
16984             
16985 //                Roo.log(" no label && no align");
16986                 cfg = combobox
16987                      
16988                 
16989         }
16990          
16991         var settings=this;
16992         ['xs','sm','md','lg'].map(function(size){
16993             if (settings[size]) {
16994                 cfg.cls += ' col-' + size + '-' + settings[size];
16995             }
16996         });
16997         
16998         return cfg;
16999         
17000     },
17001     
17002     _initEventsCalled : false,
17003     
17004     // private
17005     initEvents: function()
17006     {   
17007         if (this._initEventsCalled) { // as we call render... prevent looping...
17008             return;
17009         }
17010         this._initEventsCalled = true;
17011         
17012         if (!this.store) {
17013             throw "can not find store for combo";
17014         }
17015         
17016         this.indicator = this.indicatorEl();
17017         
17018         this.store = Roo.factory(this.store, Roo.data);
17019         this.store.parent = this;
17020         
17021         // if we are building from html. then this element is so complex, that we can not really
17022         // use the rendered HTML.
17023         // so we have to trash and replace the previous code.
17024         if (Roo.XComponent.build_from_html) {
17025             // remove this element....
17026             var e = this.el.dom, k=0;
17027             while (e ) { e = e.previousSibling;  ++k;}
17028
17029             this.el.remove();
17030             
17031             this.el=false;
17032             this.rendered = false;
17033             
17034             this.render(this.parent().getChildContainer(true), k);
17035         }
17036         
17037         if(Roo.isIOS && this.useNativeIOS){
17038             this.initIOSView();
17039             return;
17040         }
17041         
17042         /*
17043          * Touch Devices
17044          */
17045         
17046         if(Roo.isTouch && this.mobileTouchView){
17047             this.initTouchView();
17048             return;
17049         }
17050         
17051         if(this.tickable){
17052             this.initTickableEvents();
17053             return;
17054         }
17055         
17056         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17057         
17058         if(this.hiddenName){
17059             
17060             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17061             
17062             this.hiddenField.dom.value =
17063                 this.hiddenValue !== undefined ? this.hiddenValue :
17064                 this.value !== undefined ? this.value : '';
17065
17066             // prevent input submission
17067             this.el.dom.removeAttribute('name');
17068             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17069              
17070              
17071         }
17072         //if(Roo.isGecko){
17073         //    this.el.dom.setAttribute('autocomplete', 'off');
17074         //}
17075         
17076         var cls = 'x-combo-list';
17077         
17078         //this.list = new Roo.Layer({
17079         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17080         //});
17081         
17082         var _this = this;
17083         
17084         (function(){
17085             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17086             _this.list.setWidth(lw);
17087         }).defer(100);
17088         
17089         this.list.on('mouseover', this.onViewOver, this);
17090         this.list.on('mousemove', this.onViewMove, this);
17091         this.list.on('scroll', this.onViewScroll, this);
17092         
17093         /*
17094         this.list.swallowEvent('mousewheel');
17095         this.assetHeight = 0;
17096
17097         if(this.title){
17098             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17099             this.assetHeight += this.header.getHeight();
17100         }
17101
17102         this.innerList = this.list.createChild({cls:cls+'-inner'});
17103         this.innerList.on('mouseover', this.onViewOver, this);
17104         this.innerList.on('mousemove', this.onViewMove, this);
17105         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17106         
17107         if(this.allowBlank && !this.pageSize && !this.disableClear){
17108             this.footer = this.list.createChild({cls:cls+'-ft'});
17109             this.pageTb = new Roo.Toolbar(this.footer);
17110            
17111         }
17112         if(this.pageSize){
17113             this.footer = this.list.createChild({cls:cls+'-ft'});
17114             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17115                     {pageSize: this.pageSize});
17116             
17117         }
17118         
17119         if (this.pageTb && this.allowBlank && !this.disableClear) {
17120             var _this = this;
17121             this.pageTb.add(new Roo.Toolbar.Fill(), {
17122                 cls: 'x-btn-icon x-btn-clear',
17123                 text: '&#160;',
17124                 handler: function()
17125                 {
17126                     _this.collapse();
17127                     _this.clearValue();
17128                     _this.onSelect(false, -1);
17129                 }
17130             });
17131         }
17132         if (this.footer) {
17133             this.assetHeight += this.footer.getHeight();
17134         }
17135         */
17136             
17137         if(!this.tpl){
17138             this.tpl = Roo.bootstrap.version == 4 ?
17139                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17140                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17141         }
17142
17143         this.view = new Roo.View(this.list, this.tpl, {
17144             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17145         });
17146         //this.view.wrapEl.setDisplayed(false);
17147         this.view.on('click', this.onViewClick, this);
17148         
17149         
17150         this.store.on('beforeload', this.onBeforeLoad, this);
17151         this.store.on('load', this.onLoad, this);
17152         this.store.on('loadexception', this.onLoadException, this);
17153         /*
17154         if(this.resizable){
17155             this.resizer = new Roo.Resizable(this.list,  {
17156                pinned:true, handles:'se'
17157             });
17158             this.resizer.on('resize', function(r, w, h){
17159                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17160                 this.listWidth = w;
17161                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17162                 this.restrictHeight();
17163             }, this);
17164             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17165         }
17166         */
17167         if(!this.editable){
17168             this.editable = true;
17169             this.setEditable(false);
17170         }
17171         
17172         /*
17173         
17174         if (typeof(this.events.add.listeners) != 'undefined') {
17175             
17176             this.addicon = this.wrap.createChild(
17177                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17178        
17179             this.addicon.on('click', function(e) {
17180                 this.fireEvent('add', this);
17181             }, this);
17182         }
17183         if (typeof(this.events.edit.listeners) != 'undefined') {
17184             
17185             this.editicon = this.wrap.createChild(
17186                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17187             if (this.addicon) {
17188                 this.editicon.setStyle('margin-left', '40px');
17189             }
17190             this.editicon.on('click', function(e) {
17191                 
17192                 // we fire even  if inothing is selected..
17193                 this.fireEvent('edit', this, this.lastData );
17194                 
17195             }, this);
17196         }
17197         */
17198         
17199         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17200             "up" : function(e){
17201                 this.inKeyMode = true;
17202                 this.selectPrev();
17203             },
17204
17205             "down" : function(e){
17206                 if(!this.isExpanded()){
17207                     this.onTriggerClick();
17208                 }else{
17209                     this.inKeyMode = true;
17210                     this.selectNext();
17211                 }
17212             },
17213
17214             "enter" : function(e){
17215 //                this.onViewClick();
17216                 //return true;
17217                 this.collapse();
17218                 
17219                 if(this.fireEvent("specialkey", this, e)){
17220                     this.onViewClick(false);
17221                 }
17222                 
17223                 return true;
17224             },
17225
17226             "esc" : function(e){
17227                 this.collapse();
17228             },
17229
17230             "tab" : function(e){
17231                 this.collapse();
17232                 
17233                 if(this.fireEvent("specialkey", this, e)){
17234                     this.onViewClick(false);
17235                 }
17236                 
17237                 return true;
17238             },
17239
17240             scope : this,
17241
17242             doRelay : function(foo, bar, hname){
17243                 if(hname == 'down' || this.scope.isExpanded()){
17244                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17245                 }
17246                 return true;
17247             },
17248
17249             forceKeyDown: true
17250         });
17251         
17252         
17253         this.queryDelay = Math.max(this.queryDelay || 10,
17254                 this.mode == 'local' ? 10 : 250);
17255         
17256         
17257         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17258         
17259         if(this.typeAhead){
17260             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17261         }
17262         if(this.editable !== false){
17263             this.inputEl().on("keyup", this.onKeyUp, this);
17264         }
17265         if(this.forceSelection){
17266             this.inputEl().on('blur', this.doForce, this);
17267         }
17268         
17269         if(this.multiple){
17270             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17271             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17272         }
17273     },
17274     
17275     initTickableEvents: function()
17276     {   
17277         this.createList();
17278         
17279         if(this.hiddenName){
17280             
17281             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17282             
17283             this.hiddenField.dom.value =
17284                 this.hiddenValue !== undefined ? this.hiddenValue :
17285                 this.value !== undefined ? this.value : '';
17286
17287             // prevent input submission
17288             this.el.dom.removeAttribute('name');
17289             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17290              
17291              
17292         }
17293         
17294 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17295         
17296         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17297         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17298         if(this.triggerList){
17299             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17300         }
17301          
17302         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17303         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17304         
17305         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17306         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17307         
17308         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17309         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17310         
17311         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17312         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17313         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17314         
17315         this.okBtn.hide();
17316         this.cancelBtn.hide();
17317         
17318         var _this = this;
17319         
17320         (function(){
17321             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17322             _this.list.setWidth(lw);
17323         }).defer(100);
17324         
17325         this.list.on('mouseover', this.onViewOver, this);
17326         this.list.on('mousemove', this.onViewMove, this);
17327         
17328         this.list.on('scroll', this.onViewScroll, this);
17329         
17330         if(!this.tpl){
17331             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17332                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17333         }
17334
17335         this.view = new Roo.View(this.list, this.tpl, {
17336             singleSelect:true,
17337             tickable:true,
17338             parent:this,
17339             store: this.store,
17340             selectedClass: this.selectedClass
17341         });
17342         
17343         //this.view.wrapEl.setDisplayed(false);
17344         this.view.on('click', this.onViewClick, this);
17345         
17346         
17347         
17348         this.store.on('beforeload', this.onBeforeLoad, this);
17349         this.store.on('load', this.onLoad, this);
17350         this.store.on('loadexception', this.onLoadException, this);
17351         
17352         if(this.editable){
17353             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17354                 "up" : function(e){
17355                     this.inKeyMode = true;
17356                     this.selectPrev();
17357                 },
17358
17359                 "down" : function(e){
17360                     this.inKeyMode = true;
17361                     this.selectNext();
17362                 },
17363
17364                 "enter" : function(e){
17365                     if(this.fireEvent("specialkey", this, e)){
17366                         this.onViewClick(false);
17367                     }
17368                     
17369                     return true;
17370                 },
17371
17372                 "esc" : function(e){
17373                     this.onTickableFooterButtonClick(e, false, false);
17374                 },
17375
17376                 "tab" : function(e){
17377                     this.fireEvent("specialkey", this, e);
17378                     
17379                     this.onTickableFooterButtonClick(e, false, false);
17380                     
17381                     return true;
17382                 },
17383
17384                 scope : this,
17385
17386                 doRelay : function(e, fn, key){
17387                     if(this.scope.isExpanded()){
17388                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17389                     }
17390                     return true;
17391                 },
17392
17393                 forceKeyDown: true
17394             });
17395         }
17396         
17397         this.queryDelay = Math.max(this.queryDelay || 10,
17398                 this.mode == 'local' ? 10 : 250);
17399         
17400         
17401         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17402         
17403         if(this.typeAhead){
17404             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17405         }
17406         
17407         if(this.editable !== false){
17408             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17409         }
17410         
17411         this.indicator = this.indicatorEl();
17412         
17413         if(this.indicator){
17414             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17415             this.indicator.hide();
17416         }
17417         
17418     },
17419
17420     onDestroy : function(){
17421         if(this.view){
17422             this.view.setStore(null);
17423             this.view.el.removeAllListeners();
17424             this.view.el.remove();
17425             this.view.purgeListeners();
17426         }
17427         if(this.list){
17428             this.list.dom.innerHTML  = '';
17429         }
17430         
17431         if(this.store){
17432             this.store.un('beforeload', this.onBeforeLoad, this);
17433             this.store.un('load', this.onLoad, this);
17434             this.store.un('loadexception', this.onLoadException, this);
17435         }
17436         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17437     },
17438
17439     // private
17440     fireKey : function(e){
17441         if(e.isNavKeyPress() && !this.list.isVisible()){
17442             this.fireEvent("specialkey", this, e);
17443         }
17444     },
17445
17446     // private
17447     onResize: function(w, h)
17448     {
17449         
17450         
17451 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17452 //        
17453 //        if(typeof w != 'number'){
17454 //            // we do not handle it!?!?
17455 //            return;
17456 //        }
17457 //        var tw = this.trigger.getWidth();
17458 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17459 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17460 //        var x = w - tw;
17461 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17462 //            
17463 //        //this.trigger.setStyle('left', x+'px');
17464 //        
17465 //        if(this.list && this.listWidth === undefined){
17466 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17467 //            this.list.setWidth(lw);
17468 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17469 //        }
17470         
17471     
17472         
17473     },
17474
17475     /**
17476      * Allow or prevent the user from directly editing the field text.  If false is passed,
17477      * the user will only be able to select from the items defined in the dropdown list.  This method
17478      * is the runtime equivalent of setting the 'editable' config option at config time.
17479      * @param {Boolean} value True to allow the user to directly edit the field text
17480      */
17481     setEditable : function(value){
17482         if(value == this.editable){
17483             return;
17484         }
17485         this.editable = value;
17486         if(!value){
17487             this.inputEl().dom.setAttribute('readOnly', true);
17488             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17489             this.inputEl().addClass('x-combo-noedit');
17490         }else{
17491             this.inputEl().dom.removeAttribute('readOnly');
17492             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17493             this.inputEl().removeClass('x-combo-noedit');
17494         }
17495     },
17496
17497     // private
17498     
17499     onBeforeLoad : function(combo,opts){
17500         if(!this.hasFocus){
17501             return;
17502         }
17503          if (!opts.add) {
17504             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17505          }
17506         this.restrictHeight();
17507         this.selectedIndex = -1;
17508     },
17509
17510     // private
17511     onLoad : function(){
17512         
17513         this.hasQuery = false;
17514         
17515         if(!this.hasFocus){
17516             return;
17517         }
17518         
17519         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17520             this.loading.hide();
17521         }
17522         
17523         if(this.store.getCount() > 0){
17524             
17525             this.expand();
17526             this.restrictHeight();
17527             if(this.lastQuery == this.allQuery){
17528                 if(this.editable && !this.tickable){
17529                     this.inputEl().dom.select();
17530                 }
17531                 
17532                 if(
17533                     !this.selectByValue(this.value, true) &&
17534                     this.autoFocus && 
17535                     (
17536                         !this.store.lastOptions ||
17537                         typeof(this.store.lastOptions.add) == 'undefined' || 
17538                         this.store.lastOptions.add != true
17539                     )
17540                 ){
17541                     this.select(0, true);
17542                 }
17543             }else{
17544                 if(this.autoFocus){
17545                     this.selectNext();
17546                 }
17547                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17548                     this.taTask.delay(this.typeAheadDelay);
17549                 }
17550             }
17551         }else{
17552             this.onEmptyResults();
17553         }
17554         
17555         //this.el.focus();
17556     },
17557     // private
17558     onLoadException : function()
17559     {
17560         this.hasQuery = false;
17561         
17562         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17563             this.loading.hide();
17564         }
17565         
17566         if(this.tickable && this.editable){
17567             return;
17568         }
17569         
17570         this.collapse();
17571         // only causes errors at present
17572         //Roo.log(this.store.reader.jsonData);
17573         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17574             // fixme
17575             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17576         //}
17577         
17578         
17579     },
17580     // private
17581     onTypeAhead : function(){
17582         if(this.store.getCount() > 0){
17583             var r = this.store.getAt(0);
17584             var newValue = r.data[this.displayField];
17585             var len = newValue.length;
17586             var selStart = this.getRawValue().length;
17587             
17588             if(selStart != len){
17589                 this.setRawValue(newValue);
17590                 this.selectText(selStart, newValue.length);
17591             }
17592         }
17593     },
17594
17595     // private
17596     onSelect : function(record, index){
17597         
17598         if(this.fireEvent('beforeselect', this, record, index) !== false){
17599         
17600             this.setFromData(index > -1 ? record.data : false);
17601             
17602             this.collapse();
17603             this.fireEvent('select', this, record, index);
17604         }
17605     },
17606
17607     /**
17608      * Returns the currently selected field value or empty string if no value is set.
17609      * @return {String} value The selected value
17610      */
17611     getValue : function()
17612     {
17613         if(Roo.isIOS && this.useNativeIOS){
17614             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17615         }
17616         
17617         if(this.multiple){
17618             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17619         }
17620         
17621         if(this.valueField){
17622             return typeof this.value != 'undefined' ? this.value : '';
17623         }else{
17624             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17625         }
17626     },
17627     
17628     getRawValue : function()
17629     {
17630         if(Roo.isIOS && this.useNativeIOS){
17631             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17632         }
17633         
17634         var v = this.inputEl().getValue();
17635         
17636         return v;
17637     },
17638
17639     /**
17640      * Clears any text/value currently set in the field
17641      */
17642     clearValue : function(){
17643         
17644         if(this.hiddenField){
17645             this.hiddenField.dom.value = '';
17646         }
17647         this.value = '';
17648         this.setRawValue('');
17649         this.lastSelectionText = '';
17650         this.lastData = false;
17651         
17652         var close = this.closeTriggerEl();
17653         
17654         if(close){
17655             close.hide();
17656         }
17657         
17658         this.validate();
17659         
17660     },
17661
17662     /**
17663      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17664      * will be displayed in the field.  If the value does not match the data value of an existing item,
17665      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17666      * Otherwise the field will be blank (although the value will still be set).
17667      * @param {String} value The value to match
17668      */
17669     setValue : function(v)
17670     {
17671         if(Roo.isIOS && this.useNativeIOS){
17672             this.setIOSValue(v);
17673             return;
17674         }
17675         
17676         if(this.multiple){
17677             this.syncValue();
17678             return;
17679         }
17680         
17681         var text = v;
17682         if(this.valueField){
17683             var r = this.findRecord(this.valueField, v);
17684             if(r){
17685                 text = r.data[this.displayField];
17686             }else if(this.valueNotFoundText !== undefined){
17687                 text = this.valueNotFoundText;
17688             }
17689         }
17690         this.lastSelectionText = text;
17691         if(this.hiddenField){
17692             this.hiddenField.dom.value = v;
17693         }
17694         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17695         this.value = v;
17696         
17697         var close = this.closeTriggerEl();
17698         
17699         if(close){
17700             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17701         }
17702         
17703         this.validate();
17704     },
17705     /**
17706      * @property {Object} the last set data for the element
17707      */
17708     
17709     lastData : false,
17710     /**
17711      * Sets the value of the field based on a object which is related to the record format for the store.
17712      * @param {Object} value the value to set as. or false on reset?
17713      */
17714     setFromData : function(o){
17715         
17716         if(this.multiple){
17717             this.addItem(o);
17718             return;
17719         }
17720             
17721         var dv = ''; // display value
17722         var vv = ''; // value value..
17723         this.lastData = o;
17724         if (this.displayField) {
17725             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17726         } else {
17727             // this is an error condition!!!
17728             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17729         }
17730         
17731         if(this.valueField){
17732             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17733         }
17734         
17735         var close = this.closeTriggerEl();
17736         
17737         if(close){
17738             if(dv.length || vv * 1 > 0){
17739                 close.show() ;
17740                 this.blockFocus=true;
17741             } else {
17742                 close.hide();
17743             }             
17744         }
17745         
17746         if(this.hiddenField){
17747             this.hiddenField.dom.value = vv;
17748             
17749             this.lastSelectionText = dv;
17750             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17751             this.value = vv;
17752             return;
17753         }
17754         // no hidden field.. - we store the value in 'value', but still display
17755         // display field!!!!
17756         this.lastSelectionText = dv;
17757         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17758         this.value = vv;
17759         
17760         
17761         
17762     },
17763     // private
17764     reset : function(){
17765         // overridden so that last data is reset..
17766         
17767         if(this.multiple){
17768             this.clearItem();
17769             return;
17770         }
17771         
17772         this.setValue(this.originalValue);
17773         //this.clearInvalid();
17774         this.lastData = false;
17775         if (this.view) {
17776             this.view.clearSelections();
17777         }
17778         
17779         this.validate();
17780     },
17781     // private
17782     findRecord : function(prop, value){
17783         var record;
17784         if(this.store.getCount() > 0){
17785             this.store.each(function(r){
17786                 if(r.data[prop] == value){
17787                     record = r;
17788                     return false;
17789                 }
17790                 return true;
17791             });
17792         }
17793         return record;
17794     },
17795     
17796     getName: function()
17797     {
17798         // returns hidden if it's set..
17799         if (!this.rendered) {return ''};
17800         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17801         
17802     },
17803     // private
17804     onViewMove : function(e, t){
17805         this.inKeyMode = false;
17806     },
17807
17808     // private
17809     onViewOver : function(e, t){
17810         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17811             return;
17812         }
17813         var item = this.view.findItemFromChild(t);
17814         
17815         if(item){
17816             var index = this.view.indexOf(item);
17817             this.select(index, false);
17818         }
17819     },
17820
17821     // private
17822     onViewClick : function(view, doFocus, el, e)
17823     {
17824         var index = this.view.getSelectedIndexes()[0];
17825         
17826         var r = this.store.getAt(index);
17827         
17828         if(this.tickable){
17829             
17830             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17831                 return;
17832             }
17833             
17834             var rm = false;
17835             var _this = this;
17836             
17837             Roo.each(this.tickItems, function(v,k){
17838                 
17839                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17840                     Roo.log(v);
17841                     _this.tickItems.splice(k, 1);
17842                     
17843                     if(typeof(e) == 'undefined' && view == false){
17844                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17845                     }
17846                     
17847                     rm = true;
17848                     return;
17849                 }
17850             });
17851             
17852             if(rm){
17853                 return;
17854             }
17855             
17856             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17857                 this.tickItems.push(r.data);
17858             }
17859             
17860             if(typeof(e) == 'undefined' && view == false){
17861                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17862             }
17863                     
17864             return;
17865         }
17866         
17867         if(r){
17868             this.onSelect(r, index);
17869         }
17870         if(doFocus !== false && !this.blockFocus){
17871             this.inputEl().focus();
17872         }
17873     },
17874
17875     // private
17876     restrictHeight : function(){
17877         //this.innerList.dom.style.height = '';
17878         //var inner = this.innerList.dom;
17879         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17880         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17881         //this.list.beginUpdate();
17882         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17883         this.list.alignTo(this.inputEl(), this.listAlign);
17884         this.list.alignTo(this.inputEl(), this.listAlign);
17885         //this.list.endUpdate();
17886     },
17887
17888     // private
17889     onEmptyResults : function(){
17890         
17891         if(this.tickable && this.editable){
17892             this.hasFocus = false;
17893             this.restrictHeight();
17894             return;
17895         }
17896         
17897         this.collapse();
17898     },
17899
17900     /**
17901      * Returns true if the dropdown list is expanded, else false.
17902      */
17903     isExpanded : function(){
17904         return this.list.isVisible();
17905     },
17906
17907     /**
17908      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17909      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17910      * @param {String} value The data value of the item to select
17911      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17912      * selected item if it is not currently in view (defaults to true)
17913      * @return {Boolean} True if the value matched an item in the list, else false
17914      */
17915     selectByValue : function(v, scrollIntoView){
17916         if(v !== undefined && v !== null){
17917             var r = this.findRecord(this.valueField || this.displayField, v);
17918             if(r){
17919                 this.select(this.store.indexOf(r), scrollIntoView);
17920                 return true;
17921             }
17922         }
17923         return false;
17924     },
17925
17926     /**
17927      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17928      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17929      * @param {Number} index The zero-based index of the list item to select
17930      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17931      * selected item if it is not currently in view (defaults to true)
17932      */
17933     select : function(index, scrollIntoView){
17934         this.selectedIndex = index;
17935         this.view.select(index);
17936         if(scrollIntoView !== false){
17937             var el = this.view.getNode(index);
17938             /*
17939              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17940              */
17941             if(el){
17942                 this.list.scrollChildIntoView(el, false);
17943             }
17944         }
17945     },
17946
17947     // private
17948     selectNext : function(){
17949         var ct = this.store.getCount();
17950         if(ct > 0){
17951             if(this.selectedIndex == -1){
17952                 this.select(0);
17953             }else if(this.selectedIndex < ct-1){
17954                 this.select(this.selectedIndex+1);
17955             }
17956         }
17957     },
17958
17959     // private
17960     selectPrev : function(){
17961         var ct = this.store.getCount();
17962         if(ct > 0){
17963             if(this.selectedIndex == -1){
17964                 this.select(0);
17965             }else if(this.selectedIndex != 0){
17966                 this.select(this.selectedIndex-1);
17967             }
17968         }
17969     },
17970
17971     // private
17972     onKeyUp : function(e){
17973         if(this.editable !== false && !e.isSpecialKey()){
17974             this.lastKey = e.getKey();
17975             this.dqTask.delay(this.queryDelay);
17976         }
17977     },
17978
17979     // private
17980     validateBlur : function(){
17981         return !this.list || !this.list.isVisible();   
17982     },
17983
17984     // private
17985     initQuery : function(){
17986         
17987         var v = this.getRawValue();
17988         
17989         if(this.tickable && this.editable){
17990             v = this.tickableInputEl().getValue();
17991         }
17992         
17993         this.doQuery(v);
17994     },
17995
17996     // private
17997     doForce : function(){
17998         if(this.inputEl().dom.value.length > 0){
17999             this.inputEl().dom.value =
18000                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18001              
18002         }
18003     },
18004
18005     /**
18006      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18007      * query allowing the query action to be canceled if needed.
18008      * @param {String} query The SQL query to execute
18009      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18010      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18011      * saved in the current store (defaults to false)
18012      */
18013     doQuery : function(q, forceAll){
18014         
18015         if(q === undefined || q === null){
18016             q = '';
18017         }
18018         var qe = {
18019             query: q,
18020             forceAll: forceAll,
18021             combo: this,
18022             cancel:false
18023         };
18024         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18025             return false;
18026         }
18027         q = qe.query;
18028         
18029         forceAll = qe.forceAll;
18030         if(forceAll === true || (q.length >= this.minChars)){
18031             
18032             this.hasQuery = true;
18033             
18034             if(this.lastQuery != q || this.alwaysQuery){
18035                 this.lastQuery = q;
18036                 if(this.mode == 'local'){
18037                     this.selectedIndex = -1;
18038                     if(forceAll){
18039                         this.store.clearFilter();
18040                     }else{
18041                         
18042                         if(this.specialFilter){
18043                             this.fireEvent('specialfilter', this);
18044                             this.onLoad();
18045                             return;
18046                         }
18047                         
18048                         this.store.filter(this.displayField, q);
18049                     }
18050                     
18051                     this.store.fireEvent("datachanged", this.store);
18052                     
18053                     this.onLoad();
18054                     
18055                     
18056                 }else{
18057                     
18058                     this.store.baseParams[this.queryParam] = q;
18059                     
18060                     var options = {params : this.getParams(q)};
18061                     
18062                     if(this.loadNext){
18063                         options.add = true;
18064                         options.params.start = this.page * this.pageSize;
18065                     }
18066                     
18067                     this.store.load(options);
18068                     
18069                     /*
18070                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18071                      *  we should expand the list on onLoad
18072                      *  so command out it
18073                      */
18074 //                    this.expand();
18075                 }
18076             }else{
18077                 this.selectedIndex = -1;
18078                 this.onLoad();   
18079             }
18080         }
18081         
18082         this.loadNext = false;
18083     },
18084     
18085     // private
18086     getParams : function(q){
18087         var p = {};
18088         //p[this.queryParam] = q;
18089         
18090         if(this.pageSize){
18091             p.start = 0;
18092             p.limit = this.pageSize;
18093         }
18094         return p;
18095     },
18096
18097     /**
18098      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18099      */
18100     collapse : function(){
18101         if(!this.isExpanded()){
18102             return;
18103         }
18104         
18105         this.list.hide();
18106         
18107         this.hasFocus = false;
18108         
18109         if(this.tickable){
18110             this.okBtn.hide();
18111             this.cancelBtn.hide();
18112             this.trigger.show();
18113             
18114             if(this.editable){
18115                 this.tickableInputEl().dom.value = '';
18116                 this.tickableInputEl().blur();
18117             }
18118             
18119         }
18120         
18121         Roo.get(document).un('mousedown', this.collapseIf, this);
18122         Roo.get(document).un('mousewheel', this.collapseIf, this);
18123         if (!this.editable) {
18124             Roo.get(document).un('keydown', this.listKeyPress, this);
18125         }
18126         this.fireEvent('collapse', this);
18127         
18128         this.validate();
18129     },
18130
18131     // private
18132     collapseIf : function(e){
18133         var in_combo  = e.within(this.el);
18134         var in_list =  e.within(this.list);
18135         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18136         
18137         if (in_combo || in_list || is_list) {
18138             //e.stopPropagation();
18139             return;
18140         }
18141         
18142         if(this.tickable){
18143             this.onTickableFooterButtonClick(e, false, false);
18144         }
18145
18146         this.collapse();
18147         
18148     },
18149
18150     /**
18151      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18152      */
18153     expand : function(){
18154        
18155         if(this.isExpanded() || !this.hasFocus){
18156             return;
18157         }
18158         
18159         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18160         this.list.setWidth(lw);
18161         
18162         Roo.log('expand');
18163         
18164         this.list.show();
18165         
18166         this.restrictHeight();
18167         
18168         if(this.tickable){
18169             
18170             this.tickItems = Roo.apply([], this.item);
18171             
18172             this.okBtn.show();
18173             this.cancelBtn.show();
18174             this.trigger.hide();
18175             
18176             if(this.editable){
18177                 this.tickableInputEl().focus();
18178             }
18179             
18180         }
18181         
18182         Roo.get(document).on('mousedown', this.collapseIf, this);
18183         Roo.get(document).on('mousewheel', this.collapseIf, this);
18184         if (!this.editable) {
18185             Roo.get(document).on('keydown', this.listKeyPress, this);
18186         }
18187         
18188         this.fireEvent('expand', this);
18189     },
18190
18191     // private
18192     // Implements the default empty TriggerField.onTriggerClick function
18193     onTriggerClick : function(e)
18194     {
18195         Roo.log('trigger click');
18196         
18197         if(this.disabled || !this.triggerList){
18198             return;
18199         }
18200         
18201         this.page = 0;
18202         this.loadNext = false;
18203         
18204         if(this.isExpanded()){
18205             this.collapse();
18206             if (!this.blockFocus) {
18207                 this.inputEl().focus();
18208             }
18209             
18210         }else {
18211             this.hasFocus = true;
18212             if(this.triggerAction == 'all') {
18213                 this.doQuery(this.allQuery, true);
18214             } else {
18215                 this.doQuery(this.getRawValue());
18216             }
18217             if (!this.blockFocus) {
18218                 this.inputEl().focus();
18219             }
18220         }
18221     },
18222     
18223     onTickableTriggerClick : function(e)
18224     {
18225         if(this.disabled){
18226             return;
18227         }
18228         
18229         this.page = 0;
18230         this.loadNext = false;
18231         this.hasFocus = true;
18232         
18233         if(this.triggerAction == 'all') {
18234             this.doQuery(this.allQuery, true);
18235         } else {
18236             this.doQuery(this.getRawValue());
18237         }
18238     },
18239     
18240     onSearchFieldClick : function(e)
18241     {
18242         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18243             this.onTickableFooterButtonClick(e, false, false);
18244             return;
18245         }
18246         
18247         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18248             return;
18249         }
18250         
18251         this.page = 0;
18252         this.loadNext = false;
18253         this.hasFocus = true;
18254         
18255         if(this.triggerAction == 'all') {
18256             this.doQuery(this.allQuery, true);
18257         } else {
18258             this.doQuery(this.getRawValue());
18259         }
18260     },
18261     
18262     listKeyPress : function(e)
18263     {
18264         //Roo.log('listkeypress');
18265         // scroll to first matching element based on key pres..
18266         if (e.isSpecialKey()) {
18267             return false;
18268         }
18269         var k = String.fromCharCode(e.getKey()).toUpperCase();
18270         //Roo.log(k);
18271         var match  = false;
18272         var csel = this.view.getSelectedNodes();
18273         var cselitem = false;
18274         if (csel.length) {
18275             var ix = this.view.indexOf(csel[0]);
18276             cselitem  = this.store.getAt(ix);
18277             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18278                 cselitem = false;
18279             }
18280             
18281         }
18282         
18283         this.store.each(function(v) { 
18284             if (cselitem) {
18285                 // start at existing selection.
18286                 if (cselitem.id == v.id) {
18287                     cselitem = false;
18288                 }
18289                 return true;
18290             }
18291                 
18292             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18293                 match = this.store.indexOf(v);
18294                 return false;
18295             }
18296             return true;
18297         }, this);
18298         
18299         if (match === false) {
18300             return true; // no more action?
18301         }
18302         // scroll to?
18303         this.view.select(match);
18304         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18305         sn.scrollIntoView(sn.dom.parentNode, false);
18306     },
18307     
18308     onViewScroll : function(e, t){
18309         
18310         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){
18311             return;
18312         }
18313         
18314         this.hasQuery = true;
18315         
18316         this.loading = this.list.select('.loading', true).first();
18317         
18318         if(this.loading === null){
18319             this.list.createChild({
18320                 tag: 'div',
18321                 cls: 'loading roo-select2-more-results roo-select2-active',
18322                 html: 'Loading more results...'
18323             });
18324             
18325             this.loading = this.list.select('.loading', true).first();
18326             
18327             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18328             
18329             this.loading.hide();
18330         }
18331         
18332         this.loading.show();
18333         
18334         var _combo = this;
18335         
18336         this.page++;
18337         this.loadNext = true;
18338         
18339         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18340         
18341         return;
18342     },
18343     
18344     addItem : function(o)
18345     {   
18346         var dv = ''; // display value
18347         
18348         if (this.displayField) {
18349             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18350         } else {
18351             // this is an error condition!!!
18352             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18353         }
18354         
18355         if(!dv.length){
18356             return;
18357         }
18358         
18359         var choice = this.choices.createChild({
18360             tag: 'li',
18361             cls: 'roo-select2-search-choice',
18362             cn: [
18363                 {
18364                     tag: 'div',
18365                     html: dv
18366                 },
18367                 {
18368                     tag: 'a',
18369                     href: '#',
18370                     cls: 'roo-select2-search-choice-close fa fa-times',
18371                     tabindex: '-1'
18372                 }
18373             ]
18374             
18375         }, this.searchField);
18376         
18377         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18378         
18379         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18380         
18381         this.item.push(o);
18382         
18383         this.lastData = o;
18384         
18385         this.syncValue();
18386         
18387         this.inputEl().dom.value = '';
18388         
18389         this.validate();
18390     },
18391     
18392     onRemoveItem : function(e, _self, o)
18393     {
18394         e.preventDefault();
18395         
18396         this.lastItem = Roo.apply([], this.item);
18397         
18398         var index = this.item.indexOf(o.data) * 1;
18399         
18400         if( index < 0){
18401             Roo.log('not this item?!');
18402             return;
18403         }
18404         
18405         this.item.splice(index, 1);
18406         o.item.remove();
18407         
18408         this.syncValue();
18409         
18410         this.fireEvent('remove', this, e);
18411         
18412         this.validate();
18413         
18414     },
18415     
18416     syncValue : function()
18417     {
18418         if(!this.item.length){
18419             this.clearValue();
18420             return;
18421         }
18422             
18423         var value = [];
18424         var _this = this;
18425         Roo.each(this.item, function(i){
18426             if(_this.valueField){
18427                 value.push(i[_this.valueField]);
18428                 return;
18429             }
18430
18431             value.push(i);
18432         });
18433
18434         this.value = value.join(',');
18435
18436         if(this.hiddenField){
18437             this.hiddenField.dom.value = this.value;
18438         }
18439         
18440         this.store.fireEvent("datachanged", this.store);
18441         
18442         this.validate();
18443     },
18444     
18445     clearItem : function()
18446     {
18447         if(!this.multiple){
18448             return;
18449         }
18450         
18451         this.item = [];
18452         
18453         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18454            c.remove();
18455         });
18456         
18457         this.syncValue();
18458         
18459         this.validate();
18460         
18461         if(this.tickable && !Roo.isTouch){
18462             this.view.refresh();
18463         }
18464     },
18465     
18466     inputEl: function ()
18467     {
18468         if(Roo.isIOS && this.useNativeIOS){
18469             return this.el.select('select.roo-ios-select', true).first();
18470         }
18471         
18472         if(Roo.isTouch && this.mobileTouchView){
18473             return this.el.select('input.form-control',true).first();
18474         }
18475         
18476         if(this.tickable){
18477             return this.searchField;
18478         }
18479         
18480         return this.el.select('input.form-control',true).first();
18481     },
18482     
18483     onTickableFooterButtonClick : function(e, btn, el)
18484     {
18485         e.preventDefault();
18486         
18487         this.lastItem = Roo.apply([], this.item);
18488         
18489         if(btn && btn.name == 'cancel'){
18490             this.tickItems = Roo.apply([], this.item);
18491             this.collapse();
18492             return;
18493         }
18494         
18495         this.clearItem();
18496         
18497         var _this = this;
18498         
18499         Roo.each(this.tickItems, function(o){
18500             _this.addItem(o);
18501         });
18502         
18503         this.collapse();
18504         
18505     },
18506     
18507     validate : function()
18508     {
18509         if(this.getVisibilityEl().hasClass('hidden')){
18510             return true;
18511         }
18512         
18513         var v = this.getRawValue();
18514         
18515         if(this.multiple){
18516             v = this.getValue();
18517         }
18518         
18519         if(this.disabled || this.allowBlank || v.length){
18520             this.markValid();
18521             return true;
18522         }
18523         
18524         this.markInvalid();
18525         return false;
18526     },
18527     
18528     tickableInputEl : function()
18529     {
18530         if(!this.tickable || !this.editable){
18531             return this.inputEl();
18532         }
18533         
18534         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18535     },
18536     
18537     
18538     getAutoCreateTouchView : function()
18539     {
18540         var id = Roo.id();
18541         
18542         var cfg = {
18543             cls: 'form-group' //input-group
18544         };
18545         
18546         var input =  {
18547             tag: 'input',
18548             id : id,
18549             type : this.inputType,
18550             cls : 'form-control x-combo-noedit',
18551             autocomplete: 'new-password',
18552             placeholder : this.placeholder || '',
18553             readonly : true
18554         };
18555         
18556         if (this.name) {
18557             input.name = this.name;
18558         }
18559         
18560         if (this.size) {
18561             input.cls += ' input-' + this.size;
18562         }
18563         
18564         if (this.disabled) {
18565             input.disabled = true;
18566         }
18567         
18568         var inputblock = {
18569             cls : 'roo-combobox-wrap',
18570             cn : [
18571                 input
18572             ]
18573         };
18574         
18575         if(this.before){
18576             inputblock.cls += ' input-group';
18577             
18578             inputblock.cn.unshift({
18579                 tag :'span',
18580                 cls : 'input-group-addon input-group-prepend input-group-text',
18581                 html : this.before
18582             });
18583         }
18584         
18585         if(this.removable && !this.multiple){
18586             inputblock.cls += ' roo-removable';
18587             
18588             inputblock.cn.push({
18589                 tag: 'button',
18590                 html : 'x',
18591                 cls : 'roo-combo-removable-btn close'
18592             });
18593         }
18594
18595         if(this.hasFeedback && !this.allowBlank){
18596             
18597             inputblock.cls += ' has-feedback';
18598             
18599             inputblock.cn.push({
18600                 tag: 'span',
18601                 cls: 'glyphicon form-control-feedback'
18602             });
18603             
18604         }
18605         
18606         if (this.after) {
18607             
18608             inputblock.cls += (this.before) ? '' : ' input-group';
18609             
18610             inputblock.cn.push({
18611                 tag :'span',
18612                 cls : 'input-group-addon input-group-append input-group-text',
18613                 html : this.after
18614             });
18615         }
18616
18617         
18618         var ibwrap = inputblock;
18619         
18620         if(this.multiple){
18621             ibwrap = {
18622                 tag: 'ul',
18623                 cls: 'roo-select2-choices',
18624                 cn:[
18625                     {
18626                         tag: 'li',
18627                         cls: 'roo-select2-search-field',
18628                         cn: [
18629
18630                             inputblock
18631                         ]
18632                     }
18633                 ]
18634             };
18635         
18636             
18637         }
18638         
18639         var combobox = {
18640             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18641             cn: [
18642                 {
18643                     tag: 'input',
18644                     type : 'hidden',
18645                     cls: 'form-hidden-field'
18646                 },
18647                 ibwrap
18648             ]
18649         };
18650         
18651         if(!this.multiple && this.showToggleBtn){
18652             
18653             var caret = {
18654                 cls: 'caret'
18655             };
18656             
18657             if (this.caret != false) {
18658                 caret = {
18659                      tag: 'i',
18660                      cls: 'fa fa-' + this.caret
18661                 };
18662                 
18663             }
18664             
18665             combobox.cn.push({
18666                 tag :'span',
18667                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18668                 cn : [
18669                     Roo.bootstrap.version == 3 ? caret : '',
18670                     {
18671                         tag: 'span',
18672                         cls: 'combobox-clear',
18673                         cn  : [
18674                             {
18675                                 tag : 'i',
18676                                 cls: 'icon-remove'
18677                             }
18678                         ]
18679                     }
18680                 ]
18681
18682             })
18683         }
18684         
18685         if(this.multiple){
18686             combobox.cls += ' roo-select2-container-multi';
18687         }
18688         
18689         var required =  this.allowBlank ?  {
18690                     tag : 'i',
18691                     style: 'display: none'
18692                 } : {
18693                    tag : 'i',
18694                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18695                    tooltip : 'This field is required'
18696                 };
18697         
18698         var align = this.labelAlign || this.parentLabelAlign();
18699         
18700         if (align ==='left' && this.fieldLabel.length) {
18701
18702             cfg.cn = [
18703                 required,
18704                 {
18705                     tag: 'label',
18706                     cls : 'control-label col-form-label',
18707                     html : this.fieldLabel
18708
18709                 },
18710                 {
18711                     cls : 'roo-combobox-wrap ', 
18712                     cn: [
18713                         combobox
18714                     ]
18715                 }
18716             ];
18717             
18718             var labelCfg = cfg.cn[1];
18719             var contentCfg = cfg.cn[2];
18720             
18721
18722             if(this.indicatorpos == 'right'){
18723                 cfg.cn = [
18724                     {
18725                         tag: 'label',
18726                         'for' :  id,
18727                         cls : 'control-label col-form-label',
18728                         cn : [
18729                             {
18730                                 tag : 'span',
18731                                 html : this.fieldLabel
18732                             },
18733                             required
18734                         ]
18735                     },
18736                     {
18737                         cls : "roo-combobox-wrap ",
18738                         cn: [
18739                             combobox
18740                         ]
18741                     }
18742
18743                 ];
18744                 
18745                 labelCfg = cfg.cn[0];
18746                 contentCfg = cfg.cn[1];
18747             }
18748             
18749            
18750             
18751             if(this.labelWidth > 12){
18752                 labelCfg.style = "width: " + this.labelWidth + 'px';
18753             }
18754            
18755             if(this.labelWidth < 13 && this.labelmd == 0){
18756                 this.labelmd = this.labelWidth;
18757             }
18758             
18759             if(this.labellg > 0){
18760                 labelCfg.cls += ' col-lg-' + this.labellg;
18761                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18762             }
18763             
18764             if(this.labelmd > 0){
18765                 labelCfg.cls += ' col-md-' + this.labelmd;
18766                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18767             }
18768             
18769             if(this.labelsm > 0){
18770                 labelCfg.cls += ' col-sm-' + this.labelsm;
18771                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18772             }
18773             
18774             if(this.labelxs > 0){
18775                 labelCfg.cls += ' col-xs-' + this.labelxs;
18776                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18777             }
18778                 
18779                 
18780         } else if ( this.fieldLabel.length) {
18781             cfg.cn = [
18782                required,
18783                 {
18784                     tag: 'label',
18785                     cls : 'control-label',
18786                     html : this.fieldLabel
18787
18788                 },
18789                 {
18790                     cls : '', 
18791                     cn: [
18792                         combobox
18793                     ]
18794                 }
18795             ];
18796             
18797             if(this.indicatorpos == 'right'){
18798                 cfg.cn = [
18799                     {
18800                         tag: 'label',
18801                         cls : 'control-label',
18802                         html : this.fieldLabel,
18803                         cn : [
18804                             required
18805                         ]
18806                     },
18807                     {
18808                         cls : '', 
18809                         cn: [
18810                             combobox
18811                         ]
18812                     }
18813                 ];
18814             }
18815         } else {
18816             cfg.cn = combobox;    
18817         }
18818         
18819         
18820         var settings = this;
18821         
18822         ['xs','sm','md','lg'].map(function(size){
18823             if (settings[size]) {
18824                 cfg.cls += ' col-' + size + '-' + settings[size];
18825             }
18826         });
18827         
18828         return cfg;
18829     },
18830     
18831     initTouchView : function()
18832     {
18833         this.renderTouchView();
18834         
18835         this.touchViewEl.on('scroll', function(){
18836             this.el.dom.scrollTop = 0;
18837         }, this);
18838         
18839         this.originalValue = this.getValue();
18840         
18841         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18842         
18843         this.inputEl().on("click", this.showTouchView, this);
18844         if (this.triggerEl) {
18845             this.triggerEl.on("click", this.showTouchView, this);
18846         }
18847         
18848         
18849         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18850         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18851         
18852         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18853         
18854         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18855         this.store.on('load', this.onTouchViewLoad, this);
18856         this.store.on('loadexception', this.onTouchViewLoadException, this);
18857         
18858         if(this.hiddenName){
18859             
18860             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18861             
18862             this.hiddenField.dom.value =
18863                 this.hiddenValue !== undefined ? this.hiddenValue :
18864                 this.value !== undefined ? this.value : '';
18865         
18866             this.el.dom.removeAttribute('name');
18867             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18868         }
18869         
18870         if(this.multiple){
18871             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18872             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18873         }
18874         
18875         if(this.removable && !this.multiple){
18876             var close = this.closeTriggerEl();
18877             if(close){
18878                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18879                 close.on('click', this.removeBtnClick, this, close);
18880             }
18881         }
18882         /*
18883          * fix the bug in Safari iOS8
18884          */
18885         this.inputEl().on("focus", function(e){
18886             document.activeElement.blur();
18887         }, this);
18888         
18889         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18890         
18891         return;
18892         
18893         
18894     },
18895     
18896     renderTouchView : function()
18897     {
18898         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18899         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900         
18901         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18902         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18903         
18904         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18905         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18906         this.touchViewBodyEl.setStyle('overflow', 'auto');
18907         
18908         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18909         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18910         
18911         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18912         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18913         
18914     },
18915     
18916     showTouchView : function()
18917     {
18918         if(this.disabled){
18919             return;
18920         }
18921         
18922         this.touchViewHeaderEl.hide();
18923
18924         if(this.modalTitle.length){
18925             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18926             this.touchViewHeaderEl.show();
18927         }
18928
18929         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18930         this.touchViewEl.show();
18931
18932         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18933         
18934         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18935         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18936
18937         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18938
18939         if(this.modalTitle.length){
18940             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18941         }
18942         
18943         this.touchViewBodyEl.setHeight(bodyHeight);
18944
18945         if(this.animate){
18946             var _this = this;
18947             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18948         }else{
18949             this.touchViewEl.addClass(['in','show']);
18950         }
18951         
18952         if(this._touchViewMask){
18953             Roo.get(document.body).addClass("x-body-masked");
18954             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18955             this._touchViewMask.setStyle('z-index', 10000);
18956             this._touchViewMask.addClass('show');
18957         }
18958         
18959         this.doTouchViewQuery();
18960         
18961     },
18962     
18963     hideTouchView : function()
18964     {
18965         this.touchViewEl.removeClass(['in','show']);
18966
18967         if(this.animate){
18968             var _this = this;
18969             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18970         }else{
18971             this.touchViewEl.setStyle('display', 'none');
18972         }
18973         
18974         if(this._touchViewMask){
18975             this._touchViewMask.removeClass('show');
18976             Roo.get(document.body).removeClass("x-body-masked");
18977         }
18978     },
18979     
18980     setTouchViewValue : function()
18981     {
18982         if(this.multiple){
18983             this.clearItem();
18984         
18985             var _this = this;
18986
18987             Roo.each(this.tickItems, function(o){
18988                 this.addItem(o);
18989             }, this);
18990         }
18991         
18992         this.hideTouchView();
18993     },
18994     
18995     doTouchViewQuery : function()
18996     {
18997         var qe = {
18998             query: '',
18999             forceAll: true,
19000             combo: this,
19001             cancel:false
19002         };
19003         
19004         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19005             return false;
19006         }
19007         
19008         if(!this.alwaysQuery || this.mode == 'local'){
19009             this.onTouchViewLoad();
19010             return;
19011         }
19012         
19013         this.store.load();
19014     },
19015     
19016     onTouchViewBeforeLoad : function(combo,opts)
19017     {
19018         return;
19019     },
19020
19021     // private
19022     onTouchViewLoad : function()
19023     {
19024         if(this.store.getCount() < 1){
19025             this.onTouchViewEmptyResults();
19026             return;
19027         }
19028         
19029         this.clearTouchView();
19030         
19031         var rawValue = this.getRawValue();
19032         
19033         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19034         
19035         this.tickItems = [];
19036         
19037         this.store.data.each(function(d, rowIndex){
19038             var row = this.touchViewListGroup.createChild(template);
19039             
19040             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19041                 row.addClass(d.data.cls);
19042             }
19043             
19044             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19045                 var cfg = {
19046                     data : d.data,
19047                     html : d.data[this.displayField]
19048                 };
19049                 
19050                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19051                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19052                 }
19053             }
19054             row.removeClass('selected');
19055             if(!this.multiple && this.valueField &&
19056                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19057             {
19058                 // radio buttons..
19059                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19060                 row.addClass('selected');
19061             }
19062             
19063             if(this.multiple && this.valueField &&
19064                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19065             {
19066                 
19067                 // checkboxes...
19068                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19069                 this.tickItems.push(d.data);
19070             }
19071             
19072             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19073             
19074         }, this);
19075         
19076         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19077         
19078         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19079
19080         if(this.modalTitle.length){
19081             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19082         }
19083
19084         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19085         
19086         if(this.mobile_restrict_height && listHeight < bodyHeight){
19087             this.touchViewBodyEl.setHeight(listHeight);
19088         }
19089         
19090         var _this = this;
19091         
19092         if(firstChecked && listHeight > bodyHeight){
19093             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19094         }
19095         
19096     },
19097     
19098     onTouchViewLoadException : function()
19099     {
19100         this.hideTouchView();
19101     },
19102     
19103     onTouchViewEmptyResults : function()
19104     {
19105         this.clearTouchView();
19106         
19107         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19108         
19109         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19110         
19111     },
19112     
19113     clearTouchView : function()
19114     {
19115         this.touchViewListGroup.dom.innerHTML = '';
19116     },
19117     
19118     onTouchViewClick : function(e, el, o)
19119     {
19120         e.preventDefault();
19121         
19122         var row = o.row;
19123         var rowIndex = o.rowIndex;
19124         
19125         var r = this.store.getAt(rowIndex);
19126         
19127         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19128             
19129             if(!this.multiple){
19130                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19131                     c.dom.removeAttribute('checked');
19132                 }, this);
19133
19134                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19135
19136                 this.setFromData(r.data);
19137
19138                 var close = this.closeTriggerEl();
19139
19140                 if(close){
19141                     close.show();
19142                 }
19143
19144                 this.hideTouchView();
19145
19146                 this.fireEvent('select', this, r, rowIndex);
19147
19148                 return;
19149             }
19150
19151             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19152                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19153                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19154                 return;
19155             }
19156
19157             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19158             this.addItem(r.data);
19159             this.tickItems.push(r.data);
19160         }
19161     },
19162     
19163     getAutoCreateNativeIOS : function()
19164     {
19165         var cfg = {
19166             cls: 'form-group' //input-group,
19167         };
19168         
19169         var combobox =  {
19170             tag: 'select',
19171             cls : 'roo-ios-select'
19172         };
19173         
19174         if (this.name) {
19175             combobox.name = this.name;
19176         }
19177         
19178         if (this.disabled) {
19179             combobox.disabled = true;
19180         }
19181         
19182         var settings = this;
19183         
19184         ['xs','sm','md','lg'].map(function(size){
19185             if (settings[size]) {
19186                 cfg.cls += ' col-' + size + '-' + settings[size];
19187             }
19188         });
19189         
19190         cfg.cn = combobox;
19191         
19192         return cfg;
19193         
19194     },
19195     
19196     initIOSView : function()
19197     {
19198         this.store.on('load', this.onIOSViewLoad, this);
19199         
19200         return;
19201     },
19202     
19203     onIOSViewLoad : function()
19204     {
19205         if(this.store.getCount() < 1){
19206             return;
19207         }
19208         
19209         this.clearIOSView();
19210         
19211         if(this.allowBlank) {
19212             
19213             var default_text = '-- SELECT --';
19214             
19215             if(this.placeholder.length){
19216                 default_text = this.placeholder;
19217             }
19218             
19219             if(this.emptyTitle.length){
19220                 default_text += ' - ' + this.emptyTitle + ' -';
19221             }
19222             
19223             var opt = this.inputEl().createChild({
19224                 tag: 'option',
19225                 value : 0,
19226                 html : default_text
19227             });
19228             
19229             var o = {};
19230             o[this.valueField] = 0;
19231             o[this.displayField] = default_text;
19232             
19233             this.ios_options.push({
19234                 data : o,
19235                 el : opt
19236             });
19237             
19238         }
19239         
19240         this.store.data.each(function(d, rowIndex){
19241             
19242             var html = '';
19243             
19244             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19245                 html = d.data[this.displayField];
19246             }
19247             
19248             var value = '';
19249             
19250             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19251                 value = d.data[this.valueField];
19252             }
19253             
19254             var option = {
19255                 tag: 'option',
19256                 value : value,
19257                 html : html
19258             };
19259             
19260             if(this.value == d.data[this.valueField]){
19261                 option['selected'] = true;
19262             }
19263             
19264             var opt = this.inputEl().createChild(option);
19265             
19266             this.ios_options.push({
19267                 data : d.data,
19268                 el : opt
19269             });
19270             
19271         }, this);
19272         
19273         this.inputEl().on('change', function(){
19274            this.fireEvent('select', this);
19275         }, this);
19276         
19277     },
19278     
19279     clearIOSView: function()
19280     {
19281         this.inputEl().dom.innerHTML = '';
19282         
19283         this.ios_options = [];
19284     },
19285     
19286     setIOSValue: function(v)
19287     {
19288         this.value = v;
19289         
19290         if(!this.ios_options){
19291             return;
19292         }
19293         
19294         Roo.each(this.ios_options, function(opts){
19295            
19296            opts.el.dom.removeAttribute('selected');
19297            
19298            if(opts.data[this.valueField] != v){
19299                return;
19300            }
19301            
19302            opts.el.dom.setAttribute('selected', true);
19303            
19304         }, this);
19305     }
19306
19307     /** 
19308     * @cfg {Boolean} grow 
19309     * @hide 
19310     */
19311     /** 
19312     * @cfg {Number} growMin 
19313     * @hide 
19314     */
19315     /** 
19316     * @cfg {Number} growMax 
19317     * @hide 
19318     */
19319     /**
19320      * @hide
19321      * @method autoSize
19322      */
19323 });
19324
19325 Roo.apply(Roo.bootstrap.ComboBox,  {
19326     
19327     header : {
19328         tag: 'div',
19329         cls: 'modal-header',
19330         cn: [
19331             {
19332                 tag: 'h4',
19333                 cls: 'modal-title'
19334             }
19335         ]
19336     },
19337     
19338     body : {
19339         tag: 'div',
19340         cls: 'modal-body',
19341         cn: [
19342             {
19343                 tag: 'ul',
19344                 cls: 'list-group'
19345             }
19346         ]
19347     },
19348     
19349     listItemRadio : {
19350         tag: 'li',
19351         cls: 'list-group-item',
19352         cn: [
19353             {
19354                 tag: 'span',
19355                 cls: 'roo-combobox-list-group-item-value'
19356             },
19357             {
19358                 tag: 'div',
19359                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19360                 cn: [
19361                     {
19362                         tag: 'input',
19363                         type: 'radio'
19364                     },
19365                     {
19366                         tag: 'label'
19367                     }
19368                 ]
19369             }
19370         ]
19371     },
19372     
19373     listItemCheckbox : {
19374         tag: 'li',
19375         cls: 'list-group-item',
19376         cn: [
19377             {
19378                 tag: 'span',
19379                 cls: 'roo-combobox-list-group-item-value'
19380             },
19381             {
19382                 tag: 'div',
19383                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19384                 cn: [
19385                     {
19386                         tag: 'input',
19387                         type: 'checkbox'
19388                     },
19389                     {
19390                         tag: 'label'
19391                     }
19392                 ]
19393             }
19394         ]
19395     },
19396     
19397     emptyResult : {
19398         tag: 'div',
19399         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19400     },
19401     
19402     footer : {
19403         tag: 'div',
19404         cls: 'modal-footer',
19405         cn: [
19406             {
19407                 tag: 'div',
19408                 cls: 'row',
19409                 cn: [
19410                     {
19411                         tag: 'div',
19412                         cls: 'col-xs-6 text-left',
19413                         cn: {
19414                             tag: 'button',
19415                             cls: 'btn btn-danger roo-touch-view-cancel',
19416                             html: 'Cancel'
19417                         }
19418                     },
19419                     {
19420                         tag: 'div',
19421                         cls: 'col-xs-6 text-right',
19422                         cn: {
19423                             tag: 'button',
19424                             cls: 'btn btn-success roo-touch-view-ok',
19425                             html: 'OK'
19426                         }
19427                     }
19428                 ]
19429             }
19430         ]
19431         
19432     }
19433 });
19434
19435 Roo.apply(Roo.bootstrap.ComboBox,  {
19436     
19437     touchViewTemplate : {
19438         tag: 'div',
19439         cls: 'modal fade roo-combobox-touch-view',
19440         cn: [
19441             {
19442                 tag: 'div',
19443                 cls: 'modal-dialog',
19444                 style : 'position:fixed', // we have to fix position....
19445                 cn: [
19446                     {
19447                         tag: 'div',
19448                         cls: 'modal-content',
19449                         cn: [
19450                             Roo.bootstrap.ComboBox.header,
19451                             Roo.bootstrap.ComboBox.body,
19452                             Roo.bootstrap.ComboBox.footer
19453                         ]
19454                     }
19455                 ]
19456             }
19457         ]
19458     }
19459 });/*
19460  * Based on:
19461  * Ext JS Library 1.1.1
19462  * Copyright(c) 2006-2007, Ext JS, LLC.
19463  *
19464  * Originally Released Under LGPL - original licence link has changed is not relivant.
19465  *
19466  * Fork - LGPL
19467  * <script type="text/javascript">
19468  */
19469
19470 /**
19471  * @class Roo.View
19472  * @extends Roo.util.Observable
19473  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19474  * This class also supports single and multi selection modes. <br>
19475  * Create a data model bound view:
19476  <pre><code>
19477  var store = new Roo.data.Store(...);
19478
19479  var view = new Roo.View({
19480     el : "my-element",
19481     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19482  
19483     singleSelect: true,
19484     selectedClass: "ydataview-selected",
19485     store: store
19486  });
19487
19488  // listen for node click?
19489  view.on("click", function(vw, index, node, e){
19490  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19491  });
19492
19493  // load XML data
19494  dataModel.load("foobar.xml");
19495  </code></pre>
19496  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19497  * <br><br>
19498  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19499  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19500  * 
19501  * Note: old style constructor is still suported (container, template, config)
19502  * 
19503  * @constructor
19504  * Create a new View
19505  * @param {Object} config The config object
19506  * 
19507  */
19508 Roo.View = function(config, depreciated_tpl, depreciated_config){
19509     
19510     this.parent = false;
19511     
19512     if (typeof(depreciated_tpl) == 'undefined') {
19513         // new way.. - universal constructor.
19514         Roo.apply(this, config);
19515         this.el  = Roo.get(this.el);
19516     } else {
19517         // old format..
19518         this.el  = Roo.get(config);
19519         this.tpl = depreciated_tpl;
19520         Roo.apply(this, depreciated_config);
19521     }
19522     this.wrapEl  = this.el.wrap().wrap();
19523     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19524     
19525     
19526     if(typeof(this.tpl) == "string"){
19527         this.tpl = new Roo.Template(this.tpl);
19528     } else {
19529         // support xtype ctors..
19530         this.tpl = new Roo.factory(this.tpl, Roo);
19531     }
19532     
19533     
19534     this.tpl.compile();
19535     
19536     /** @private */
19537     this.addEvents({
19538         /**
19539          * @event beforeclick
19540          * Fires before a click is processed. Returns false to cancel the default action.
19541          * @param {Roo.View} this
19542          * @param {Number} index The index of the target node
19543          * @param {HTMLElement} node The target node
19544          * @param {Roo.EventObject} e The raw event object
19545          */
19546             "beforeclick" : true,
19547         /**
19548          * @event click
19549          * Fires when a template node is clicked.
19550          * @param {Roo.View} this
19551          * @param {Number} index The index of the target node
19552          * @param {HTMLElement} node The target node
19553          * @param {Roo.EventObject} e The raw event object
19554          */
19555             "click" : true,
19556         /**
19557          * @event dblclick
19558          * Fires when a template node is double clicked.
19559          * @param {Roo.View} this
19560          * @param {Number} index The index of the target node
19561          * @param {HTMLElement} node The target node
19562          * @param {Roo.EventObject} e The raw event object
19563          */
19564             "dblclick" : true,
19565         /**
19566          * @event contextmenu
19567          * Fires when a template node is right clicked.
19568          * @param {Roo.View} this
19569          * @param {Number} index The index of the target node
19570          * @param {HTMLElement} node The target node
19571          * @param {Roo.EventObject} e The raw event object
19572          */
19573             "contextmenu" : true,
19574         /**
19575          * @event selectionchange
19576          * Fires when the selected nodes change.
19577          * @param {Roo.View} this
19578          * @param {Array} selections Array of the selected nodes
19579          */
19580             "selectionchange" : true,
19581     
19582         /**
19583          * @event beforeselect
19584          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19585          * @param {Roo.View} this
19586          * @param {HTMLElement} node The node to be selected
19587          * @param {Array} selections Array of currently selected nodes
19588          */
19589             "beforeselect" : true,
19590         /**
19591          * @event preparedata
19592          * Fires on every row to render, to allow you to change the data.
19593          * @param {Roo.View} this
19594          * @param {Object} data to be rendered (change this)
19595          */
19596           "preparedata" : true
19597           
19598           
19599         });
19600
19601
19602
19603     this.el.on({
19604         "click": this.onClick,
19605         "dblclick": this.onDblClick,
19606         "contextmenu": this.onContextMenu,
19607         scope:this
19608     });
19609
19610     this.selections = [];
19611     this.nodes = [];
19612     this.cmp = new Roo.CompositeElementLite([]);
19613     if(this.store){
19614         this.store = Roo.factory(this.store, Roo.data);
19615         this.setStore(this.store, true);
19616     }
19617     
19618     if ( this.footer && this.footer.xtype) {
19619            
19620          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19621         
19622         this.footer.dataSource = this.store;
19623         this.footer.container = fctr;
19624         this.footer = Roo.factory(this.footer, Roo);
19625         fctr.insertFirst(this.el);
19626         
19627         // this is a bit insane - as the paging toolbar seems to detach the el..
19628 //        dom.parentNode.parentNode.parentNode
19629          // they get detached?
19630     }
19631     
19632     
19633     Roo.View.superclass.constructor.call(this);
19634     
19635     
19636 };
19637
19638 Roo.extend(Roo.View, Roo.util.Observable, {
19639     
19640      /**
19641      * @cfg {Roo.data.Store} store Data store to load data from.
19642      */
19643     store : false,
19644     
19645     /**
19646      * @cfg {String|Roo.Element} el The container element.
19647      */
19648     el : '',
19649     
19650     /**
19651      * @cfg {String|Roo.Template} tpl The template used by this View 
19652      */
19653     tpl : false,
19654     /**
19655      * @cfg {String} dataName the named area of the template to use as the data area
19656      *                          Works with domtemplates roo-name="name"
19657      */
19658     dataName: false,
19659     /**
19660      * @cfg {String} selectedClass The css class to add to selected nodes
19661      */
19662     selectedClass : "x-view-selected",
19663      /**
19664      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19665      */
19666     emptyText : "",
19667     
19668     /**
19669      * @cfg {String} text to display on mask (default Loading)
19670      */
19671     mask : false,
19672     /**
19673      * @cfg {Boolean} multiSelect Allow multiple selection
19674      */
19675     multiSelect : false,
19676     /**
19677      * @cfg {Boolean} singleSelect Allow single selection
19678      */
19679     singleSelect:  false,
19680     
19681     /**
19682      * @cfg {Boolean} toggleSelect - selecting 
19683      */
19684     toggleSelect : false,
19685     
19686     /**
19687      * @cfg {Boolean} tickable - selecting 
19688      */
19689     tickable : false,
19690     
19691     /**
19692      * Returns the element this view is bound to.
19693      * @return {Roo.Element}
19694      */
19695     getEl : function(){
19696         return this.wrapEl;
19697     },
19698     
19699     
19700
19701     /**
19702      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19703      */
19704     refresh : function(){
19705         //Roo.log('refresh');
19706         var t = this.tpl;
19707         
19708         // if we are using something like 'domtemplate', then
19709         // the what gets used is:
19710         // t.applySubtemplate(NAME, data, wrapping data..)
19711         // the outer template then get' applied with
19712         //     the store 'extra data'
19713         // and the body get's added to the
19714         //      roo-name="data" node?
19715         //      <span class='roo-tpl-{name}'></span> ?????
19716         
19717         
19718         
19719         this.clearSelections();
19720         this.el.update("");
19721         var html = [];
19722         var records = this.store.getRange();
19723         if(records.length < 1) {
19724             
19725             // is this valid??  = should it render a template??
19726             
19727             this.el.update(this.emptyText);
19728             return;
19729         }
19730         var el = this.el;
19731         if (this.dataName) {
19732             this.el.update(t.apply(this.store.meta)); //????
19733             el = this.el.child('.roo-tpl-' + this.dataName);
19734         }
19735         
19736         for(var i = 0, len = records.length; i < len; i++){
19737             var data = this.prepareData(records[i].data, i, records[i]);
19738             this.fireEvent("preparedata", this, data, i, records[i]);
19739             
19740             var d = Roo.apply({}, data);
19741             
19742             if(this.tickable){
19743                 Roo.apply(d, {'roo-id' : Roo.id()});
19744                 
19745                 var _this = this;
19746             
19747                 Roo.each(this.parent.item, function(item){
19748                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19749                         return;
19750                     }
19751                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19752                 });
19753             }
19754             
19755             html[html.length] = Roo.util.Format.trim(
19756                 this.dataName ?
19757                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19758                     t.apply(d)
19759             );
19760         }
19761         
19762         
19763         
19764         el.update(html.join(""));
19765         this.nodes = el.dom.childNodes;
19766         this.updateIndexes(0);
19767     },
19768     
19769
19770     /**
19771      * Function to override to reformat the data that is sent to
19772      * the template for each node.
19773      * DEPRICATED - use the preparedata event handler.
19774      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19775      * a JSON object for an UpdateManager bound view).
19776      */
19777     prepareData : function(data, index, record)
19778     {
19779         this.fireEvent("preparedata", this, data, index, record);
19780         return data;
19781     },
19782
19783     onUpdate : function(ds, record){
19784         // Roo.log('on update');   
19785         this.clearSelections();
19786         var index = this.store.indexOf(record);
19787         var n = this.nodes[index];
19788         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19789         n.parentNode.removeChild(n);
19790         this.updateIndexes(index, index);
19791     },
19792
19793     
19794     
19795 // --------- FIXME     
19796     onAdd : function(ds, records, index)
19797     {
19798         //Roo.log(['on Add', ds, records, index] );        
19799         this.clearSelections();
19800         if(this.nodes.length == 0){
19801             this.refresh();
19802             return;
19803         }
19804         var n = this.nodes[index];
19805         for(var i = 0, len = records.length; i < len; i++){
19806             var d = this.prepareData(records[i].data, i, records[i]);
19807             if(n){
19808                 this.tpl.insertBefore(n, d);
19809             }else{
19810                 
19811                 this.tpl.append(this.el, d);
19812             }
19813         }
19814         this.updateIndexes(index);
19815     },
19816
19817     onRemove : function(ds, record, index){
19818        // Roo.log('onRemove');
19819         this.clearSelections();
19820         var el = this.dataName  ?
19821             this.el.child('.roo-tpl-' + this.dataName) :
19822             this.el; 
19823         
19824         el.dom.removeChild(this.nodes[index]);
19825         this.updateIndexes(index);
19826     },
19827
19828     /**
19829      * Refresh an individual node.
19830      * @param {Number} index
19831      */
19832     refreshNode : function(index){
19833         this.onUpdate(this.store, this.store.getAt(index));
19834     },
19835
19836     updateIndexes : function(startIndex, endIndex){
19837         var ns = this.nodes;
19838         startIndex = startIndex || 0;
19839         endIndex = endIndex || ns.length - 1;
19840         for(var i = startIndex; i <= endIndex; i++){
19841             ns[i].nodeIndex = i;
19842         }
19843     },
19844
19845     /**
19846      * Changes the data store this view uses and refresh the view.
19847      * @param {Store} store
19848      */
19849     setStore : function(store, initial){
19850         if(!initial && this.store){
19851             this.store.un("datachanged", this.refresh);
19852             this.store.un("add", this.onAdd);
19853             this.store.un("remove", this.onRemove);
19854             this.store.un("update", this.onUpdate);
19855             this.store.un("clear", this.refresh);
19856             this.store.un("beforeload", this.onBeforeLoad);
19857             this.store.un("load", this.onLoad);
19858             this.store.un("loadexception", this.onLoad);
19859         }
19860         if(store){
19861           
19862             store.on("datachanged", this.refresh, this);
19863             store.on("add", this.onAdd, this);
19864             store.on("remove", this.onRemove, this);
19865             store.on("update", this.onUpdate, this);
19866             store.on("clear", this.refresh, this);
19867             store.on("beforeload", this.onBeforeLoad, this);
19868             store.on("load", this.onLoad, this);
19869             store.on("loadexception", this.onLoad, this);
19870         }
19871         
19872         if(store){
19873             this.refresh();
19874         }
19875     },
19876     /**
19877      * onbeforeLoad - masks the loading area.
19878      *
19879      */
19880     onBeforeLoad : function(store,opts)
19881     {
19882          //Roo.log('onBeforeLoad');   
19883         if (!opts.add) {
19884             this.el.update("");
19885         }
19886         this.el.mask(this.mask ? this.mask : "Loading" ); 
19887     },
19888     onLoad : function ()
19889     {
19890         this.el.unmask();
19891     },
19892     
19893
19894     /**
19895      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19896      * @param {HTMLElement} node
19897      * @return {HTMLElement} The template node
19898      */
19899     findItemFromChild : function(node){
19900         var el = this.dataName  ?
19901             this.el.child('.roo-tpl-' + this.dataName,true) :
19902             this.el.dom; 
19903         
19904         if(!node || node.parentNode == el){
19905                     return node;
19906             }
19907             var p = node.parentNode;
19908             while(p && p != el){
19909             if(p.parentNode == el){
19910                 return p;
19911             }
19912             p = p.parentNode;
19913         }
19914             return null;
19915     },
19916
19917     /** @ignore */
19918     onClick : function(e){
19919         var item = this.findItemFromChild(e.getTarget());
19920         if(item){
19921             var index = this.indexOf(item);
19922             if(this.onItemClick(item, index, e) !== false){
19923                 this.fireEvent("click", this, index, item, e);
19924             }
19925         }else{
19926             this.clearSelections();
19927         }
19928     },
19929
19930     /** @ignore */
19931     onContextMenu : function(e){
19932         var item = this.findItemFromChild(e.getTarget());
19933         if(item){
19934             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19935         }
19936     },
19937
19938     /** @ignore */
19939     onDblClick : function(e){
19940         var item = this.findItemFromChild(e.getTarget());
19941         if(item){
19942             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19943         }
19944     },
19945
19946     onItemClick : function(item, index, e)
19947     {
19948         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19949             return false;
19950         }
19951         if (this.toggleSelect) {
19952             var m = this.isSelected(item) ? 'unselect' : 'select';
19953             //Roo.log(m);
19954             var _t = this;
19955             _t[m](item, true, false);
19956             return true;
19957         }
19958         if(this.multiSelect || this.singleSelect){
19959             if(this.multiSelect && e.shiftKey && this.lastSelection){
19960                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19961             }else{
19962                 this.select(item, this.multiSelect && e.ctrlKey);
19963                 this.lastSelection = item;
19964             }
19965             
19966             if(!this.tickable){
19967                 e.preventDefault();
19968             }
19969             
19970         }
19971         return true;
19972     },
19973
19974     /**
19975      * Get the number of selected nodes.
19976      * @return {Number}
19977      */
19978     getSelectionCount : function(){
19979         return this.selections.length;
19980     },
19981
19982     /**
19983      * Get the currently selected nodes.
19984      * @return {Array} An array of HTMLElements
19985      */
19986     getSelectedNodes : function(){
19987         return this.selections;
19988     },
19989
19990     /**
19991      * Get the indexes of the selected nodes.
19992      * @return {Array}
19993      */
19994     getSelectedIndexes : function(){
19995         var indexes = [], s = this.selections;
19996         for(var i = 0, len = s.length; i < len; i++){
19997             indexes.push(s[i].nodeIndex);
19998         }
19999         return indexes;
20000     },
20001
20002     /**
20003      * Clear all selections
20004      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20005      */
20006     clearSelections : function(suppressEvent){
20007         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20008             this.cmp.elements = this.selections;
20009             this.cmp.removeClass(this.selectedClass);
20010             this.selections = [];
20011             if(!suppressEvent){
20012                 this.fireEvent("selectionchange", this, this.selections);
20013             }
20014         }
20015     },
20016
20017     /**
20018      * Returns true if the passed node is selected
20019      * @param {HTMLElement/Number} node The node or node index
20020      * @return {Boolean}
20021      */
20022     isSelected : function(node){
20023         var s = this.selections;
20024         if(s.length < 1){
20025             return false;
20026         }
20027         node = this.getNode(node);
20028         return s.indexOf(node) !== -1;
20029     },
20030
20031     /**
20032      * Selects nodes.
20033      * @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
20034      * @param {Boolean} keepExisting (optional) true to keep existing selections
20035      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20036      */
20037     select : function(nodeInfo, keepExisting, suppressEvent){
20038         if(nodeInfo instanceof Array){
20039             if(!keepExisting){
20040                 this.clearSelections(true);
20041             }
20042             for(var i = 0, len = nodeInfo.length; i < len; i++){
20043                 this.select(nodeInfo[i], true, true);
20044             }
20045             return;
20046         } 
20047         var node = this.getNode(nodeInfo);
20048         if(!node || this.isSelected(node)){
20049             return; // already selected.
20050         }
20051         if(!keepExisting){
20052             this.clearSelections(true);
20053         }
20054         
20055         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20056             Roo.fly(node).addClass(this.selectedClass);
20057             this.selections.push(node);
20058             if(!suppressEvent){
20059                 this.fireEvent("selectionchange", this, this.selections);
20060             }
20061         }
20062         
20063         
20064     },
20065       /**
20066      * Unselects nodes.
20067      * @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
20068      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20069      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20070      */
20071     unselect : function(nodeInfo, keepExisting, suppressEvent)
20072     {
20073         if(nodeInfo instanceof Array){
20074             Roo.each(this.selections, function(s) {
20075                 this.unselect(s, nodeInfo);
20076             }, this);
20077             return;
20078         }
20079         var node = this.getNode(nodeInfo);
20080         if(!node || !this.isSelected(node)){
20081             //Roo.log("not selected");
20082             return; // not selected.
20083         }
20084         // fireevent???
20085         var ns = [];
20086         Roo.each(this.selections, function(s) {
20087             if (s == node ) {
20088                 Roo.fly(node).removeClass(this.selectedClass);
20089
20090                 return;
20091             }
20092             ns.push(s);
20093         },this);
20094         
20095         this.selections= ns;
20096         this.fireEvent("selectionchange", this, this.selections);
20097     },
20098
20099     /**
20100      * Gets a template node.
20101      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20102      * @return {HTMLElement} The node or null if it wasn't found
20103      */
20104     getNode : function(nodeInfo){
20105         if(typeof nodeInfo == "string"){
20106             return document.getElementById(nodeInfo);
20107         }else if(typeof nodeInfo == "number"){
20108             return this.nodes[nodeInfo];
20109         }
20110         return nodeInfo;
20111     },
20112
20113     /**
20114      * Gets a range template nodes.
20115      * @param {Number} startIndex
20116      * @param {Number} endIndex
20117      * @return {Array} An array of nodes
20118      */
20119     getNodes : function(start, end){
20120         var ns = this.nodes;
20121         start = start || 0;
20122         end = typeof end == "undefined" ? ns.length - 1 : end;
20123         var nodes = [];
20124         if(start <= end){
20125             for(var i = start; i <= end; i++){
20126                 nodes.push(ns[i]);
20127             }
20128         } else{
20129             for(var i = start; i >= end; i--){
20130                 nodes.push(ns[i]);
20131             }
20132         }
20133         return nodes;
20134     },
20135
20136     /**
20137      * Finds the index of the passed node
20138      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20139      * @return {Number} The index of the node or -1
20140      */
20141     indexOf : function(node){
20142         node = this.getNode(node);
20143         if(typeof node.nodeIndex == "number"){
20144             return node.nodeIndex;
20145         }
20146         var ns = this.nodes;
20147         for(var i = 0, len = ns.length; i < len; i++){
20148             if(ns[i] == node){
20149                 return i;
20150             }
20151         }
20152         return -1;
20153     }
20154 });
20155 /*
20156  * - LGPL
20157  *
20158  * based on jquery fullcalendar
20159  * 
20160  */
20161
20162 Roo.bootstrap = Roo.bootstrap || {};
20163 /**
20164  * @class Roo.bootstrap.Calendar
20165  * @extends Roo.bootstrap.Component
20166  * Bootstrap Calendar class
20167  * @cfg {Boolean} loadMask (true|false) default false
20168  * @cfg {Object} header generate the user specific header of the calendar, default false
20169
20170  * @constructor
20171  * Create a new Container
20172  * @param {Object} config The config object
20173  */
20174
20175
20176
20177 Roo.bootstrap.Calendar = function(config){
20178     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20179      this.addEvents({
20180         /**
20181              * @event select
20182              * Fires when a date is selected
20183              * @param {DatePicker} this
20184              * @param {Date} date The selected date
20185              */
20186         'select': true,
20187         /**
20188              * @event monthchange
20189              * Fires when the displayed month changes 
20190              * @param {DatePicker} this
20191              * @param {Date} date The selected month
20192              */
20193         'monthchange': true,
20194         /**
20195              * @event evententer
20196              * Fires when mouse over an event
20197              * @param {Calendar} this
20198              * @param {event} Event
20199              */
20200         'evententer': true,
20201         /**
20202              * @event eventleave
20203              * Fires when the mouse leaves an
20204              * @param {Calendar} this
20205              * @param {event}
20206              */
20207         'eventleave': true,
20208         /**
20209              * @event eventclick
20210              * Fires when the mouse click an
20211              * @param {Calendar} this
20212              * @param {event}
20213              */
20214         'eventclick': true
20215         
20216     });
20217
20218 };
20219
20220 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20221     
20222           /**
20223      * @cfg {Roo.data.Store} store
20224      * The data source for the calendar
20225      */
20226         store : false,
20227      /**
20228      * @cfg {Number} startDay
20229      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20230      */
20231     startDay : 0,
20232     
20233     loadMask : false,
20234     
20235     header : false,
20236       
20237     getAutoCreate : function(){
20238         
20239         
20240         var fc_button = function(name, corner, style, content ) {
20241             return Roo.apply({},{
20242                 tag : 'span',
20243                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20244                          (corner.length ?
20245                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20246                             ''
20247                         ),
20248                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20249                 unselectable: 'on'
20250             });
20251         };
20252         
20253         var header = {};
20254         
20255         if(!this.header){
20256             header = {
20257                 tag : 'table',
20258                 cls : 'fc-header',
20259                 style : 'width:100%',
20260                 cn : [
20261                     {
20262                         tag: 'tr',
20263                         cn : [
20264                             {
20265                                 tag : 'td',
20266                                 cls : 'fc-header-left',
20267                                 cn : [
20268                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20269                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20270                                     { tag: 'span', cls: 'fc-header-space' },
20271                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20272
20273
20274                                 ]
20275                             },
20276
20277                             {
20278                                 tag : 'td',
20279                                 cls : 'fc-header-center',
20280                                 cn : [
20281                                     {
20282                                         tag: 'span',
20283                                         cls: 'fc-header-title',
20284                                         cn : {
20285                                             tag: 'H2',
20286                                             html : 'month / year'
20287                                         }
20288                                     }
20289
20290                                 ]
20291                             },
20292                             {
20293                                 tag : 'td',
20294                                 cls : 'fc-header-right',
20295                                 cn : [
20296                               /*      fc_button('month', 'left', '', 'month' ),
20297                                     fc_button('week', '', '', 'week' ),
20298                                     fc_button('day', 'right', '', 'day' )
20299                                 */    
20300
20301                                 ]
20302                             }
20303
20304                         ]
20305                     }
20306                 ]
20307             };
20308         }
20309         
20310         header = this.header;
20311         
20312        
20313         var cal_heads = function() {
20314             var ret = [];
20315             // fixme - handle this.
20316             
20317             for (var i =0; i < Date.dayNames.length; i++) {
20318                 var d = Date.dayNames[i];
20319                 ret.push({
20320                     tag: 'th',
20321                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20322                     html : d.substring(0,3)
20323                 });
20324                 
20325             }
20326             ret[0].cls += ' fc-first';
20327             ret[6].cls += ' fc-last';
20328             return ret;
20329         };
20330         var cal_cell = function(n) {
20331             return  {
20332                 tag: 'td',
20333                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20334                 cn : [
20335                     {
20336                         cn : [
20337                             {
20338                                 cls: 'fc-day-number',
20339                                 html: 'D'
20340                             },
20341                             {
20342                                 cls: 'fc-day-content',
20343                              
20344                                 cn : [
20345                                      {
20346                                         style: 'position: relative;' // height: 17px;
20347                                     }
20348                                 ]
20349                             }
20350                             
20351                             
20352                         ]
20353                     }
20354                 ]
20355                 
20356             }
20357         };
20358         var cal_rows = function() {
20359             
20360             var ret = [];
20361             for (var r = 0; r < 6; r++) {
20362                 var row= {
20363                     tag : 'tr',
20364                     cls : 'fc-week',
20365                     cn : []
20366                 };
20367                 
20368                 for (var i =0; i < Date.dayNames.length; i++) {
20369                     var d = Date.dayNames[i];
20370                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20371
20372                 }
20373                 row.cn[0].cls+=' fc-first';
20374                 row.cn[0].cn[0].style = 'min-height:90px';
20375                 row.cn[6].cls+=' fc-last';
20376                 ret.push(row);
20377                 
20378             }
20379             ret[0].cls += ' fc-first';
20380             ret[4].cls += ' fc-prev-last';
20381             ret[5].cls += ' fc-last';
20382             return ret;
20383             
20384         };
20385         
20386         var cal_table = {
20387             tag: 'table',
20388             cls: 'fc-border-separate',
20389             style : 'width:100%',
20390             cellspacing  : 0,
20391             cn : [
20392                 { 
20393                     tag: 'thead',
20394                     cn : [
20395                         { 
20396                             tag: 'tr',
20397                             cls : 'fc-first fc-last',
20398                             cn : cal_heads()
20399                         }
20400                     ]
20401                 },
20402                 { 
20403                     tag: 'tbody',
20404                     cn : cal_rows()
20405                 }
20406                   
20407             ]
20408         };
20409          
20410          var cfg = {
20411             cls : 'fc fc-ltr',
20412             cn : [
20413                 header,
20414                 {
20415                     cls : 'fc-content',
20416                     style : "position: relative;",
20417                     cn : [
20418                         {
20419                             cls : 'fc-view fc-view-month fc-grid',
20420                             style : 'position: relative',
20421                             unselectable : 'on',
20422                             cn : [
20423                                 {
20424                                     cls : 'fc-event-container',
20425                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20426                                 },
20427                                 cal_table
20428                             ]
20429                         }
20430                     ]
20431     
20432                 }
20433            ] 
20434             
20435         };
20436         
20437          
20438         
20439         return cfg;
20440     },
20441     
20442     
20443     initEvents : function()
20444     {
20445         if(!this.store){
20446             throw "can not find store for calendar";
20447         }
20448         
20449         var mark = {
20450             tag: "div",
20451             cls:"x-dlg-mask",
20452             style: "text-align:center",
20453             cn: [
20454                 {
20455                     tag: "div",
20456                     style: "background-color:white;width:50%;margin:250 auto",
20457                     cn: [
20458                         {
20459                             tag: "img",
20460                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20461                         },
20462                         {
20463                             tag: "span",
20464                             html: "Loading"
20465                         }
20466                         
20467                     ]
20468                 }
20469             ]
20470         };
20471         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20472         
20473         var size = this.el.select('.fc-content', true).first().getSize();
20474         this.maskEl.setSize(size.width, size.height);
20475         this.maskEl.enableDisplayMode("block");
20476         if(!this.loadMask){
20477             this.maskEl.hide();
20478         }
20479         
20480         this.store = Roo.factory(this.store, Roo.data);
20481         this.store.on('load', this.onLoad, this);
20482         this.store.on('beforeload', this.onBeforeLoad, this);
20483         
20484         this.resize();
20485         
20486         this.cells = this.el.select('.fc-day',true);
20487         //Roo.log(this.cells);
20488         this.textNodes = this.el.query('.fc-day-number');
20489         this.cells.addClassOnOver('fc-state-hover');
20490         
20491         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20492         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20493         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20494         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20495         
20496         this.on('monthchange', this.onMonthChange, this);
20497         
20498         this.update(new Date().clearTime());
20499     },
20500     
20501     resize : function() {
20502         var sz  = this.el.getSize();
20503         
20504         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20505         this.el.select('.fc-day-content div',true).setHeight(34);
20506     },
20507     
20508     
20509     // private
20510     showPrevMonth : function(e){
20511         this.update(this.activeDate.add("mo", -1));
20512     },
20513     showToday : function(e){
20514         this.update(new Date().clearTime());
20515     },
20516     // private
20517     showNextMonth : function(e){
20518         this.update(this.activeDate.add("mo", 1));
20519     },
20520
20521     // private
20522     showPrevYear : function(){
20523         this.update(this.activeDate.add("y", -1));
20524     },
20525
20526     // private
20527     showNextYear : function(){
20528         this.update(this.activeDate.add("y", 1));
20529     },
20530
20531     
20532    // private
20533     update : function(date)
20534     {
20535         var vd = this.activeDate;
20536         this.activeDate = date;
20537 //        if(vd && this.el){
20538 //            var t = date.getTime();
20539 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20540 //                Roo.log('using add remove');
20541 //                
20542 //                this.fireEvent('monthchange', this, date);
20543 //                
20544 //                this.cells.removeClass("fc-state-highlight");
20545 //                this.cells.each(function(c){
20546 //                   if(c.dateValue == t){
20547 //                       c.addClass("fc-state-highlight");
20548 //                       setTimeout(function(){
20549 //                            try{c.dom.firstChild.focus();}catch(e){}
20550 //                       }, 50);
20551 //                       return false;
20552 //                   }
20553 //                   return true;
20554 //                });
20555 //                return;
20556 //            }
20557 //        }
20558         
20559         var days = date.getDaysInMonth();
20560         
20561         var firstOfMonth = date.getFirstDateOfMonth();
20562         var startingPos = firstOfMonth.getDay()-this.startDay;
20563         
20564         if(startingPos < this.startDay){
20565             startingPos += 7;
20566         }
20567         
20568         var pm = date.add(Date.MONTH, -1);
20569         var prevStart = pm.getDaysInMonth()-startingPos;
20570 //        
20571         this.cells = this.el.select('.fc-day',true);
20572         this.textNodes = this.el.query('.fc-day-number');
20573         this.cells.addClassOnOver('fc-state-hover');
20574         
20575         var cells = this.cells.elements;
20576         var textEls = this.textNodes;
20577         
20578         Roo.each(cells, function(cell){
20579             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20580         });
20581         
20582         days += startingPos;
20583
20584         // convert everything to numbers so it's fast
20585         var day = 86400000;
20586         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20587         //Roo.log(d);
20588         //Roo.log(pm);
20589         //Roo.log(prevStart);
20590         
20591         var today = new Date().clearTime().getTime();
20592         var sel = date.clearTime().getTime();
20593         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20594         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20595         var ddMatch = this.disabledDatesRE;
20596         var ddText = this.disabledDatesText;
20597         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20598         var ddaysText = this.disabledDaysText;
20599         var format = this.format;
20600         
20601         var setCellClass = function(cal, cell){
20602             cell.row = 0;
20603             cell.events = [];
20604             cell.more = [];
20605             //Roo.log('set Cell Class');
20606             cell.title = "";
20607             var t = d.getTime();
20608             
20609             //Roo.log(d);
20610             
20611             cell.dateValue = t;
20612             if(t == today){
20613                 cell.className += " fc-today";
20614                 cell.className += " fc-state-highlight";
20615                 cell.title = cal.todayText;
20616             }
20617             if(t == sel){
20618                 // disable highlight in other month..
20619                 //cell.className += " fc-state-highlight";
20620                 
20621             }
20622             // disabling
20623             if(t < min) {
20624                 cell.className = " fc-state-disabled";
20625                 cell.title = cal.minText;
20626                 return;
20627             }
20628             if(t > max) {
20629                 cell.className = " fc-state-disabled";
20630                 cell.title = cal.maxText;
20631                 return;
20632             }
20633             if(ddays){
20634                 if(ddays.indexOf(d.getDay()) != -1){
20635                     cell.title = ddaysText;
20636                     cell.className = " fc-state-disabled";
20637                 }
20638             }
20639             if(ddMatch && format){
20640                 var fvalue = d.dateFormat(format);
20641                 if(ddMatch.test(fvalue)){
20642                     cell.title = ddText.replace("%0", fvalue);
20643                     cell.className = " fc-state-disabled";
20644                 }
20645             }
20646             
20647             if (!cell.initialClassName) {
20648                 cell.initialClassName = cell.dom.className;
20649             }
20650             
20651             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20652         };
20653
20654         var i = 0;
20655         
20656         for(; i < startingPos; i++) {
20657             textEls[i].innerHTML = (++prevStart);
20658             d.setDate(d.getDate()+1);
20659             
20660             cells[i].className = "fc-past fc-other-month";
20661             setCellClass(this, cells[i]);
20662         }
20663         
20664         var intDay = 0;
20665         
20666         for(; i < days; i++){
20667             intDay = i - startingPos + 1;
20668             textEls[i].innerHTML = (intDay);
20669             d.setDate(d.getDate()+1);
20670             
20671             cells[i].className = ''; // "x-date-active";
20672             setCellClass(this, cells[i]);
20673         }
20674         var extraDays = 0;
20675         
20676         for(; i < 42; i++) {
20677             textEls[i].innerHTML = (++extraDays);
20678             d.setDate(d.getDate()+1);
20679             
20680             cells[i].className = "fc-future fc-other-month";
20681             setCellClass(this, cells[i]);
20682         }
20683         
20684         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20685         
20686         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20687         
20688         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20689         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20690         
20691         if(totalRows != 6){
20692             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20693             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20694         }
20695         
20696         this.fireEvent('monthchange', this, date);
20697         
20698         
20699         /*
20700         if(!this.internalRender){
20701             var main = this.el.dom.firstChild;
20702             var w = main.offsetWidth;
20703             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20704             Roo.fly(main).setWidth(w);
20705             this.internalRender = true;
20706             // opera does not respect the auto grow header center column
20707             // then, after it gets a width opera refuses to recalculate
20708             // without a second pass
20709             if(Roo.isOpera && !this.secondPass){
20710                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20711                 this.secondPass = true;
20712                 this.update.defer(10, this, [date]);
20713             }
20714         }
20715         */
20716         
20717     },
20718     
20719     findCell : function(dt) {
20720         dt = dt.clearTime().getTime();
20721         var ret = false;
20722         this.cells.each(function(c){
20723             //Roo.log("check " +c.dateValue + '?=' + dt);
20724             if(c.dateValue == dt){
20725                 ret = c;
20726                 return false;
20727             }
20728             return true;
20729         });
20730         
20731         return ret;
20732     },
20733     
20734     findCells : function(ev) {
20735         var s = ev.start.clone().clearTime().getTime();
20736        // Roo.log(s);
20737         var e= ev.end.clone().clearTime().getTime();
20738        // Roo.log(e);
20739         var ret = [];
20740         this.cells.each(function(c){
20741              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20742             
20743             if(c.dateValue > e){
20744                 return ;
20745             }
20746             if(c.dateValue < s){
20747                 return ;
20748             }
20749             ret.push(c);
20750         });
20751         
20752         return ret;    
20753     },
20754     
20755 //    findBestRow: function(cells)
20756 //    {
20757 //        var ret = 0;
20758 //        
20759 //        for (var i =0 ; i < cells.length;i++) {
20760 //            ret  = Math.max(cells[i].rows || 0,ret);
20761 //        }
20762 //        return ret;
20763 //        
20764 //    },
20765     
20766     
20767     addItem : function(ev)
20768     {
20769         // look for vertical location slot in
20770         var cells = this.findCells(ev);
20771         
20772 //        ev.row = this.findBestRow(cells);
20773         
20774         // work out the location.
20775         
20776         var crow = false;
20777         var rows = [];
20778         for(var i =0; i < cells.length; i++) {
20779             
20780             cells[i].row = cells[0].row;
20781             
20782             if(i == 0){
20783                 cells[i].row = cells[i].row + 1;
20784             }
20785             
20786             if (!crow) {
20787                 crow = {
20788                     start : cells[i],
20789                     end :  cells[i]
20790                 };
20791                 continue;
20792             }
20793             if (crow.start.getY() == cells[i].getY()) {
20794                 // on same row.
20795                 crow.end = cells[i];
20796                 continue;
20797             }
20798             // different row.
20799             rows.push(crow);
20800             crow = {
20801                 start: cells[i],
20802                 end : cells[i]
20803             };
20804             
20805         }
20806         
20807         rows.push(crow);
20808         ev.els = [];
20809         ev.rows = rows;
20810         ev.cells = cells;
20811         
20812         cells[0].events.push(ev);
20813         
20814         this.calevents.push(ev);
20815     },
20816     
20817     clearEvents: function() {
20818         
20819         if(!this.calevents){
20820             return;
20821         }
20822         
20823         Roo.each(this.cells.elements, function(c){
20824             c.row = 0;
20825             c.events = [];
20826             c.more = [];
20827         });
20828         
20829         Roo.each(this.calevents, function(e) {
20830             Roo.each(e.els, function(el) {
20831                 el.un('mouseenter' ,this.onEventEnter, this);
20832                 el.un('mouseleave' ,this.onEventLeave, this);
20833                 el.remove();
20834             },this);
20835         },this);
20836         
20837         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20838             e.remove();
20839         });
20840         
20841     },
20842     
20843     renderEvents: function()
20844     {   
20845         var _this = this;
20846         
20847         this.cells.each(function(c) {
20848             
20849             if(c.row < 5){
20850                 return;
20851             }
20852             
20853             var ev = c.events;
20854             
20855             var r = 4;
20856             if(c.row != c.events.length){
20857                 r = 4 - (4 - (c.row - c.events.length));
20858             }
20859             
20860             c.events = ev.slice(0, r);
20861             c.more = ev.slice(r);
20862             
20863             if(c.more.length && c.more.length == 1){
20864                 c.events.push(c.more.pop());
20865             }
20866             
20867             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20868             
20869         });
20870             
20871         this.cells.each(function(c) {
20872             
20873             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20874             
20875             
20876             for (var e = 0; e < c.events.length; e++){
20877                 var ev = c.events[e];
20878                 var rows = ev.rows;
20879                 
20880                 for(var i = 0; i < rows.length; i++) {
20881                 
20882                     // how many rows should it span..
20883
20884                     var  cfg = {
20885                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20886                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20887
20888                         unselectable : "on",
20889                         cn : [
20890                             {
20891                                 cls: 'fc-event-inner',
20892                                 cn : [
20893     //                                {
20894     //                                  tag:'span',
20895     //                                  cls: 'fc-event-time',
20896     //                                  html : cells.length > 1 ? '' : ev.time
20897     //                                },
20898                                     {
20899                                       tag:'span',
20900                                       cls: 'fc-event-title',
20901                                       html : String.format('{0}', ev.title)
20902                                     }
20903
20904
20905                                 ]
20906                             },
20907                             {
20908                                 cls: 'ui-resizable-handle ui-resizable-e',
20909                                 html : '&nbsp;&nbsp;&nbsp'
20910                             }
20911
20912                         ]
20913                     };
20914
20915                     if (i == 0) {
20916                         cfg.cls += ' fc-event-start';
20917                     }
20918                     if ((i+1) == rows.length) {
20919                         cfg.cls += ' fc-event-end';
20920                     }
20921
20922                     var ctr = _this.el.select('.fc-event-container',true).first();
20923                     var cg = ctr.createChild(cfg);
20924
20925                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20926                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20927
20928                     var r = (c.more.length) ? 1 : 0;
20929                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20930                     cg.setWidth(ebox.right - sbox.x -2);
20931
20932                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20933                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20934                     cg.on('click', _this.onEventClick, _this, ev);
20935
20936                     ev.els.push(cg);
20937                     
20938                 }
20939                 
20940             }
20941             
20942             
20943             if(c.more.length){
20944                 var  cfg = {
20945                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20946                     style : 'position: absolute',
20947                     unselectable : "on",
20948                     cn : [
20949                         {
20950                             cls: 'fc-event-inner',
20951                             cn : [
20952                                 {
20953                                   tag:'span',
20954                                   cls: 'fc-event-title',
20955                                   html : 'More'
20956                                 }
20957
20958
20959                             ]
20960                         },
20961                         {
20962                             cls: 'ui-resizable-handle ui-resizable-e',
20963                             html : '&nbsp;&nbsp;&nbsp'
20964                         }
20965
20966                     ]
20967                 };
20968
20969                 var ctr = _this.el.select('.fc-event-container',true).first();
20970                 var cg = ctr.createChild(cfg);
20971
20972                 var sbox = c.select('.fc-day-content',true).first().getBox();
20973                 var ebox = c.select('.fc-day-content',true).first().getBox();
20974                 //Roo.log(cg);
20975                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20976                 cg.setWidth(ebox.right - sbox.x -2);
20977
20978                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20979                 
20980             }
20981             
20982         });
20983         
20984         
20985         
20986     },
20987     
20988     onEventEnter: function (e, el,event,d) {
20989         this.fireEvent('evententer', this, el, event);
20990     },
20991     
20992     onEventLeave: function (e, el,event,d) {
20993         this.fireEvent('eventleave', this, el, event);
20994     },
20995     
20996     onEventClick: function (e, el,event,d) {
20997         this.fireEvent('eventclick', this, el, event);
20998     },
20999     
21000     onMonthChange: function () {
21001         this.store.load();
21002     },
21003     
21004     onMoreEventClick: function(e, el, more)
21005     {
21006         var _this = this;
21007         
21008         this.calpopover.placement = 'right';
21009         this.calpopover.setTitle('More');
21010         
21011         this.calpopover.setContent('');
21012         
21013         var ctr = this.calpopover.el.select('.popover-content', true).first();
21014         
21015         Roo.each(more, function(m){
21016             var cfg = {
21017                 cls : 'fc-event-hori fc-event-draggable',
21018                 html : m.title
21019             };
21020             var cg = ctr.createChild(cfg);
21021             
21022             cg.on('click', _this.onEventClick, _this, m);
21023         });
21024         
21025         this.calpopover.show(el);
21026         
21027         
21028     },
21029     
21030     onLoad: function () 
21031     {   
21032         this.calevents = [];
21033         var cal = this;
21034         
21035         if(this.store.getCount() > 0){
21036             this.store.data.each(function(d){
21037                cal.addItem({
21038                     id : d.data.id,
21039                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21040                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21041                     time : d.data.start_time,
21042                     title : d.data.title,
21043                     description : d.data.description,
21044                     venue : d.data.venue
21045                 });
21046             });
21047         }
21048         
21049         this.renderEvents();
21050         
21051         if(this.calevents.length && this.loadMask){
21052             this.maskEl.hide();
21053         }
21054     },
21055     
21056     onBeforeLoad: function()
21057     {
21058         this.clearEvents();
21059         if(this.loadMask){
21060             this.maskEl.show();
21061         }
21062     }
21063 });
21064
21065  
21066  /*
21067  * - LGPL
21068  *
21069  * element
21070  * 
21071  */
21072
21073 /**
21074  * @class Roo.bootstrap.Popover
21075  * @extends Roo.bootstrap.Component
21076  * @builder-top
21077  * Bootstrap Popover class
21078  * @cfg {String} html contents of the popover   (or false to use children..)
21079  * @cfg {String} title of popover (or false to hide)
21080  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21081  * @cfg {String} trigger click || hover (or false to trigger manually)
21082  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21083  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21084  *      - if false and it has a 'parent' then it will be automatically added to that element
21085  *      - if string - Roo.get  will be called 
21086  * @cfg {Number} delay - delay before showing
21087  
21088  * @constructor
21089  * Create a new Popover
21090  * @param {Object} config The config object
21091  */
21092
21093 Roo.bootstrap.Popover = function(config){
21094     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21095     
21096     this.addEvents({
21097         // raw events
21098          /**
21099          * @event show
21100          * After the popover show
21101          * 
21102          * @param {Roo.bootstrap.Popover} this
21103          */
21104         "show" : true,
21105         /**
21106          * @event hide
21107          * After the popover hide
21108          * 
21109          * @param {Roo.bootstrap.Popover} this
21110          */
21111         "hide" : true
21112     });
21113 };
21114
21115 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21116     
21117     title: false,
21118     html: false,
21119     
21120     placement : 'right',
21121     trigger : 'hover', // hover
21122     modal : false,
21123     delay : 0,
21124     
21125     over: false,
21126     
21127     can_build_overlaid : false,
21128     
21129     maskEl : false, // the mask element
21130     headerEl : false,
21131     contentEl : false,
21132     alignEl : false, // when show is called with an element - this get's stored.
21133     
21134     getChildContainer : function()
21135     {
21136         return this.contentEl;
21137         
21138     },
21139     getPopoverHeader : function()
21140     {
21141         this.title = true; // flag not to hide it..
21142         this.headerEl.addClass('p-0');
21143         return this.headerEl
21144     },
21145     
21146     
21147     getAutoCreate : function(){
21148          
21149         var cfg = {
21150            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21151            style: 'display:block',
21152            cn : [
21153                 {
21154                     cls : 'arrow'
21155                 },
21156                 {
21157                     cls : 'popover-inner ',
21158                     cn : [
21159                         {
21160                             tag: 'h3',
21161                             cls: 'popover-title popover-header',
21162                             html : this.title === false ? '' : this.title
21163                         },
21164                         {
21165                             cls : 'popover-content popover-body '  + (this.cls || ''),
21166                             html : this.html || ''
21167                         }
21168                     ]
21169                     
21170                 }
21171            ]
21172         };
21173         
21174         return cfg;
21175     },
21176     /**
21177      * @param {string} the title
21178      */
21179     setTitle: function(str)
21180     {
21181         this.title = str;
21182         if (this.el) {
21183             this.headerEl.dom.innerHTML = str;
21184         }
21185         
21186     },
21187     /**
21188      * @param {string} the body content
21189      */
21190     setContent: function(str)
21191     {
21192         this.html = str;
21193         if (this.contentEl) {
21194             this.contentEl.dom.innerHTML = str;
21195         }
21196         
21197     },
21198     // as it get's added to the bottom of the page.
21199     onRender : function(ct, position)
21200     {
21201         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21202         
21203         
21204         
21205         if(!this.el){
21206             var cfg = Roo.apply({},  this.getAutoCreate());
21207             cfg.id = Roo.id();
21208             
21209             if (this.cls) {
21210                 cfg.cls += ' ' + this.cls;
21211             }
21212             if (this.style) {
21213                 cfg.style = this.style;
21214             }
21215             //Roo.log("adding to ");
21216             this.el = Roo.get(document.body).createChild(cfg, position);
21217 //            Roo.log(this.el);
21218         }
21219         
21220         this.contentEl = this.el.select('.popover-content',true).first();
21221         this.headerEl =  this.el.select('.popover-title',true).first();
21222         
21223         var nitems = [];
21224         if(typeof(this.items) != 'undefined'){
21225             var items = this.items;
21226             delete this.items;
21227
21228             for(var i =0;i < items.length;i++) {
21229                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21230             }
21231         }
21232
21233         this.items = nitems;
21234         
21235         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21236         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21237         
21238         
21239         
21240         this.initEvents();
21241     },
21242     
21243     resizeMask : function()
21244     {
21245         this.maskEl.setSize(
21246             Roo.lib.Dom.getViewWidth(true),
21247             Roo.lib.Dom.getViewHeight(true)
21248         );
21249     },
21250     
21251     initEvents : function()
21252     {
21253         
21254         if (!this.modal) { 
21255             Roo.bootstrap.Popover.register(this);
21256         }
21257          
21258         this.arrowEl = this.el.select('.arrow',true).first();
21259         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21260         this.el.enableDisplayMode('block');
21261         this.el.hide();
21262  
21263         
21264         if (this.over === false && !this.parent()) {
21265             return; 
21266         }
21267         if (this.triggers === false) {
21268             return;
21269         }
21270          
21271         // support parent
21272         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21273         var triggers = this.trigger ? this.trigger.split(' ') : [];
21274         Roo.each(triggers, function(trigger) {
21275         
21276             if (trigger == 'click') {
21277                 on_el.on('click', this.toggle, this);
21278             } else if (trigger != 'manual') {
21279                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21280                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21281       
21282                 on_el.on(eventIn  ,this.enter, this);
21283                 on_el.on(eventOut, this.leave, this);
21284             }
21285         }, this);
21286     },
21287     
21288     
21289     // private
21290     timeout : null,
21291     hoverState : null,
21292     
21293     toggle : function () {
21294         this.hoverState == 'in' ? this.leave() : this.enter();
21295     },
21296     
21297     enter : function () {
21298         
21299         clearTimeout(this.timeout);
21300     
21301         this.hoverState = 'in';
21302     
21303         if (!this.delay || !this.delay.show) {
21304             this.show();
21305             return;
21306         }
21307         var _t = this;
21308         this.timeout = setTimeout(function () {
21309             if (_t.hoverState == 'in') {
21310                 _t.show();
21311             }
21312         }, this.delay.show)
21313     },
21314     
21315     leave : function() {
21316         clearTimeout(this.timeout);
21317     
21318         this.hoverState = 'out';
21319     
21320         if (!this.delay || !this.delay.hide) {
21321             this.hide();
21322             return;
21323         }
21324         var _t = this;
21325         this.timeout = setTimeout(function () {
21326             if (_t.hoverState == 'out') {
21327                 _t.hide();
21328             }
21329         }, this.delay.hide)
21330     },
21331     /**
21332      * Show the popover
21333      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21334      * @param {string} (left|right|top|bottom) position
21335      */
21336     show : function (on_el, placement)
21337     {
21338         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21339         on_el = on_el || false; // default to false
21340          
21341         if (!on_el) {
21342             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21343                 on_el = this.parent().el;
21344             } else if (this.over) {
21345                 on_el = Roo.get(this.over);
21346             }
21347             
21348         }
21349         
21350         this.alignEl = Roo.get( on_el );
21351
21352         if (!this.el) {
21353             this.render(document.body);
21354         }
21355         
21356         
21357          
21358         
21359         if (this.title === false) {
21360             this.headerEl.hide();
21361         }
21362         
21363        
21364         this.el.show();
21365         this.el.dom.style.display = 'block';
21366          
21367  
21368         if (this.alignEl) {
21369             this.updatePosition(this.placement, true);
21370              
21371         } else {
21372             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21373             var es = this.el.getSize();
21374             var x = Roo.lib.Dom.getViewWidth()/2;
21375             var y = Roo.lib.Dom.getViewHeight()/2;
21376             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21377             
21378         }
21379
21380         
21381         //var arrow = this.el.select('.arrow',true).first();
21382         //arrow.set(align[2], 
21383         
21384         this.el.addClass('in');
21385         
21386          
21387         
21388         this.hoverState = 'in';
21389         
21390         if (this.modal) {
21391             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21392             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21393             this.maskEl.dom.style.display = 'block';
21394             this.maskEl.addClass('show');
21395         }
21396         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21397  
21398         this.fireEvent('show', this);
21399         
21400     },
21401     /**
21402      * fire this manually after loading a grid in the table for example
21403      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21404      * @param {Boolean} try and move it if we cant get right position.
21405      */
21406     updatePosition : function(placement, try_move)
21407     {
21408         // allow for calling with no parameters
21409         placement = placement   ? placement :  this.placement;
21410         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21411         
21412         this.el.removeClass([
21413             'fade','top','bottom', 'left', 'right','in',
21414             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21415         ]);
21416         this.el.addClass(placement + ' bs-popover-' + placement);
21417         
21418         if (!this.alignEl ) {
21419             return false;
21420         }
21421         
21422         switch (placement) {
21423             case 'right':
21424                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21425                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21426                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21427                     //normal display... or moved up/down.
21428                     this.el.setXY(offset);
21429                     var xy = this.alignEl.getAnchorXY('tr', false);
21430                     xy[0]+=2;xy[1]+=5;
21431                     this.arrowEl.setXY(xy);
21432                     return true;
21433                 }
21434                 // continue through...
21435                 return this.updatePosition('left', false);
21436                 
21437             
21438             case 'left':
21439                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21440                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21441                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21442                     //normal display... or moved up/down.
21443                     this.el.setXY(offset);
21444                     var xy = this.alignEl.getAnchorXY('tl', false);
21445                     xy[0]-=10;xy[1]+=5; // << fix me
21446                     this.arrowEl.setXY(xy);
21447                     return true;
21448                 }
21449                 // call self...
21450                 return this.updatePosition('right', false);
21451             
21452             case 'top':
21453                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21454                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21455                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21456                     //normal display... or moved up/down.
21457                     this.el.setXY(offset);
21458                     var xy = this.alignEl.getAnchorXY('t', false);
21459                     xy[1]-=10; // << fix me
21460                     this.arrowEl.setXY(xy);
21461                     return true;
21462                 }
21463                 // fall through
21464                return this.updatePosition('bottom', false);
21465             
21466             case 'bottom':
21467                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21468                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21469                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21470                     //normal display... or moved up/down.
21471                     this.el.setXY(offset);
21472                     var xy = this.alignEl.getAnchorXY('b', false);
21473                      xy[1]+=2; // << fix me
21474                     this.arrowEl.setXY(xy);
21475                     return true;
21476                 }
21477                 // fall through
21478                 return this.updatePosition('top', false);
21479                 
21480             
21481         }
21482         
21483         
21484         return false;
21485     },
21486     
21487     hide : function()
21488     {
21489         this.el.setXY([0,0]);
21490         this.el.removeClass('in');
21491         this.el.hide();
21492         this.hoverState = null;
21493         this.maskEl.hide(); // always..
21494         this.fireEvent('hide', this);
21495     }
21496     
21497 });
21498
21499
21500 Roo.apply(Roo.bootstrap.Popover, {
21501
21502     alignment : {
21503         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21504         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21505         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21506         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21507     },
21508     
21509     zIndex : 20001,
21510
21511     clickHander : false,
21512     
21513     
21514
21515     onMouseDown : function(e)
21516     {
21517         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21518             /// what is nothing is showing..
21519             this.hideAll();
21520         }
21521          
21522     },
21523     
21524     
21525     popups : [],
21526     
21527     register : function(popup)
21528     {
21529         if (!Roo.bootstrap.Popover.clickHandler) {
21530             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21531         }
21532         // hide other popups.
21533         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21534         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21535         this.hideAll(); //<< why?
21536         //this.popups.push(popup);
21537     },
21538     hideAll : function()
21539     {
21540         this.popups.forEach(function(p) {
21541             p.hide();
21542         });
21543     },
21544     onShow : function() {
21545         Roo.bootstrap.Popover.popups.push(this);
21546     },
21547     onHide : function() {
21548         Roo.bootstrap.Popover.popups.remove(this);
21549     } 
21550
21551 });/*
21552  * - LGPL
21553  *
21554  * Card header - holder for the card header elements.
21555  * 
21556  */
21557
21558 /**
21559  * @class Roo.bootstrap.PopoverNav
21560  * @extends Roo.bootstrap.NavGroup
21561  * Bootstrap Popover header navigation class
21562  * @constructor
21563  * Create a new Popover Header Navigation 
21564  * @param {Object} config The config object
21565  */
21566
21567 Roo.bootstrap.PopoverNav = function(config){
21568     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21569 };
21570
21571 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21572     
21573     
21574     container_method : 'getPopoverHeader' 
21575     
21576      
21577     
21578     
21579    
21580 });
21581
21582  
21583
21584  /*
21585  * - LGPL
21586  *
21587  * Progress
21588  * 
21589  */
21590
21591 /**
21592  * @class Roo.bootstrap.Progress
21593  * @extends Roo.bootstrap.Component
21594  * Bootstrap Progress class
21595  * @cfg {Boolean} striped striped of the progress bar
21596  * @cfg {Boolean} active animated of the progress bar
21597  * 
21598  * 
21599  * @constructor
21600  * Create a new Progress
21601  * @param {Object} config The config object
21602  */
21603
21604 Roo.bootstrap.Progress = function(config){
21605     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21606 };
21607
21608 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21609     
21610     striped : false,
21611     active: false,
21612     
21613     getAutoCreate : function(){
21614         var cfg = {
21615             tag: 'div',
21616             cls: 'progress'
21617         };
21618         
21619         
21620         if(this.striped){
21621             cfg.cls += ' progress-striped';
21622         }
21623       
21624         if(this.active){
21625             cfg.cls += ' active';
21626         }
21627         
21628         
21629         return cfg;
21630     }
21631    
21632 });
21633
21634  
21635
21636  /*
21637  * - LGPL
21638  *
21639  * ProgressBar
21640  * 
21641  */
21642
21643 /**
21644  * @class Roo.bootstrap.ProgressBar
21645  * @extends Roo.bootstrap.Component
21646  * Bootstrap ProgressBar class
21647  * @cfg {Number} aria_valuenow aria-value now
21648  * @cfg {Number} aria_valuemin aria-value min
21649  * @cfg {Number} aria_valuemax aria-value max
21650  * @cfg {String} label label for the progress bar
21651  * @cfg {String} panel (success | info | warning | danger )
21652  * @cfg {String} role role of the progress bar
21653  * @cfg {String} sr_only text
21654  * 
21655  * 
21656  * @constructor
21657  * Create a new ProgressBar
21658  * @param {Object} config The config object
21659  */
21660
21661 Roo.bootstrap.ProgressBar = function(config){
21662     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21663 };
21664
21665 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21666     
21667     aria_valuenow : 0,
21668     aria_valuemin : 0,
21669     aria_valuemax : 100,
21670     label : false,
21671     panel : false,
21672     role : false,
21673     sr_only: false,
21674     
21675     getAutoCreate : function()
21676     {
21677         
21678         var cfg = {
21679             tag: 'div',
21680             cls: 'progress-bar',
21681             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21682         };
21683         
21684         if(this.sr_only){
21685             cfg.cn = {
21686                 tag: 'span',
21687                 cls: 'sr-only',
21688                 html: this.sr_only
21689             }
21690         }
21691         
21692         if(this.role){
21693             cfg.role = this.role;
21694         }
21695         
21696         if(this.aria_valuenow){
21697             cfg['aria-valuenow'] = this.aria_valuenow;
21698         }
21699         
21700         if(this.aria_valuemin){
21701             cfg['aria-valuemin'] = this.aria_valuemin;
21702         }
21703         
21704         if(this.aria_valuemax){
21705             cfg['aria-valuemax'] = this.aria_valuemax;
21706         }
21707         
21708         if(this.label && !this.sr_only){
21709             cfg.html = this.label;
21710         }
21711         
21712         if(this.panel){
21713             cfg.cls += ' progress-bar-' + this.panel;
21714         }
21715         
21716         return cfg;
21717     },
21718     
21719     update : function(aria_valuenow)
21720     {
21721         this.aria_valuenow = aria_valuenow;
21722         
21723         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21724     }
21725    
21726 });
21727
21728  
21729
21730  /*
21731  * - LGPL
21732  *
21733  * column
21734  * 
21735  */
21736
21737 /**
21738  * @class Roo.bootstrap.TabGroup
21739  * @extends Roo.bootstrap.Column
21740  * Bootstrap Column class
21741  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21742  * @cfg {Boolean} carousel true to make the group behave like a carousel
21743  * @cfg {Boolean} bullets show bullets for the panels
21744  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21745  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21746  * @cfg {Boolean} showarrow (true|false) show arrow default true
21747  * 
21748  * @constructor
21749  * Create a new TabGroup
21750  * @param {Object} config The config object
21751  */
21752
21753 Roo.bootstrap.TabGroup = function(config){
21754     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21755     if (!this.navId) {
21756         this.navId = Roo.id();
21757     }
21758     this.tabs = [];
21759     Roo.bootstrap.TabGroup.register(this);
21760     
21761 };
21762
21763 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21764     
21765     carousel : false,
21766     transition : false,
21767     bullets : 0,
21768     timer : 0,
21769     autoslide : false,
21770     slideFn : false,
21771     slideOnTouch : false,
21772     showarrow : true,
21773     
21774     getAutoCreate : function()
21775     {
21776         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21777         
21778         cfg.cls += ' tab-content';
21779         
21780         if (this.carousel) {
21781             cfg.cls += ' carousel slide';
21782             
21783             cfg.cn = [{
21784                cls : 'carousel-inner',
21785                cn : []
21786             }];
21787         
21788             if(this.bullets  && !Roo.isTouch){
21789                 
21790                 var bullets = {
21791                     cls : 'carousel-bullets',
21792                     cn : []
21793                 };
21794                
21795                 if(this.bullets_cls){
21796                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21797                 }
21798                 
21799                 bullets.cn.push({
21800                     cls : 'clear'
21801                 });
21802                 
21803                 cfg.cn[0].cn.push(bullets);
21804             }
21805             
21806             if(this.showarrow){
21807                 cfg.cn[0].cn.push({
21808                     tag : 'div',
21809                     class : 'carousel-arrow',
21810                     cn : [
21811                         {
21812                             tag : 'div',
21813                             class : 'carousel-prev',
21814                             cn : [
21815                                 {
21816                                     tag : 'i',
21817                                     class : 'fa fa-chevron-left'
21818                                 }
21819                             ]
21820                         },
21821                         {
21822                             tag : 'div',
21823                             class : 'carousel-next',
21824                             cn : [
21825                                 {
21826                                     tag : 'i',
21827                                     class : 'fa fa-chevron-right'
21828                                 }
21829                             ]
21830                         }
21831                     ]
21832                 });
21833             }
21834             
21835         }
21836         
21837         return cfg;
21838     },
21839     
21840     initEvents:  function()
21841     {
21842 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21843 //            this.el.on("touchstart", this.onTouchStart, this);
21844 //        }
21845         
21846         if(this.autoslide){
21847             var _this = this;
21848             
21849             this.slideFn = window.setInterval(function() {
21850                 _this.showPanelNext();
21851             }, this.timer);
21852         }
21853         
21854         if(this.showarrow){
21855             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21856             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21857         }
21858         
21859         
21860     },
21861     
21862 //    onTouchStart : function(e, el, o)
21863 //    {
21864 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21865 //            return;
21866 //        }
21867 //        
21868 //        this.showPanelNext();
21869 //    },
21870     
21871     
21872     getChildContainer : function()
21873     {
21874         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21875     },
21876     
21877     /**
21878     * register a Navigation item
21879     * @param {Roo.bootstrap.NavItem} the navitem to add
21880     */
21881     register : function(item)
21882     {
21883         this.tabs.push( item);
21884         item.navId = this.navId; // not really needed..
21885         this.addBullet();
21886     
21887     },
21888     
21889     getActivePanel : function()
21890     {
21891         var r = false;
21892         Roo.each(this.tabs, function(t) {
21893             if (t.active) {
21894                 r = t;
21895                 return false;
21896             }
21897             return null;
21898         });
21899         return r;
21900         
21901     },
21902     getPanelByName : function(n)
21903     {
21904         var r = false;
21905         Roo.each(this.tabs, function(t) {
21906             if (t.tabId == n) {
21907                 r = t;
21908                 return false;
21909             }
21910             return null;
21911         });
21912         return r;
21913     },
21914     indexOfPanel : function(p)
21915     {
21916         var r = false;
21917         Roo.each(this.tabs, function(t,i) {
21918             if (t.tabId == p.tabId) {
21919                 r = i;
21920                 return false;
21921             }
21922             return null;
21923         });
21924         return r;
21925     },
21926     /**
21927      * show a specific panel
21928      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21929      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21930      */
21931     showPanel : function (pan)
21932     {
21933         if(this.transition || typeof(pan) == 'undefined'){
21934             Roo.log("waiting for the transitionend");
21935             return false;
21936         }
21937         
21938         if (typeof(pan) == 'number') {
21939             pan = this.tabs[pan];
21940         }
21941         
21942         if (typeof(pan) == 'string') {
21943             pan = this.getPanelByName(pan);
21944         }
21945         
21946         var cur = this.getActivePanel();
21947         
21948         if(!pan || !cur){
21949             Roo.log('pan or acitve pan is undefined');
21950             return false;
21951         }
21952         
21953         if (pan.tabId == this.getActivePanel().tabId) {
21954             return true;
21955         }
21956         
21957         if (false === cur.fireEvent('beforedeactivate')) {
21958             return false;
21959         }
21960         
21961         if(this.bullets > 0 && !Roo.isTouch){
21962             this.setActiveBullet(this.indexOfPanel(pan));
21963         }
21964         
21965         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21966             
21967             //class="carousel-item carousel-item-next carousel-item-left"
21968             
21969             this.transition = true;
21970             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21971             var lr = dir == 'next' ? 'left' : 'right';
21972             pan.el.addClass(dir); // or prev
21973             pan.el.addClass('carousel-item-' + dir); // or prev
21974             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21975             cur.el.addClass(lr); // or right
21976             pan.el.addClass(lr);
21977             cur.el.addClass('carousel-item-' +lr); // or right
21978             pan.el.addClass('carousel-item-' +lr);
21979             
21980             
21981             var _this = this;
21982             cur.el.on('transitionend', function() {
21983                 Roo.log("trans end?");
21984                 
21985                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21986                 pan.setActive(true);
21987                 
21988                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21989                 cur.setActive(false);
21990                 
21991                 _this.transition = false;
21992                 
21993             }, this, { single:  true } );
21994             
21995             return true;
21996         }
21997         
21998         cur.setActive(false);
21999         pan.setActive(true);
22000         
22001         return true;
22002         
22003     },
22004     showPanelNext : function()
22005     {
22006         var i = this.indexOfPanel(this.getActivePanel());
22007         
22008         if (i >= this.tabs.length - 1 && !this.autoslide) {
22009             return;
22010         }
22011         
22012         if (i >= this.tabs.length - 1 && this.autoslide) {
22013             i = -1;
22014         }
22015         
22016         this.showPanel(this.tabs[i+1]);
22017     },
22018     
22019     showPanelPrev : function()
22020     {
22021         var i = this.indexOfPanel(this.getActivePanel());
22022         
22023         if (i  < 1 && !this.autoslide) {
22024             return;
22025         }
22026         
22027         if (i < 1 && this.autoslide) {
22028             i = this.tabs.length;
22029         }
22030         
22031         this.showPanel(this.tabs[i-1]);
22032     },
22033     
22034     
22035     addBullet: function()
22036     {
22037         if(!this.bullets || Roo.isTouch){
22038             return;
22039         }
22040         var ctr = this.el.select('.carousel-bullets',true).first();
22041         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22042         var bullet = ctr.createChild({
22043             cls : 'bullet bullet-' + i
22044         },ctr.dom.lastChild);
22045         
22046         
22047         var _this = this;
22048         
22049         bullet.on('click', (function(e, el, o, ii, t){
22050
22051             e.preventDefault();
22052
22053             this.showPanel(ii);
22054
22055             if(this.autoslide && this.slideFn){
22056                 clearInterval(this.slideFn);
22057                 this.slideFn = window.setInterval(function() {
22058                     _this.showPanelNext();
22059                 }, this.timer);
22060             }
22061
22062         }).createDelegate(this, [i, bullet], true));
22063                 
22064         
22065     },
22066      
22067     setActiveBullet : function(i)
22068     {
22069         if(Roo.isTouch){
22070             return;
22071         }
22072         
22073         Roo.each(this.el.select('.bullet', true).elements, function(el){
22074             el.removeClass('selected');
22075         });
22076
22077         var bullet = this.el.select('.bullet-' + i, true).first();
22078         
22079         if(!bullet){
22080             return;
22081         }
22082         
22083         bullet.addClass('selected');
22084     }
22085     
22086     
22087   
22088 });
22089
22090  
22091
22092  
22093  
22094 Roo.apply(Roo.bootstrap.TabGroup, {
22095     
22096     groups: {},
22097      /**
22098     * register a Navigation Group
22099     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22100     */
22101     register : function(navgrp)
22102     {
22103         this.groups[navgrp.navId] = navgrp;
22104         
22105     },
22106     /**
22107     * fetch a Navigation Group based on the navigation ID
22108     * if one does not exist , it will get created.
22109     * @param {string} the navgroup to add
22110     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22111     */
22112     get: function(navId) {
22113         if (typeof(this.groups[navId]) == 'undefined') {
22114             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22115         }
22116         return this.groups[navId] ;
22117     }
22118     
22119     
22120     
22121 });
22122
22123  /*
22124  * - LGPL
22125  *
22126  * TabPanel
22127  * 
22128  */
22129
22130 /**
22131  * @class Roo.bootstrap.TabPanel
22132  * @extends Roo.bootstrap.Component
22133  * @children Roo.bootstrap.Component
22134  * Bootstrap TabPanel class
22135  * @cfg {Boolean} active panel active
22136  * @cfg {String} html panel content
22137  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22138  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22139  * @cfg {String} href click to link..
22140  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22141  * 
22142  * 
22143  * @constructor
22144  * Create a new TabPanel
22145  * @param {Object} config The config object
22146  */
22147
22148 Roo.bootstrap.TabPanel = function(config){
22149     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22150     this.addEvents({
22151         /**
22152              * @event changed
22153              * Fires when the active status changes
22154              * @param {Roo.bootstrap.TabPanel} this
22155              * @param {Boolean} state the new state
22156             
22157          */
22158         'changed': true,
22159         /**
22160              * @event beforedeactivate
22161              * Fires before a tab is de-activated - can be used to do validation on a form.
22162              * @param {Roo.bootstrap.TabPanel} this
22163              * @return {Boolean} false if there is an error
22164             
22165          */
22166         'beforedeactivate': true
22167      });
22168     
22169     this.tabId = this.tabId || Roo.id();
22170   
22171 };
22172
22173 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22174     
22175     active: false,
22176     html: false,
22177     tabId: false,
22178     navId : false,
22179     href : '',
22180     touchSlide : false,
22181     getAutoCreate : function(){
22182         
22183         
22184         var cfg = {
22185             tag: 'div',
22186             // item is needed for carousel - not sure if it has any effect otherwise
22187             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22188             html: this.html || ''
22189         };
22190         
22191         if(this.active){
22192             cfg.cls += ' active';
22193         }
22194         
22195         if(this.tabId){
22196             cfg.tabId = this.tabId;
22197         }
22198         
22199         
22200         
22201         return cfg;
22202     },
22203     
22204     initEvents:  function()
22205     {
22206         var p = this.parent();
22207         
22208         this.navId = this.navId || p.navId;
22209         
22210         if (typeof(this.navId) != 'undefined') {
22211             // not really needed.. but just in case.. parent should be a NavGroup.
22212             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22213             
22214             tg.register(this);
22215             
22216             var i = tg.tabs.length - 1;
22217             
22218             if(this.active && tg.bullets > 0 && i < tg.bullets){
22219                 tg.setActiveBullet(i);
22220             }
22221         }
22222         
22223         this.el.on('click', this.onClick, this);
22224         
22225         if(Roo.isTouch && this.touchSlide){
22226             this.el.on("touchstart", this.onTouchStart, this);
22227             this.el.on("touchmove", this.onTouchMove, this);
22228             this.el.on("touchend", this.onTouchEnd, this);
22229         }
22230         
22231     },
22232     
22233     onRender : function(ct, position)
22234     {
22235         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22236     },
22237     
22238     setActive : function(state)
22239     {
22240         Roo.log("panel - set active " + this.tabId + "=" + state);
22241         
22242         this.active = state;
22243         if (!state) {
22244             this.el.removeClass('active');
22245             
22246         } else  if (!this.el.hasClass('active')) {
22247             this.el.addClass('active');
22248         }
22249         
22250         this.fireEvent('changed', this, state);
22251     },
22252     
22253     onClick : function(e)
22254     {
22255         e.preventDefault();
22256         
22257         if(!this.href.length){
22258             return;
22259         }
22260         
22261         window.location.href = this.href;
22262     },
22263     
22264     startX : 0,
22265     startY : 0,
22266     endX : 0,
22267     endY : 0,
22268     swiping : false,
22269     
22270     onTouchStart : function(e)
22271     {
22272         this.swiping = false;
22273         
22274         this.startX = e.browserEvent.touches[0].clientX;
22275         this.startY = e.browserEvent.touches[0].clientY;
22276     },
22277     
22278     onTouchMove : function(e)
22279     {
22280         this.swiping = true;
22281         
22282         this.endX = e.browserEvent.touches[0].clientX;
22283         this.endY = e.browserEvent.touches[0].clientY;
22284     },
22285     
22286     onTouchEnd : function(e)
22287     {
22288         if(!this.swiping){
22289             this.onClick(e);
22290             return;
22291         }
22292         
22293         var tabGroup = this.parent();
22294         
22295         if(this.endX > this.startX){ // swiping right
22296             tabGroup.showPanelPrev();
22297             return;
22298         }
22299         
22300         if(this.startX > this.endX){ // swiping left
22301             tabGroup.showPanelNext();
22302             return;
22303         }
22304     }
22305     
22306     
22307 });
22308  
22309
22310  
22311
22312  /*
22313  * - LGPL
22314  *
22315  * DateField
22316  * 
22317  */
22318
22319 /**
22320  * @class Roo.bootstrap.DateField
22321  * @extends Roo.bootstrap.Input
22322  * Bootstrap DateField class
22323  * @cfg {Number} weekStart default 0
22324  * @cfg {String} viewMode default empty, (months|years)
22325  * @cfg {String} minViewMode default empty, (months|years)
22326  * @cfg {Number} startDate default -Infinity
22327  * @cfg {Number} endDate default Infinity
22328  * @cfg {Boolean} todayHighlight default false
22329  * @cfg {Boolean} todayBtn default false
22330  * @cfg {Boolean} calendarWeeks default false
22331  * @cfg {Object} daysOfWeekDisabled default empty
22332  * @cfg {Boolean} singleMode default false (true | false)
22333  * 
22334  * @cfg {Boolean} keyboardNavigation default true
22335  * @cfg {String} language default en
22336  * 
22337  * @constructor
22338  * Create a new DateField
22339  * @param {Object} config The config object
22340  */
22341
22342 Roo.bootstrap.DateField = function(config){
22343     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22344      this.addEvents({
22345             /**
22346              * @event show
22347              * Fires when this field show.
22348              * @param {Roo.bootstrap.DateField} this
22349              * @param {Mixed} date The date value
22350              */
22351             show : true,
22352             /**
22353              * @event show
22354              * Fires when this field hide.
22355              * @param {Roo.bootstrap.DateField} this
22356              * @param {Mixed} date The date value
22357              */
22358             hide : true,
22359             /**
22360              * @event select
22361              * Fires when select a date.
22362              * @param {Roo.bootstrap.DateField} this
22363              * @param {Mixed} date The date value
22364              */
22365             select : true,
22366             /**
22367              * @event beforeselect
22368              * Fires when before select a date.
22369              * @param {Roo.bootstrap.DateField} this
22370              * @param {Mixed} date The date value
22371              */
22372             beforeselect : true
22373         });
22374 };
22375
22376 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22377     
22378     /**
22379      * @cfg {String} format
22380      * The default date format string which can be overriden for localization support.  The format must be
22381      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22382      */
22383     format : "m/d/y",
22384     /**
22385      * @cfg {String} altFormats
22386      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22387      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22388      */
22389     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22390     
22391     weekStart : 0,
22392     
22393     viewMode : '',
22394     
22395     minViewMode : '',
22396     
22397     todayHighlight : false,
22398     
22399     todayBtn: false,
22400     
22401     language: 'en',
22402     
22403     keyboardNavigation: true,
22404     
22405     calendarWeeks: false,
22406     
22407     startDate: -Infinity,
22408     
22409     endDate: Infinity,
22410     
22411     daysOfWeekDisabled: [],
22412     
22413     _events: [],
22414     
22415     singleMode : false,
22416     
22417     UTCDate: function()
22418     {
22419         return new Date(Date.UTC.apply(Date, arguments));
22420     },
22421     
22422     UTCToday: function()
22423     {
22424         var today = new Date();
22425         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22426     },
22427     
22428     getDate: function() {
22429             var d = this.getUTCDate();
22430             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22431     },
22432     
22433     getUTCDate: function() {
22434             return this.date;
22435     },
22436     
22437     setDate: function(d) {
22438             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22439     },
22440     
22441     setUTCDate: function(d) {
22442             this.date = d;
22443             this.setValue(this.formatDate(this.date));
22444     },
22445         
22446     onRender: function(ct, position)
22447     {
22448         
22449         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22450         
22451         this.language = this.language || 'en';
22452         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22453         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22454         
22455         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22456         this.format = this.format || 'm/d/y';
22457         this.isInline = false;
22458         this.isInput = true;
22459         this.component = this.el.select('.add-on', true).first() || false;
22460         this.component = (this.component && this.component.length === 0) ? false : this.component;
22461         this.hasInput = this.component && this.inputEl().length;
22462         
22463         if (typeof(this.minViewMode === 'string')) {
22464             switch (this.minViewMode) {
22465                 case 'months':
22466                     this.minViewMode = 1;
22467                     break;
22468                 case 'years':
22469                     this.minViewMode = 2;
22470                     break;
22471                 default:
22472                     this.minViewMode = 0;
22473                     break;
22474             }
22475         }
22476         
22477         if (typeof(this.viewMode === 'string')) {
22478             switch (this.viewMode) {
22479                 case 'months':
22480                     this.viewMode = 1;
22481                     break;
22482                 case 'years':
22483                     this.viewMode = 2;
22484                     break;
22485                 default:
22486                     this.viewMode = 0;
22487                     break;
22488             }
22489         }
22490                 
22491         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22492         
22493 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22494         
22495         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22496         
22497         this.picker().on('mousedown', this.onMousedown, this);
22498         this.picker().on('click', this.onClick, this);
22499         
22500         this.picker().addClass('datepicker-dropdown');
22501         
22502         this.startViewMode = this.viewMode;
22503         
22504         if(this.singleMode){
22505             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22506                 v.setVisibilityMode(Roo.Element.DISPLAY);
22507                 v.hide();
22508             });
22509             
22510             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22511                 v.setStyle('width', '189px');
22512             });
22513         }
22514         
22515         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22516             if(!this.calendarWeeks){
22517                 v.remove();
22518                 return;
22519             }
22520             
22521             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22522             v.attr('colspan', function(i, val){
22523                 return parseInt(val) + 1;
22524             });
22525         });
22526                         
22527         
22528         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22529         
22530         this.setStartDate(this.startDate);
22531         this.setEndDate(this.endDate);
22532         
22533         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22534         
22535         this.fillDow();
22536         this.fillMonths();
22537         this.update();
22538         this.showMode();
22539         
22540         if(this.isInline) {
22541             this.showPopup();
22542         }
22543     },
22544     
22545     picker : function()
22546     {
22547         return this.pickerEl;
22548 //        return this.el.select('.datepicker', true).first();
22549     },
22550     
22551     fillDow: function()
22552     {
22553         var dowCnt = this.weekStart;
22554         
22555         var dow = {
22556             tag: 'tr',
22557             cn: [
22558                 
22559             ]
22560         };
22561         
22562         if(this.calendarWeeks){
22563             dow.cn.push({
22564                 tag: 'th',
22565                 cls: 'cw',
22566                 html: '&nbsp;'
22567             })
22568         }
22569         
22570         while (dowCnt < this.weekStart + 7) {
22571             dow.cn.push({
22572                 tag: 'th',
22573                 cls: 'dow',
22574                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22575             });
22576         }
22577         
22578         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22579     },
22580     
22581     fillMonths: function()
22582     {    
22583         var i = 0;
22584         var months = this.picker().select('>.datepicker-months td', true).first();
22585         
22586         months.dom.innerHTML = '';
22587         
22588         while (i < 12) {
22589             var month = {
22590                 tag: 'span',
22591                 cls: 'month',
22592                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22593             };
22594             
22595             months.createChild(month);
22596         }
22597         
22598     },
22599     
22600     update: function()
22601     {
22602         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;
22603         
22604         if (this.date < this.startDate) {
22605             this.viewDate = new Date(this.startDate);
22606         } else if (this.date > this.endDate) {
22607             this.viewDate = new Date(this.endDate);
22608         } else {
22609             this.viewDate = new Date(this.date);
22610         }
22611         
22612         this.fill();
22613     },
22614     
22615     fill: function() 
22616     {
22617         var d = new Date(this.viewDate),
22618                 year = d.getUTCFullYear(),
22619                 month = d.getUTCMonth(),
22620                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22621                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22622                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22623                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22624                 currentDate = this.date && this.date.valueOf(),
22625                 today = this.UTCToday();
22626         
22627         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22628         
22629 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22630         
22631 //        this.picker.select('>tfoot th.today').
22632 //                                              .text(dates[this.language].today)
22633 //                                              .toggle(this.todayBtn !== false);
22634     
22635         this.updateNavArrows();
22636         this.fillMonths();
22637                                                 
22638         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22639         
22640         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22641          
22642         prevMonth.setUTCDate(day);
22643         
22644         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22645         
22646         var nextMonth = new Date(prevMonth);
22647         
22648         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22649         
22650         nextMonth = nextMonth.valueOf();
22651         
22652         var fillMonths = false;
22653         
22654         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22655         
22656         while(prevMonth.valueOf() <= nextMonth) {
22657             var clsName = '';
22658             
22659             if (prevMonth.getUTCDay() === this.weekStart) {
22660                 if(fillMonths){
22661                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22662                 }
22663                     
22664                 fillMonths = {
22665                     tag: 'tr',
22666                     cn: []
22667                 };
22668                 
22669                 if(this.calendarWeeks){
22670                     // ISO 8601: First week contains first thursday.
22671                     // ISO also states week starts on Monday, but we can be more abstract here.
22672                     var
22673                     // Start of current week: based on weekstart/current date
22674                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22675                     // Thursday of this week
22676                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22677                     // First Thursday of year, year from thursday
22678                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22679                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22680                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22681                     
22682                     fillMonths.cn.push({
22683                         tag: 'td',
22684                         cls: 'cw',
22685                         html: calWeek
22686                     });
22687                 }
22688             }
22689             
22690             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22691                 clsName += ' old';
22692             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22693                 clsName += ' new';
22694             }
22695             if (this.todayHighlight &&
22696                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22697                 prevMonth.getUTCMonth() == today.getMonth() &&
22698                 prevMonth.getUTCDate() == today.getDate()) {
22699                 clsName += ' today';
22700             }
22701             
22702             if (currentDate && prevMonth.valueOf() === currentDate) {
22703                 clsName += ' active';
22704             }
22705             
22706             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22707                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22708                     clsName += ' disabled';
22709             }
22710             
22711             fillMonths.cn.push({
22712                 tag: 'td',
22713                 cls: 'day ' + clsName,
22714                 html: prevMonth.getDate()
22715             });
22716             
22717             prevMonth.setDate(prevMonth.getDate()+1);
22718         }
22719           
22720         var currentYear = this.date && this.date.getUTCFullYear();
22721         var currentMonth = this.date && this.date.getUTCMonth();
22722         
22723         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22724         
22725         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22726             v.removeClass('active');
22727             
22728             if(currentYear === year && k === currentMonth){
22729                 v.addClass('active');
22730             }
22731             
22732             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22733                 v.addClass('disabled');
22734             }
22735             
22736         });
22737         
22738         
22739         year = parseInt(year/10, 10) * 10;
22740         
22741         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22742         
22743         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22744         
22745         year -= 1;
22746         for (var i = -1; i < 11; i++) {
22747             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22748                 tag: 'span',
22749                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22750                 html: year
22751             });
22752             
22753             year += 1;
22754         }
22755     },
22756     
22757     showMode: function(dir) 
22758     {
22759         if (dir) {
22760             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22761         }
22762         
22763         Roo.each(this.picker().select('>div',true).elements, function(v){
22764             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22765             v.hide();
22766         });
22767         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22768     },
22769     
22770     place: function()
22771     {
22772         if(this.isInline) {
22773             return;
22774         }
22775         
22776         this.picker().removeClass(['bottom', 'top']);
22777         
22778         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22779             /*
22780              * place to the top of element!
22781              *
22782              */
22783             
22784             this.picker().addClass('top');
22785             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22786             
22787             return;
22788         }
22789         
22790         this.picker().addClass('bottom');
22791         
22792         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22793     },
22794     
22795     parseDate : function(value)
22796     {
22797         if(!value || value instanceof Date){
22798             return value;
22799         }
22800         var v = Date.parseDate(value, this.format);
22801         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22802             v = Date.parseDate(value, 'Y-m-d');
22803         }
22804         if(!v && this.altFormats){
22805             if(!this.altFormatsArray){
22806                 this.altFormatsArray = this.altFormats.split("|");
22807             }
22808             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22809                 v = Date.parseDate(value, this.altFormatsArray[i]);
22810             }
22811         }
22812         return v;
22813     },
22814     
22815     formatDate : function(date, fmt)
22816     {   
22817         return (!date || !(date instanceof Date)) ?
22818         date : date.dateFormat(fmt || this.format);
22819     },
22820     
22821     onFocus : function()
22822     {
22823         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22824         this.showPopup();
22825     },
22826     
22827     onBlur : function()
22828     {
22829         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22830         
22831         var d = this.inputEl().getValue();
22832         
22833         this.setValue(d);
22834                 
22835         this.hidePopup();
22836     },
22837     
22838     showPopup : function()
22839     {
22840         this.picker().show();
22841         this.update();
22842         this.place();
22843         
22844         this.fireEvent('showpopup', this, this.date);
22845     },
22846     
22847     hidePopup : function()
22848     {
22849         if(this.isInline) {
22850             return;
22851         }
22852         this.picker().hide();
22853         this.viewMode = this.startViewMode;
22854         this.showMode();
22855         
22856         this.fireEvent('hidepopup', this, this.date);
22857         
22858     },
22859     
22860     onMousedown: function(e)
22861     {
22862         e.stopPropagation();
22863         e.preventDefault();
22864     },
22865     
22866     keyup: function(e)
22867     {
22868         Roo.bootstrap.DateField.superclass.keyup.call(this);
22869         this.update();
22870     },
22871
22872     setValue: function(v)
22873     {
22874         if(this.fireEvent('beforeselect', this, v) !== false){
22875             var d = new Date(this.parseDate(v) ).clearTime();
22876         
22877             if(isNaN(d.getTime())){
22878                 this.date = this.viewDate = '';
22879                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22880                 return;
22881             }
22882
22883             v = this.formatDate(d);
22884
22885             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22886
22887             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22888
22889             this.update();
22890
22891             this.fireEvent('select', this, this.date);
22892         }
22893     },
22894     
22895     getValue: function()
22896     {
22897         return this.formatDate(this.date);
22898     },
22899     
22900     fireKey: function(e)
22901     {
22902         if (!this.picker().isVisible()){
22903             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22904                 this.showPopup();
22905             }
22906             return;
22907         }
22908         
22909         var dateChanged = false,
22910         dir, day, month,
22911         newDate, newViewDate;
22912         
22913         switch(e.keyCode){
22914             case 27: // escape
22915                 this.hidePopup();
22916                 e.preventDefault();
22917                 break;
22918             case 37: // left
22919             case 39: // right
22920                 if (!this.keyboardNavigation) {
22921                     break;
22922                 }
22923                 dir = e.keyCode == 37 ? -1 : 1;
22924                 
22925                 if (e.ctrlKey){
22926                     newDate = this.moveYear(this.date, dir);
22927                     newViewDate = this.moveYear(this.viewDate, dir);
22928                 } else if (e.shiftKey){
22929                     newDate = this.moveMonth(this.date, dir);
22930                     newViewDate = this.moveMonth(this.viewDate, dir);
22931                 } else {
22932                     newDate = new Date(this.date);
22933                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22934                     newViewDate = new Date(this.viewDate);
22935                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22936                 }
22937                 if (this.dateWithinRange(newDate)){
22938                     this.date = newDate;
22939                     this.viewDate = newViewDate;
22940                     this.setValue(this.formatDate(this.date));
22941 //                    this.update();
22942                     e.preventDefault();
22943                     dateChanged = true;
22944                 }
22945                 break;
22946             case 38: // up
22947             case 40: // down
22948                 if (!this.keyboardNavigation) {
22949                     break;
22950                 }
22951                 dir = e.keyCode == 38 ? -1 : 1;
22952                 if (e.ctrlKey){
22953                     newDate = this.moveYear(this.date, dir);
22954                     newViewDate = this.moveYear(this.viewDate, dir);
22955                 } else if (e.shiftKey){
22956                     newDate = this.moveMonth(this.date, dir);
22957                     newViewDate = this.moveMonth(this.viewDate, dir);
22958                 } else {
22959                     newDate = new Date(this.date);
22960                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22961                     newViewDate = new Date(this.viewDate);
22962                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22963                 }
22964                 if (this.dateWithinRange(newDate)){
22965                     this.date = newDate;
22966                     this.viewDate = newViewDate;
22967                     this.setValue(this.formatDate(this.date));
22968 //                    this.update();
22969                     e.preventDefault();
22970                     dateChanged = true;
22971                 }
22972                 break;
22973             case 13: // enter
22974                 this.setValue(this.formatDate(this.date));
22975                 this.hidePopup();
22976                 e.preventDefault();
22977                 break;
22978             case 9: // tab
22979                 this.setValue(this.formatDate(this.date));
22980                 this.hidePopup();
22981                 break;
22982             case 16: // shift
22983             case 17: // ctrl
22984             case 18: // alt
22985                 break;
22986             default :
22987                 this.hidePopup();
22988                 
22989         }
22990     },
22991     
22992     
22993     onClick: function(e) 
22994     {
22995         e.stopPropagation();
22996         e.preventDefault();
22997         
22998         var target = e.getTarget();
22999         
23000         if(target.nodeName.toLowerCase() === 'i'){
23001             target = Roo.get(target).dom.parentNode;
23002         }
23003         
23004         var nodeName = target.nodeName;
23005         var className = target.className;
23006         var html = target.innerHTML;
23007         //Roo.log(nodeName);
23008         
23009         switch(nodeName.toLowerCase()) {
23010             case 'th':
23011                 switch(className) {
23012                     case 'switch':
23013                         this.showMode(1);
23014                         break;
23015                     case 'prev':
23016                     case 'next':
23017                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23018                         switch(this.viewMode){
23019                                 case 0:
23020                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23021                                         break;
23022                                 case 1:
23023                                 case 2:
23024                                         this.viewDate = this.moveYear(this.viewDate, dir);
23025                                         break;
23026                         }
23027                         this.fill();
23028                         break;
23029                     case 'today':
23030                         var date = new Date();
23031                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23032 //                        this.fill()
23033                         this.setValue(this.formatDate(this.date));
23034                         
23035                         this.hidePopup();
23036                         break;
23037                 }
23038                 break;
23039             case 'span':
23040                 if (className.indexOf('disabled') < 0) {
23041                 if (!this.viewDate) {
23042                     this.viewDate = new Date();
23043                 }
23044                 this.viewDate.setUTCDate(1);
23045                     if (className.indexOf('month') > -1) {
23046                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23047                     } else {
23048                         var year = parseInt(html, 10) || 0;
23049                         this.viewDate.setUTCFullYear(year);
23050                         
23051                     }
23052                     
23053                     if(this.singleMode){
23054                         this.setValue(this.formatDate(this.viewDate));
23055                         this.hidePopup();
23056                         return;
23057                     }
23058                     
23059                     this.showMode(-1);
23060                     this.fill();
23061                 }
23062                 break;
23063                 
23064             case 'td':
23065                 //Roo.log(className);
23066                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23067                     var day = parseInt(html, 10) || 1;
23068                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23069                         month = (this.viewDate || new Date()).getUTCMonth();
23070
23071                     if (className.indexOf('old') > -1) {
23072                         if(month === 0 ){
23073                             month = 11;
23074                             year -= 1;
23075                         }else{
23076                             month -= 1;
23077                         }
23078                     } else if (className.indexOf('new') > -1) {
23079                         if (month == 11) {
23080                             month = 0;
23081                             year += 1;
23082                         } else {
23083                             month += 1;
23084                         }
23085                     }
23086                     //Roo.log([year,month,day]);
23087                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23088                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23089 //                    this.fill();
23090                     //Roo.log(this.formatDate(this.date));
23091                     this.setValue(this.formatDate(this.date));
23092                     this.hidePopup();
23093                 }
23094                 break;
23095         }
23096     },
23097     
23098     setStartDate: function(startDate)
23099     {
23100         this.startDate = startDate || -Infinity;
23101         if (this.startDate !== -Infinity) {
23102             this.startDate = this.parseDate(this.startDate);
23103         }
23104         this.update();
23105         this.updateNavArrows();
23106     },
23107
23108     setEndDate: function(endDate)
23109     {
23110         this.endDate = endDate || Infinity;
23111         if (this.endDate !== Infinity) {
23112             this.endDate = this.parseDate(this.endDate);
23113         }
23114         this.update();
23115         this.updateNavArrows();
23116     },
23117     
23118     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23119     {
23120         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23121         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23122             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23123         }
23124         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23125             return parseInt(d, 10);
23126         });
23127         this.update();
23128         this.updateNavArrows();
23129     },
23130     
23131     updateNavArrows: function() 
23132     {
23133         if(this.singleMode){
23134             return;
23135         }
23136         
23137         var d = new Date(this.viewDate),
23138         year = d.getUTCFullYear(),
23139         month = d.getUTCMonth();
23140         
23141         Roo.each(this.picker().select('.prev', true).elements, function(v){
23142             v.show();
23143             switch (this.viewMode) {
23144                 case 0:
23145
23146                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23147                         v.hide();
23148                     }
23149                     break;
23150                 case 1:
23151                 case 2:
23152                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23153                         v.hide();
23154                     }
23155                     break;
23156             }
23157         });
23158         
23159         Roo.each(this.picker().select('.next', true).elements, function(v){
23160             v.show();
23161             switch (this.viewMode) {
23162                 case 0:
23163
23164                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23165                         v.hide();
23166                     }
23167                     break;
23168                 case 1:
23169                 case 2:
23170                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23171                         v.hide();
23172                     }
23173                     break;
23174             }
23175         })
23176     },
23177     
23178     moveMonth: function(date, dir)
23179     {
23180         if (!dir) {
23181             return date;
23182         }
23183         var new_date = new Date(date.valueOf()),
23184         day = new_date.getUTCDate(),
23185         month = new_date.getUTCMonth(),
23186         mag = Math.abs(dir),
23187         new_month, test;
23188         dir = dir > 0 ? 1 : -1;
23189         if (mag == 1){
23190             test = dir == -1
23191             // If going back one month, make sure month is not current month
23192             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23193             ? function(){
23194                 return new_date.getUTCMonth() == month;
23195             }
23196             // If going forward one month, make sure month is as expected
23197             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23198             : function(){
23199                 return new_date.getUTCMonth() != new_month;
23200             };
23201             new_month = month + dir;
23202             new_date.setUTCMonth(new_month);
23203             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23204             if (new_month < 0 || new_month > 11) {
23205                 new_month = (new_month + 12) % 12;
23206             }
23207         } else {
23208             // For magnitudes >1, move one month at a time...
23209             for (var i=0; i<mag; i++) {
23210                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23211                 new_date = this.moveMonth(new_date, dir);
23212             }
23213             // ...then reset the day, keeping it in the new month
23214             new_month = new_date.getUTCMonth();
23215             new_date.setUTCDate(day);
23216             test = function(){
23217                 return new_month != new_date.getUTCMonth();
23218             };
23219         }
23220         // Common date-resetting loop -- if date is beyond end of month, make it
23221         // end of month
23222         while (test()){
23223             new_date.setUTCDate(--day);
23224             new_date.setUTCMonth(new_month);
23225         }
23226         return new_date;
23227     },
23228
23229     moveYear: function(date, dir)
23230     {
23231         return this.moveMonth(date, dir*12);
23232     },
23233
23234     dateWithinRange: function(date)
23235     {
23236         return date >= this.startDate && date <= this.endDate;
23237     },
23238
23239     
23240     remove: function() 
23241     {
23242         this.picker().remove();
23243     },
23244     
23245     validateValue : function(value)
23246     {
23247         if(this.getVisibilityEl().hasClass('hidden')){
23248             return true;
23249         }
23250         
23251         if(value.length < 1)  {
23252             if(this.allowBlank){
23253                 return true;
23254             }
23255             return false;
23256         }
23257         
23258         if(value.length < this.minLength){
23259             return false;
23260         }
23261         if(value.length > this.maxLength){
23262             return false;
23263         }
23264         if(this.vtype){
23265             var vt = Roo.form.VTypes;
23266             if(!vt[this.vtype](value, this)){
23267                 return false;
23268             }
23269         }
23270         if(typeof this.validator == "function"){
23271             var msg = this.validator(value);
23272             if(msg !== true){
23273                 return false;
23274             }
23275         }
23276         
23277         if(this.regex && !this.regex.test(value)){
23278             return false;
23279         }
23280         
23281         if(typeof(this.parseDate(value)) == 'undefined'){
23282             return false;
23283         }
23284         
23285         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23286             return false;
23287         }      
23288         
23289         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23290             return false;
23291         } 
23292         
23293         
23294         return true;
23295     },
23296     
23297     reset : function()
23298     {
23299         this.date = this.viewDate = '';
23300         
23301         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23302     }
23303    
23304 });
23305
23306 Roo.apply(Roo.bootstrap.DateField,  {
23307     
23308     head : {
23309         tag: 'thead',
23310         cn: [
23311         {
23312             tag: 'tr',
23313             cn: [
23314             {
23315                 tag: 'th',
23316                 cls: 'prev',
23317                 html: '<i class="fa fa-arrow-left"/>'
23318             },
23319             {
23320                 tag: 'th',
23321                 cls: 'switch',
23322                 colspan: '5'
23323             },
23324             {
23325                 tag: 'th',
23326                 cls: 'next',
23327                 html: '<i class="fa fa-arrow-right"/>'
23328             }
23329
23330             ]
23331         }
23332         ]
23333     },
23334     
23335     content : {
23336         tag: 'tbody',
23337         cn: [
23338         {
23339             tag: 'tr',
23340             cn: [
23341             {
23342                 tag: 'td',
23343                 colspan: '7'
23344             }
23345             ]
23346         }
23347         ]
23348     },
23349     
23350     footer : {
23351         tag: 'tfoot',
23352         cn: [
23353         {
23354             tag: 'tr',
23355             cn: [
23356             {
23357                 tag: 'th',
23358                 colspan: '7',
23359                 cls: 'today'
23360             }
23361                     
23362             ]
23363         }
23364         ]
23365     },
23366     
23367     dates:{
23368         en: {
23369             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23370             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23371             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23372             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23373             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23374             today: "Today"
23375         }
23376     },
23377     
23378     modes: [
23379     {
23380         clsName: 'days',
23381         navFnc: 'Month',
23382         navStep: 1
23383     },
23384     {
23385         clsName: 'months',
23386         navFnc: 'FullYear',
23387         navStep: 1
23388     },
23389     {
23390         clsName: 'years',
23391         navFnc: 'FullYear',
23392         navStep: 10
23393     }]
23394 });
23395
23396 Roo.apply(Roo.bootstrap.DateField,  {
23397   
23398     template : {
23399         tag: 'div',
23400         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23401         cn: [
23402         {
23403             tag: 'div',
23404             cls: 'datepicker-days',
23405             cn: [
23406             {
23407                 tag: 'table',
23408                 cls: 'table-condensed',
23409                 cn:[
23410                 Roo.bootstrap.DateField.head,
23411                 {
23412                     tag: 'tbody'
23413                 },
23414                 Roo.bootstrap.DateField.footer
23415                 ]
23416             }
23417             ]
23418         },
23419         {
23420             tag: 'div',
23421             cls: 'datepicker-months',
23422             cn: [
23423             {
23424                 tag: 'table',
23425                 cls: 'table-condensed',
23426                 cn:[
23427                 Roo.bootstrap.DateField.head,
23428                 Roo.bootstrap.DateField.content,
23429                 Roo.bootstrap.DateField.footer
23430                 ]
23431             }
23432             ]
23433         },
23434         {
23435             tag: 'div',
23436             cls: 'datepicker-years',
23437             cn: [
23438             {
23439                 tag: 'table',
23440                 cls: 'table-condensed',
23441                 cn:[
23442                 Roo.bootstrap.DateField.head,
23443                 Roo.bootstrap.DateField.content,
23444                 Roo.bootstrap.DateField.footer
23445                 ]
23446             }
23447             ]
23448         }
23449         ]
23450     }
23451 });
23452
23453  
23454
23455  /*
23456  * - LGPL
23457  *
23458  * TimeField
23459  * 
23460  */
23461
23462 /**
23463  * @class Roo.bootstrap.TimeField
23464  * @extends Roo.bootstrap.Input
23465  * Bootstrap DateField class
23466  * 
23467  * 
23468  * @constructor
23469  * Create a new TimeField
23470  * @param {Object} config The config object
23471  */
23472
23473 Roo.bootstrap.TimeField = function(config){
23474     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23475     this.addEvents({
23476             /**
23477              * @event show
23478              * Fires when this field show.
23479              * @param {Roo.bootstrap.DateField} thisthis
23480              * @param {Mixed} date The date value
23481              */
23482             show : true,
23483             /**
23484              * @event show
23485              * Fires when this field hide.
23486              * @param {Roo.bootstrap.DateField} this
23487              * @param {Mixed} date The date value
23488              */
23489             hide : true,
23490             /**
23491              * @event select
23492              * Fires when select a date.
23493              * @param {Roo.bootstrap.DateField} this
23494              * @param {Mixed} date The date value
23495              */
23496             select : true
23497         });
23498 };
23499
23500 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23501     
23502     /**
23503      * @cfg {String} format
23504      * The default time format string which can be overriden for localization support.  The format must be
23505      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23506      */
23507     format : "H:i",
23508
23509     getAutoCreate : function()
23510     {
23511         this.after = '<i class="fa far fa-clock"></i>';
23512         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23513         
23514          
23515     },
23516     onRender: function(ct, position)
23517     {
23518         
23519         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23520                 
23521         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23522         
23523         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23524         
23525         this.pop = this.picker().select('>.datepicker-time',true).first();
23526         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23527         
23528         this.picker().on('mousedown', this.onMousedown, this);
23529         this.picker().on('click', this.onClick, this);
23530         
23531         this.picker().addClass('datepicker-dropdown');
23532     
23533         this.fillTime();
23534         this.update();
23535             
23536         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23537         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23538         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23539         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23540         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23541         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23542
23543     },
23544     
23545     fireKey: function(e){
23546         if (!this.picker().isVisible()){
23547             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23548                 this.show();
23549             }
23550             return;
23551         }
23552
23553         e.preventDefault();
23554         
23555         switch(e.keyCode){
23556             case 27: // escape
23557                 this.hide();
23558                 break;
23559             case 37: // left
23560             case 39: // right
23561                 this.onTogglePeriod();
23562                 break;
23563             case 38: // up
23564                 this.onIncrementMinutes();
23565                 break;
23566             case 40: // down
23567                 this.onDecrementMinutes();
23568                 break;
23569             case 13: // enter
23570             case 9: // tab
23571                 this.setTime();
23572                 break;
23573         }
23574     },
23575     
23576     onClick: function(e) {
23577         e.stopPropagation();
23578         e.preventDefault();
23579     },
23580     
23581     picker : function()
23582     {
23583         return this.pickerEl;
23584     },
23585     
23586     fillTime: function()
23587     {    
23588         var time = this.pop.select('tbody', true).first();
23589         
23590         time.dom.innerHTML = '';
23591         
23592         time.createChild({
23593             tag: 'tr',
23594             cn: [
23595                 {
23596                     tag: 'td',
23597                     cn: [
23598                         {
23599                             tag: 'a',
23600                             href: '#',
23601                             cls: 'btn',
23602                             cn: [
23603                                 {
23604                                     tag: 'i',
23605                                     cls: 'hours-up fa fas fa-chevron-up'
23606                                 }
23607                             ]
23608                         } 
23609                     ]
23610                 },
23611                 {
23612                     tag: 'td',
23613                     cls: 'separator'
23614                 },
23615                 {
23616                     tag: 'td',
23617                     cn: [
23618                         {
23619                             tag: 'a',
23620                             href: '#',
23621                             cls: 'btn',
23622                             cn: [
23623                                 {
23624                                     tag: 'i',
23625                                     cls: 'minutes-up fa fas fa-chevron-up'
23626                                 }
23627                             ]
23628                         }
23629                     ]
23630                 },
23631                 {
23632                     tag: 'td',
23633                     cls: 'separator'
23634                 }
23635             ]
23636         });
23637         
23638         time.createChild({
23639             tag: 'tr',
23640             cn: [
23641                 {
23642                     tag: 'td',
23643                     cn: [
23644                         {
23645                             tag: 'span',
23646                             cls: 'timepicker-hour',
23647                             html: '00'
23648                         }  
23649                     ]
23650                 },
23651                 {
23652                     tag: 'td',
23653                     cls: 'separator',
23654                     html: ':'
23655                 },
23656                 {
23657                     tag: 'td',
23658                     cn: [
23659                         {
23660                             tag: 'span',
23661                             cls: 'timepicker-minute',
23662                             html: '00'
23663                         }  
23664                     ]
23665                 },
23666                 {
23667                     tag: 'td',
23668                     cls: 'separator'
23669                 },
23670                 {
23671                     tag: 'td',
23672                     cn: [
23673                         {
23674                             tag: 'button',
23675                             type: 'button',
23676                             cls: 'btn btn-primary period',
23677                             html: 'AM'
23678                             
23679                         }
23680                     ]
23681                 }
23682             ]
23683         });
23684         
23685         time.createChild({
23686             tag: 'tr',
23687             cn: [
23688                 {
23689                     tag: 'td',
23690                     cn: [
23691                         {
23692                             tag: 'a',
23693                             href: '#',
23694                             cls: 'btn',
23695                             cn: [
23696                                 {
23697                                     tag: 'span',
23698                                     cls: 'hours-down fa fas fa-chevron-down'
23699                                 }
23700                             ]
23701                         }
23702                     ]
23703                 },
23704                 {
23705                     tag: 'td',
23706                     cls: 'separator'
23707                 },
23708                 {
23709                     tag: 'td',
23710                     cn: [
23711                         {
23712                             tag: 'a',
23713                             href: '#',
23714                             cls: 'btn',
23715                             cn: [
23716                                 {
23717                                     tag: 'span',
23718                                     cls: 'minutes-down fa fas fa-chevron-down'
23719                                 }
23720                             ]
23721                         }
23722                     ]
23723                 },
23724                 {
23725                     tag: 'td',
23726                     cls: 'separator'
23727                 }
23728             ]
23729         });
23730         
23731     },
23732     
23733     update: function()
23734     {
23735         
23736         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23737         
23738         this.fill();
23739     },
23740     
23741     fill: function() 
23742     {
23743         var hours = this.time.getHours();
23744         var minutes = this.time.getMinutes();
23745         var period = 'AM';
23746         
23747         if(hours > 11){
23748             period = 'PM';
23749         }
23750         
23751         if(hours == 0){
23752             hours = 12;
23753         }
23754         
23755         
23756         if(hours > 12){
23757             hours = hours - 12;
23758         }
23759         
23760         if(hours < 10){
23761             hours = '0' + hours;
23762         }
23763         
23764         if(minutes < 10){
23765             minutes = '0' + minutes;
23766         }
23767         
23768         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23769         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23770         this.pop.select('button', true).first().dom.innerHTML = period;
23771         
23772     },
23773     
23774     place: function()
23775     {   
23776         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23777         
23778         var cls = ['bottom'];
23779         
23780         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23781             cls.pop();
23782             cls.push('top');
23783         }
23784         
23785         cls.push('right');
23786         
23787         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23788             cls.pop();
23789             cls.push('left');
23790         }
23791         //this.picker().setXY(20000,20000);
23792         this.picker().addClass(cls.join('-'));
23793         
23794         var _this = this;
23795         
23796         Roo.each(cls, function(c){
23797             if(c == 'bottom'){
23798                 (function() {
23799                  //  
23800                 }).defer(200);
23801                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23802                 //_this.picker().setTop(_this.inputEl().getHeight());
23803                 return;
23804             }
23805             if(c == 'top'){
23806                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23807                 
23808                 //_this.picker().setTop(0 - _this.picker().getHeight());
23809                 return;
23810             }
23811             /*
23812             if(c == 'left'){
23813                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23814                 return;
23815             }
23816             if(c == 'right'){
23817                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23818                 return;
23819             }
23820             */
23821         });
23822         
23823     },
23824   
23825     onFocus : function()
23826     {
23827         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23828         this.show();
23829     },
23830     
23831     onBlur : function()
23832     {
23833         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23834         this.hide();
23835     },
23836     
23837     show : function()
23838     {
23839         this.picker().show();
23840         this.pop.show();
23841         this.update();
23842         this.place();
23843         
23844         this.fireEvent('show', this, this.date);
23845     },
23846     
23847     hide : function()
23848     {
23849         this.picker().hide();
23850         this.pop.hide();
23851         
23852         this.fireEvent('hide', this, this.date);
23853     },
23854     
23855     setTime : function()
23856     {
23857         this.hide();
23858         this.setValue(this.time.format(this.format));
23859         
23860         this.fireEvent('select', this, this.date);
23861         
23862         
23863     },
23864     
23865     onMousedown: function(e){
23866         e.stopPropagation();
23867         e.preventDefault();
23868     },
23869     
23870     onIncrementHours: function()
23871     {
23872         Roo.log('onIncrementHours');
23873         this.time = this.time.add(Date.HOUR, 1);
23874         this.update();
23875         
23876     },
23877     
23878     onDecrementHours: function()
23879     {
23880         Roo.log('onDecrementHours');
23881         this.time = this.time.add(Date.HOUR, -1);
23882         this.update();
23883     },
23884     
23885     onIncrementMinutes: function()
23886     {
23887         Roo.log('onIncrementMinutes');
23888         this.time = this.time.add(Date.MINUTE, 1);
23889         this.update();
23890     },
23891     
23892     onDecrementMinutes: function()
23893     {
23894         Roo.log('onDecrementMinutes');
23895         this.time = this.time.add(Date.MINUTE, -1);
23896         this.update();
23897     },
23898     
23899     onTogglePeriod: function()
23900     {
23901         Roo.log('onTogglePeriod');
23902         this.time = this.time.add(Date.HOUR, 12);
23903         this.update();
23904     }
23905     
23906    
23907 });
23908  
23909
23910 Roo.apply(Roo.bootstrap.TimeField,  {
23911   
23912     template : {
23913         tag: 'div',
23914         cls: 'datepicker dropdown-menu',
23915         cn: [
23916             {
23917                 tag: 'div',
23918                 cls: 'datepicker-time',
23919                 cn: [
23920                 {
23921                     tag: 'table',
23922                     cls: 'table-condensed',
23923                     cn:[
23924                         {
23925                             tag: 'tbody',
23926                             cn: [
23927                                 {
23928                                     tag: 'tr',
23929                                     cn: [
23930                                     {
23931                                         tag: 'td',
23932                                         colspan: '7'
23933                                     }
23934                                     ]
23935                                 }
23936                             ]
23937                         },
23938                         {
23939                             tag: 'tfoot',
23940                             cn: [
23941                                 {
23942                                     tag: 'tr',
23943                                     cn: [
23944                                     {
23945                                         tag: 'th',
23946                                         colspan: '7',
23947                                         cls: '',
23948                                         cn: [
23949                                             {
23950                                                 tag: 'button',
23951                                                 cls: 'btn btn-info ok',
23952                                                 html: 'OK'
23953                                             }
23954                                         ]
23955                                     }
23956                     
23957                                     ]
23958                                 }
23959                             ]
23960                         }
23961                     ]
23962                 }
23963                 ]
23964             }
23965         ]
23966     }
23967 });
23968
23969  
23970
23971  /*
23972  * - LGPL
23973  *
23974  * MonthField
23975  * 
23976  */
23977
23978 /**
23979  * @class Roo.bootstrap.MonthField
23980  * @extends Roo.bootstrap.Input
23981  * Bootstrap MonthField class
23982  * 
23983  * @cfg {String} language default en
23984  * 
23985  * @constructor
23986  * Create a new MonthField
23987  * @param {Object} config The config object
23988  */
23989
23990 Roo.bootstrap.MonthField = function(config){
23991     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23992     
23993     this.addEvents({
23994         /**
23995          * @event show
23996          * Fires when this field show.
23997          * @param {Roo.bootstrap.MonthField} this
23998          * @param {Mixed} date The date value
23999          */
24000         show : true,
24001         /**
24002          * @event show
24003          * Fires when this field hide.
24004          * @param {Roo.bootstrap.MonthField} this
24005          * @param {Mixed} date The date value
24006          */
24007         hide : true,
24008         /**
24009          * @event select
24010          * Fires when select a date.
24011          * @param {Roo.bootstrap.MonthField} this
24012          * @param {String} oldvalue The old value
24013          * @param {String} newvalue The new value
24014          */
24015         select : true
24016     });
24017 };
24018
24019 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24020     
24021     onRender: function(ct, position)
24022     {
24023         
24024         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24025         
24026         this.language = this.language || 'en';
24027         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24028         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24029         
24030         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24031         this.isInline = false;
24032         this.isInput = true;
24033         this.component = this.el.select('.add-on', true).first() || false;
24034         this.component = (this.component && this.component.length === 0) ? false : this.component;
24035         this.hasInput = this.component && this.inputEL().length;
24036         
24037         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24038         
24039         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24040         
24041         this.picker().on('mousedown', this.onMousedown, this);
24042         this.picker().on('click', this.onClick, this);
24043         
24044         this.picker().addClass('datepicker-dropdown');
24045         
24046         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24047             v.setStyle('width', '189px');
24048         });
24049         
24050         this.fillMonths();
24051         
24052         this.update();
24053         
24054         if(this.isInline) {
24055             this.show();
24056         }
24057         
24058     },
24059     
24060     setValue: function(v, suppressEvent)
24061     {   
24062         var o = this.getValue();
24063         
24064         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24065         
24066         this.update();
24067
24068         if(suppressEvent !== true){
24069             this.fireEvent('select', this, o, v);
24070         }
24071         
24072     },
24073     
24074     getValue: function()
24075     {
24076         return this.value;
24077     },
24078     
24079     onClick: function(e) 
24080     {
24081         e.stopPropagation();
24082         e.preventDefault();
24083         
24084         var target = e.getTarget();
24085         
24086         if(target.nodeName.toLowerCase() === 'i'){
24087             target = Roo.get(target).dom.parentNode;
24088         }
24089         
24090         var nodeName = target.nodeName;
24091         var className = target.className;
24092         var html = target.innerHTML;
24093         
24094         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24095             return;
24096         }
24097         
24098         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24099         
24100         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24101         
24102         this.hide();
24103                         
24104     },
24105     
24106     picker : function()
24107     {
24108         return this.pickerEl;
24109     },
24110     
24111     fillMonths: function()
24112     {    
24113         var i = 0;
24114         var months = this.picker().select('>.datepicker-months td', true).first();
24115         
24116         months.dom.innerHTML = '';
24117         
24118         while (i < 12) {
24119             var month = {
24120                 tag: 'span',
24121                 cls: 'month',
24122                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24123             };
24124             
24125             months.createChild(month);
24126         }
24127         
24128     },
24129     
24130     update: function()
24131     {
24132         var _this = this;
24133         
24134         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24135             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24136         }
24137         
24138         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24139             e.removeClass('active');
24140             
24141             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24142                 e.addClass('active');
24143             }
24144         })
24145     },
24146     
24147     place: function()
24148     {
24149         if(this.isInline) {
24150             return;
24151         }
24152         
24153         this.picker().removeClass(['bottom', 'top']);
24154         
24155         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24156             /*
24157              * place to the top of element!
24158              *
24159              */
24160             
24161             this.picker().addClass('top');
24162             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24163             
24164             return;
24165         }
24166         
24167         this.picker().addClass('bottom');
24168         
24169         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24170     },
24171     
24172     onFocus : function()
24173     {
24174         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24175         this.show();
24176     },
24177     
24178     onBlur : function()
24179     {
24180         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24181         
24182         var d = this.inputEl().getValue();
24183         
24184         this.setValue(d);
24185                 
24186         this.hide();
24187     },
24188     
24189     show : function()
24190     {
24191         this.picker().show();
24192         this.picker().select('>.datepicker-months', true).first().show();
24193         this.update();
24194         this.place();
24195         
24196         this.fireEvent('show', this, this.date);
24197     },
24198     
24199     hide : function()
24200     {
24201         if(this.isInline) {
24202             return;
24203         }
24204         this.picker().hide();
24205         this.fireEvent('hide', this, this.date);
24206         
24207     },
24208     
24209     onMousedown: function(e)
24210     {
24211         e.stopPropagation();
24212         e.preventDefault();
24213     },
24214     
24215     keyup: function(e)
24216     {
24217         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24218         this.update();
24219     },
24220
24221     fireKey: function(e)
24222     {
24223         if (!this.picker().isVisible()){
24224             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24225                 this.show();
24226             }
24227             return;
24228         }
24229         
24230         var dir;
24231         
24232         switch(e.keyCode){
24233             case 27: // escape
24234                 this.hide();
24235                 e.preventDefault();
24236                 break;
24237             case 37: // left
24238             case 39: // right
24239                 dir = e.keyCode == 37 ? -1 : 1;
24240                 
24241                 this.vIndex = this.vIndex + dir;
24242                 
24243                 if(this.vIndex < 0){
24244                     this.vIndex = 0;
24245                 }
24246                 
24247                 if(this.vIndex > 11){
24248                     this.vIndex = 11;
24249                 }
24250                 
24251                 if(isNaN(this.vIndex)){
24252                     this.vIndex = 0;
24253                 }
24254                 
24255                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24256                 
24257                 break;
24258             case 38: // up
24259             case 40: // down
24260                 
24261                 dir = e.keyCode == 38 ? -1 : 1;
24262                 
24263                 this.vIndex = this.vIndex + dir * 4;
24264                 
24265                 if(this.vIndex < 0){
24266                     this.vIndex = 0;
24267                 }
24268                 
24269                 if(this.vIndex > 11){
24270                     this.vIndex = 11;
24271                 }
24272                 
24273                 if(isNaN(this.vIndex)){
24274                     this.vIndex = 0;
24275                 }
24276                 
24277                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24278                 break;
24279                 
24280             case 13: // enter
24281                 
24282                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24283                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24284                 }
24285                 
24286                 this.hide();
24287                 e.preventDefault();
24288                 break;
24289             case 9: // tab
24290                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24291                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24292                 }
24293                 this.hide();
24294                 break;
24295             case 16: // shift
24296             case 17: // ctrl
24297             case 18: // alt
24298                 break;
24299             default :
24300                 this.hide();
24301                 
24302         }
24303     },
24304     
24305     remove: function() 
24306     {
24307         this.picker().remove();
24308     }
24309    
24310 });
24311
24312 Roo.apply(Roo.bootstrap.MonthField,  {
24313     
24314     content : {
24315         tag: 'tbody',
24316         cn: [
24317         {
24318             tag: 'tr',
24319             cn: [
24320             {
24321                 tag: 'td',
24322                 colspan: '7'
24323             }
24324             ]
24325         }
24326         ]
24327     },
24328     
24329     dates:{
24330         en: {
24331             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24332             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24333         }
24334     }
24335 });
24336
24337 Roo.apply(Roo.bootstrap.MonthField,  {
24338   
24339     template : {
24340         tag: 'div',
24341         cls: 'datepicker dropdown-menu roo-dynamic',
24342         cn: [
24343             {
24344                 tag: 'div',
24345                 cls: 'datepicker-months',
24346                 cn: [
24347                 {
24348                     tag: 'table',
24349                     cls: 'table-condensed',
24350                     cn:[
24351                         Roo.bootstrap.DateField.content
24352                     ]
24353                 }
24354                 ]
24355             }
24356         ]
24357     }
24358 });
24359
24360  
24361
24362  
24363  /*
24364  * - LGPL
24365  *
24366  * CheckBox
24367  * 
24368  */
24369
24370 /**
24371  * @class Roo.bootstrap.CheckBox
24372  * @extends Roo.bootstrap.Input
24373  * Bootstrap CheckBox class
24374  * 
24375  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24376  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24377  * @cfg {String} boxLabel The text that appears beside the checkbox
24378  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24379  * @cfg {Boolean} checked initnal the element
24380  * @cfg {Boolean} inline inline the element (default false)
24381  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24382  * @cfg {String} tooltip label tooltip
24383  * 
24384  * @constructor
24385  * Create a new CheckBox
24386  * @param {Object} config The config object
24387  */
24388
24389 Roo.bootstrap.CheckBox = function(config){
24390     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24391    
24392     this.addEvents({
24393         /**
24394         * @event check
24395         * Fires when the element is checked or unchecked.
24396         * @param {Roo.bootstrap.CheckBox} this This input
24397         * @param {Boolean} checked The new checked value
24398         */
24399        check : true,
24400        /**
24401         * @event click
24402         * Fires when the element is click.
24403         * @param {Roo.bootstrap.CheckBox} this This input
24404         */
24405        click : true
24406     });
24407     
24408 };
24409
24410 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24411   
24412     inputType: 'checkbox',
24413     inputValue: 1,
24414     valueOff: 0,
24415     boxLabel: false,
24416     checked: false,
24417     weight : false,
24418     inline: false,
24419     tooltip : '',
24420     
24421     // checkbox success does not make any sense really.. 
24422     invalidClass : "",
24423     validClass : "",
24424     
24425     
24426     getAutoCreate : function()
24427     {
24428         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24429         
24430         var id = Roo.id();
24431         
24432         var cfg = {};
24433         
24434         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24435         
24436         if(this.inline){
24437             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24438         }
24439         
24440         var input =  {
24441             tag: 'input',
24442             id : id,
24443             type : this.inputType,
24444             value : this.inputValue,
24445             cls : 'roo-' + this.inputType, //'form-box',
24446             placeholder : this.placeholder || ''
24447             
24448         };
24449         
24450         if(this.inputType != 'radio'){
24451             var hidden =  {
24452                 tag: 'input',
24453                 type : 'hidden',
24454                 cls : 'roo-hidden-value',
24455                 value : this.checked ? this.inputValue : this.valueOff
24456             };
24457         }
24458         
24459             
24460         if (this.weight) { // Validity check?
24461             cfg.cls += " " + this.inputType + "-" + this.weight;
24462         }
24463         
24464         if (this.disabled) {
24465             input.disabled=true;
24466         }
24467         
24468         if(this.checked){
24469             input.checked = this.checked;
24470         }
24471         
24472         if (this.name) {
24473             
24474             input.name = this.name;
24475             
24476             if(this.inputType != 'radio'){
24477                 hidden.name = this.name;
24478                 input.name = '_hidden_' + this.name;
24479             }
24480         }
24481         
24482         if (this.size) {
24483             input.cls += ' input-' + this.size;
24484         }
24485         
24486         var settings=this;
24487         
24488         ['xs','sm','md','lg'].map(function(size){
24489             if (settings[size]) {
24490                 cfg.cls += ' col-' + size + '-' + settings[size];
24491             }
24492         });
24493         
24494         var inputblock = input;
24495          
24496         if (this.before || this.after) {
24497             
24498             inputblock = {
24499                 cls : 'input-group',
24500                 cn :  [] 
24501             };
24502             
24503             if (this.before) {
24504                 inputblock.cn.push({
24505                     tag :'span',
24506                     cls : 'input-group-addon',
24507                     html : this.before
24508                 });
24509             }
24510             
24511             inputblock.cn.push(input);
24512             
24513             if(this.inputType != 'radio'){
24514                 inputblock.cn.push(hidden);
24515             }
24516             
24517             if (this.after) {
24518                 inputblock.cn.push({
24519                     tag :'span',
24520                     cls : 'input-group-addon',
24521                     html : this.after
24522                 });
24523             }
24524             
24525         }
24526         var boxLabelCfg = false;
24527         
24528         if(this.boxLabel){
24529            
24530             boxLabelCfg = {
24531                 tag: 'label',
24532                 //'for': id, // box label is handled by onclick - so no for...
24533                 cls: 'box-label',
24534                 html: this.boxLabel
24535             };
24536             if(this.tooltip){
24537                 boxLabelCfg.tooltip = this.tooltip;
24538             }
24539              
24540         }
24541         
24542         
24543         if (align ==='left' && this.fieldLabel.length) {
24544 //                Roo.log("left and has label");
24545             cfg.cn = [
24546                 {
24547                     tag: 'label',
24548                     'for' :  id,
24549                     cls : 'control-label',
24550                     html : this.fieldLabel
24551                 },
24552                 {
24553                     cls : "", 
24554                     cn: [
24555                         inputblock
24556                     ]
24557                 }
24558             ];
24559             
24560             if (boxLabelCfg) {
24561                 cfg.cn[1].cn.push(boxLabelCfg);
24562             }
24563             
24564             if(this.labelWidth > 12){
24565                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24566             }
24567             
24568             if(this.labelWidth < 13 && this.labelmd == 0){
24569                 this.labelmd = this.labelWidth;
24570             }
24571             
24572             if(this.labellg > 0){
24573                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24574                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24575             }
24576             
24577             if(this.labelmd > 0){
24578                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24579                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24580             }
24581             
24582             if(this.labelsm > 0){
24583                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24584                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24585             }
24586             
24587             if(this.labelxs > 0){
24588                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24589                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24590             }
24591             
24592         } else if ( this.fieldLabel.length) {
24593 //                Roo.log(" label");
24594                 cfg.cn = [
24595                    
24596                     {
24597                         tag: this.boxLabel ? 'span' : 'label',
24598                         'for': id,
24599                         cls: 'control-label box-input-label',
24600                         //cls : 'input-group-addon',
24601                         html : this.fieldLabel
24602                     },
24603                     
24604                     inputblock
24605                     
24606                 ];
24607                 if (boxLabelCfg) {
24608                     cfg.cn.push(boxLabelCfg);
24609                 }
24610
24611         } else {
24612             
24613 //                Roo.log(" no label && no align");
24614                 cfg.cn = [  inputblock ] ;
24615                 if (boxLabelCfg) {
24616                     cfg.cn.push(boxLabelCfg);
24617                 }
24618
24619                 
24620         }
24621         
24622        
24623         
24624         if(this.inputType != 'radio'){
24625             cfg.cn.push(hidden);
24626         }
24627         
24628         return cfg;
24629         
24630     },
24631     
24632     /**
24633      * return the real input element.
24634      */
24635     inputEl: function ()
24636     {
24637         return this.el.select('input.roo-' + this.inputType,true).first();
24638     },
24639     hiddenEl: function ()
24640     {
24641         return this.el.select('input.roo-hidden-value',true).first();
24642     },
24643     
24644     labelEl: function()
24645     {
24646         return this.el.select('label.control-label',true).first();
24647     },
24648     /* depricated... */
24649     
24650     label: function()
24651     {
24652         return this.labelEl();
24653     },
24654     
24655     boxLabelEl: function()
24656     {
24657         return this.el.select('label.box-label',true).first();
24658     },
24659     
24660     initEvents : function()
24661     {
24662 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24663         
24664         this.inputEl().on('click', this.onClick,  this);
24665         
24666         if (this.boxLabel) { 
24667             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24668         }
24669         
24670         this.startValue = this.getValue();
24671         
24672         if(this.groupId){
24673             Roo.bootstrap.CheckBox.register(this);
24674         }
24675     },
24676     
24677     onClick : function(e)
24678     {   
24679         if(this.fireEvent('click', this, e) !== false){
24680             this.setChecked(!this.checked);
24681         }
24682         
24683     },
24684     
24685     setChecked : function(state,suppressEvent)
24686     {
24687         this.startValue = this.getValue();
24688
24689         if(this.inputType == 'radio'){
24690             
24691             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24692                 e.dom.checked = false;
24693             });
24694             
24695             this.inputEl().dom.checked = true;
24696             
24697             this.inputEl().dom.value = this.inputValue;
24698             
24699             if(suppressEvent !== true){
24700                 this.fireEvent('check', this, true);
24701             }
24702             
24703             this.validate();
24704             
24705             return;
24706         }
24707         
24708         this.checked = state;
24709         
24710         this.inputEl().dom.checked = state;
24711         
24712         
24713         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24714         
24715         if(suppressEvent !== true){
24716             this.fireEvent('check', this, state);
24717         }
24718         
24719         this.validate();
24720     },
24721     
24722     getValue : function()
24723     {
24724         if(this.inputType == 'radio'){
24725             return this.getGroupValue();
24726         }
24727         
24728         return this.hiddenEl().dom.value;
24729         
24730     },
24731     
24732     getGroupValue : function()
24733     {
24734         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24735             return '';
24736         }
24737         
24738         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24739     },
24740     
24741     setValue : function(v,suppressEvent)
24742     {
24743         if(this.inputType == 'radio'){
24744             this.setGroupValue(v, suppressEvent);
24745             return;
24746         }
24747         
24748         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24749         
24750         this.validate();
24751     },
24752     
24753     setGroupValue : function(v, suppressEvent)
24754     {
24755         this.startValue = this.getValue();
24756         
24757         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24758             e.dom.checked = false;
24759             
24760             if(e.dom.value == v){
24761                 e.dom.checked = true;
24762             }
24763         });
24764         
24765         if(suppressEvent !== true){
24766             this.fireEvent('check', this, true);
24767         }
24768
24769         this.validate();
24770         
24771         return;
24772     },
24773     
24774     validate : function()
24775     {
24776         if(this.getVisibilityEl().hasClass('hidden')){
24777             return true;
24778         }
24779         
24780         if(
24781                 this.disabled || 
24782                 (this.inputType == 'radio' && this.validateRadio()) ||
24783                 (this.inputType == 'checkbox' && this.validateCheckbox())
24784         ){
24785             this.markValid();
24786             return true;
24787         }
24788         
24789         this.markInvalid();
24790         return false;
24791     },
24792     
24793     validateRadio : function()
24794     {
24795         if(this.getVisibilityEl().hasClass('hidden')){
24796             return true;
24797         }
24798         
24799         if(this.allowBlank){
24800             return true;
24801         }
24802         
24803         var valid = false;
24804         
24805         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24806             if(!e.dom.checked){
24807                 return;
24808             }
24809             
24810             valid = true;
24811             
24812             return false;
24813         });
24814         
24815         return valid;
24816     },
24817     
24818     validateCheckbox : function()
24819     {
24820         if(!this.groupId){
24821             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24822             //return (this.getValue() == this.inputValue) ? true : false;
24823         }
24824         
24825         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24826         
24827         if(!group){
24828             return false;
24829         }
24830         
24831         var r = false;
24832         
24833         for(var i in group){
24834             if(group[i].el.isVisible(true)){
24835                 r = false;
24836                 break;
24837             }
24838             
24839             r = true;
24840         }
24841         
24842         for(var i in group){
24843             if(r){
24844                 break;
24845             }
24846             
24847             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24848         }
24849         
24850         return r;
24851     },
24852     
24853     /**
24854      * Mark this field as valid
24855      */
24856     markValid : function()
24857     {
24858         var _this = this;
24859         
24860         this.fireEvent('valid', this);
24861         
24862         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24863         
24864         if(this.groupId){
24865             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24866         }
24867         
24868         if(label){
24869             label.markValid();
24870         }
24871
24872         if(this.inputType == 'radio'){
24873             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24874                 var fg = e.findParent('.form-group', false, true);
24875                 if (Roo.bootstrap.version == 3) {
24876                     fg.removeClass([_this.invalidClass, _this.validClass]);
24877                     fg.addClass(_this.validClass);
24878                 } else {
24879                     fg.removeClass(['is-valid', 'is-invalid']);
24880                     fg.addClass('is-valid');
24881                 }
24882             });
24883             
24884             return;
24885         }
24886
24887         if(!this.groupId){
24888             var fg = this.el.findParent('.form-group', false, true);
24889             if (Roo.bootstrap.version == 3) {
24890                 fg.removeClass([this.invalidClass, this.validClass]);
24891                 fg.addClass(this.validClass);
24892             } else {
24893                 fg.removeClass(['is-valid', 'is-invalid']);
24894                 fg.addClass('is-valid');
24895             }
24896             return;
24897         }
24898         
24899         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24900         
24901         if(!group){
24902             return;
24903         }
24904         
24905         for(var i in group){
24906             var fg = group[i].el.findParent('.form-group', false, true);
24907             if (Roo.bootstrap.version == 3) {
24908                 fg.removeClass([this.invalidClass, this.validClass]);
24909                 fg.addClass(this.validClass);
24910             } else {
24911                 fg.removeClass(['is-valid', 'is-invalid']);
24912                 fg.addClass('is-valid');
24913             }
24914         }
24915     },
24916     
24917      /**
24918      * Mark this field as invalid
24919      * @param {String} msg The validation message
24920      */
24921     markInvalid : function(msg)
24922     {
24923         if(this.allowBlank){
24924             return;
24925         }
24926         
24927         var _this = this;
24928         
24929         this.fireEvent('invalid', this, msg);
24930         
24931         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24932         
24933         if(this.groupId){
24934             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24935         }
24936         
24937         if(label){
24938             label.markInvalid();
24939         }
24940             
24941         if(this.inputType == 'radio'){
24942             
24943             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24944                 var fg = e.findParent('.form-group', false, true);
24945                 if (Roo.bootstrap.version == 3) {
24946                     fg.removeClass([_this.invalidClass, _this.validClass]);
24947                     fg.addClass(_this.invalidClass);
24948                 } else {
24949                     fg.removeClass(['is-invalid', 'is-valid']);
24950                     fg.addClass('is-invalid');
24951                 }
24952             });
24953             
24954             return;
24955         }
24956         
24957         if(!this.groupId){
24958             var fg = this.el.findParent('.form-group', false, true);
24959             if (Roo.bootstrap.version == 3) {
24960                 fg.removeClass([_this.invalidClass, _this.validClass]);
24961                 fg.addClass(_this.invalidClass);
24962             } else {
24963                 fg.removeClass(['is-invalid', 'is-valid']);
24964                 fg.addClass('is-invalid');
24965             }
24966             return;
24967         }
24968         
24969         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24970         
24971         if(!group){
24972             return;
24973         }
24974         
24975         for(var i in group){
24976             var fg = group[i].el.findParent('.form-group', false, true);
24977             if (Roo.bootstrap.version == 3) {
24978                 fg.removeClass([_this.invalidClass, _this.validClass]);
24979                 fg.addClass(_this.invalidClass);
24980             } else {
24981                 fg.removeClass(['is-invalid', 'is-valid']);
24982                 fg.addClass('is-invalid');
24983             }
24984         }
24985         
24986     },
24987     
24988     clearInvalid : function()
24989     {
24990         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24991         
24992         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24993         
24994         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24995         
24996         if (label && label.iconEl) {
24997             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24998             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24999         }
25000     },
25001     
25002     disable : function()
25003     {
25004         if(this.inputType != 'radio'){
25005             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25006             return;
25007         }
25008         
25009         var _this = this;
25010         
25011         if(this.rendered){
25012             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25013                 _this.getActionEl().addClass(this.disabledClass);
25014                 e.dom.disabled = true;
25015             });
25016         }
25017         
25018         this.disabled = true;
25019         this.fireEvent("disable", this);
25020         return this;
25021     },
25022
25023     enable : function()
25024     {
25025         if(this.inputType != 'radio'){
25026             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25027             return;
25028         }
25029         
25030         var _this = this;
25031         
25032         if(this.rendered){
25033             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25034                 _this.getActionEl().removeClass(this.disabledClass);
25035                 e.dom.disabled = false;
25036             });
25037         }
25038         
25039         this.disabled = false;
25040         this.fireEvent("enable", this);
25041         return this;
25042     },
25043     
25044     setBoxLabel : function(v)
25045     {
25046         this.boxLabel = v;
25047         
25048         if(this.rendered){
25049             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25050         }
25051     }
25052
25053 });
25054
25055 Roo.apply(Roo.bootstrap.CheckBox, {
25056     
25057     groups: {},
25058     
25059      /**
25060     * register a CheckBox Group
25061     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25062     */
25063     register : function(checkbox)
25064     {
25065         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25066             this.groups[checkbox.groupId] = {};
25067         }
25068         
25069         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25070             return;
25071         }
25072         
25073         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25074         
25075     },
25076     /**
25077     * fetch a CheckBox Group based on the group ID
25078     * @param {string} the group ID
25079     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25080     */
25081     get: function(groupId) {
25082         if (typeof(this.groups[groupId]) == 'undefined') {
25083             return false;
25084         }
25085         
25086         return this.groups[groupId] ;
25087     }
25088     
25089     
25090 });
25091 /*
25092  * - LGPL
25093  *
25094  * RadioItem
25095  * 
25096  */
25097
25098 /**
25099  * @class Roo.bootstrap.Radio
25100  * @extends Roo.bootstrap.Component
25101  * Bootstrap Radio class
25102  * @cfg {String} boxLabel - the label associated
25103  * @cfg {String} value - the value of radio
25104  * 
25105  * @constructor
25106  * Create a new Radio
25107  * @param {Object} config The config object
25108  */
25109 Roo.bootstrap.Radio = function(config){
25110     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25111     
25112 };
25113
25114 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25115     
25116     boxLabel : '',
25117     
25118     value : '',
25119     
25120     getAutoCreate : function()
25121     {
25122         var cfg = {
25123             tag : 'div',
25124             cls : 'form-group radio',
25125             cn : [
25126                 {
25127                     tag : 'label',
25128                     cls : 'box-label',
25129                     html : this.boxLabel
25130                 }
25131             ]
25132         };
25133         
25134         return cfg;
25135     },
25136     
25137     initEvents : function() 
25138     {
25139         this.parent().register(this);
25140         
25141         this.el.on('click', this.onClick, this);
25142         
25143     },
25144     
25145     onClick : function(e)
25146     {
25147         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25148             this.setChecked(true);
25149         }
25150     },
25151     
25152     setChecked : function(state, suppressEvent)
25153     {
25154         this.parent().setValue(this.value, suppressEvent);
25155         
25156     },
25157     
25158     setBoxLabel : function(v)
25159     {
25160         this.boxLabel = v;
25161         
25162         if(this.rendered){
25163             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25164         }
25165     }
25166     
25167 });
25168  
25169
25170  /*
25171  * - LGPL
25172  *
25173  * Input
25174  * 
25175  */
25176
25177 /**
25178  * @class Roo.bootstrap.SecurePass
25179  * @extends Roo.bootstrap.Input
25180  * Bootstrap SecurePass class
25181  *
25182  * 
25183  * @constructor
25184  * Create a new SecurePass
25185  * @param {Object} config The config object
25186  */
25187  
25188 Roo.bootstrap.SecurePass = function (config) {
25189     // these go here, so the translation tool can replace them..
25190     this.errors = {
25191         PwdEmpty: "Please type a password, and then retype it to confirm.",
25192         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25193         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25194         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25195         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25196         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25197         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25198         TooWeak: "Your password is Too Weak."
25199     },
25200     this.meterLabel = "Password strength:";
25201     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25202     this.meterClass = [
25203         "roo-password-meter-tooweak", 
25204         "roo-password-meter-weak", 
25205         "roo-password-meter-medium", 
25206         "roo-password-meter-strong", 
25207         "roo-password-meter-grey"
25208     ];
25209     
25210     this.errors = {};
25211     
25212     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25213 }
25214
25215 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25216     /**
25217      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25218      * {
25219      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25220      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25221      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25222      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25223      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25224      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25225      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25226      * })
25227      */
25228     // private
25229     
25230     meterWidth: 300,
25231     errorMsg :'',    
25232     errors: false,
25233     imageRoot: '/',
25234     /**
25235      * @cfg {String/Object} Label for the strength meter (defaults to
25236      * 'Password strength:')
25237      */
25238     // private
25239     meterLabel: '',
25240     /**
25241      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25242      * ['Weak', 'Medium', 'Strong'])
25243      */
25244     // private    
25245     pwdStrengths: false,    
25246     // private
25247     strength: 0,
25248     // private
25249     _lastPwd: null,
25250     // private
25251     kCapitalLetter: 0,
25252     kSmallLetter: 1,
25253     kDigit: 2,
25254     kPunctuation: 3,
25255     
25256     insecure: false,
25257     // private
25258     initEvents: function ()
25259     {
25260         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25261
25262         if (this.el.is('input[type=password]') && Roo.isSafari) {
25263             this.el.on('keydown', this.SafariOnKeyDown, this);
25264         }
25265
25266         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25267     },
25268     // private
25269     onRender: function (ct, position)
25270     {
25271         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25272         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25273         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25274
25275         this.trigger.createChild({
25276                    cn: [
25277                     {
25278                     //id: 'PwdMeter',
25279                     tag: 'div',
25280                     cls: 'roo-password-meter-grey col-xs-12',
25281                     style: {
25282                         //width: 0,
25283                         //width: this.meterWidth + 'px'                                                
25284                         }
25285                     },
25286                     {                            
25287                          cls: 'roo-password-meter-text'                          
25288                     }
25289                 ]            
25290         });
25291
25292          
25293         if (this.hideTrigger) {
25294             this.trigger.setDisplayed(false);
25295         }
25296         this.setSize(this.width || '', this.height || '');
25297     },
25298     // private
25299     onDestroy: function ()
25300     {
25301         if (this.trigger) {
25302             this.trigger.removeAllListeners();
25303             this.trigger.remove();
25304         }
25305         if (this.wrap) {
25306             this.wrap.remove();
25307         }
25308         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25309     },
25310     // private
25311     checkStrength: function ()
25312     {
25313         var pwd = this.inputEl().getValue();
25314         if (pwd == this._lastPwd) {
25315             return;
25316         }
25317
25318         var strength;
25319         if (this.ClientSideStrongPassword(pwd)) {
25320             strength = 3;
25321         } else if (this.ClientSideMediumPassword(pwd)) {
25322             strength = 2;
25323         } else if (this.ClientSideWeakPassword(pwd)) {
25324             strength = 1;
25325         } else {
25326             strength = 0;
25327         }
25328         
25329         Roo.log('strength1: ' + strength);
25330         
25331         //var pm = this.trigger.child('div/div/div').dom;
25332         var pm = this.trigger.child('div/div');
25333         pm.removeClass(this.meterClass);
25334         pm.addClass(this.meterClass[strength]);
25335                 
25336         
25337         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25338                 
25339         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25340         
25341         this._lastPwd = pwd;
25342     },
25343     reset: function ()
25344     {
25345         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25346         
25347         this._lastPwd = '';
25348         
25349         var pm = this.trigger.child('div/div');
25350         pm.removeClass(this.meterClass);
25351         pm.addClass('roo-password-meter-grey');        
25352         
25353         
25354         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25355         
25356         pt.innerHTML = '';
25357         this.inputEl().dom.type='password';
25358     },
25359     // private
25360     validateValue: function (value)
25361     {
25362         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25363             return false;
25364         }
25365         if (value.length == 0) {
25366             if (this.allowBlank) {
25367                 this.clearInvalid();
25368                 return true;
25369             }
25370
25371             this.markInvalid(this.errors.PwdEmpty);
25372             this.errorMsg = this.errors.PwdEmpty;
25373             return false;
25374         }
25375         
25376         if(this.insecure){
25377             return true;
25378         }
25379         
25380         if (!value.match(/[\x21-\x7e]+/)) {
25381             this.markInvalid(this.errors.PwdBadChar);
25382             this.errorMsg = this.errors.PwdBadChar;
25383             return false;
25384         }
25385         if (value.length < 6) {
25386             this.markInvalid(this.errors.PwdShort);
25387             this.errorMsg = this.errors.PwdShort;
25388             return false;
25389         }
25390         if (value.length > 16) {
25391             this.markInvalid(this.errors.PwdLong);
25392             this.errorMsg = this.errors.PwdLong;
25393             return false;
25394         }
25395         var strength;
25396         if (this.ClientSideStrongPassword(value)) {
25397             strength = 3;
25398         } else if (this.ClientSideMediumPassword(value)) {
25399             strength = 2;
25400         } else if (this.ClientSideWeakPassword(value)) {
25401             strength = 1;
25402         } else {
25403             strength = 0;
25404         }
25405
25406         
25407         if (strength < 2) {
25408             //this.markInvalid(this.errors.TooWeak);
25409             this.errorMsg = this.errors.TooWeak;
25410             //return false;
25411         }
25412         
25413         
25414         console.log('strength2: ' + strength);
25415         
25416         //var pm = this.trigger.child('div/div/div').dom;
25417         
25418         var pm = this.trigger.child('div/div');
25419         pm.removeClass(this.meterClass);
25420         pm.addClass(this.meterClass[strength]);
25421                 
25422         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25423                 
25424         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25425         
25426         this.errorMsg = ''; 
25427         return true;
25428     },
25429     // private
25430     CharacterSetChecks: function (type)
25431     {
25432         this.type = type;
25433         this.fResult = false;
25434     },
25435     // private
25436     isctype: function (character, type)
25437     {
25438         switch (type) {  
25439             case this.kCapitalLetter:
25440                 if (character >= 'A' && character <= 'Z') {
25441                     return true;
25442                 }
25443                 break;
25444             
25445             case this.kSmallLetter:
25446                 if (character >= 'a' && character <= 'z') {
25447                     return true;
25448                 }
25449                 break;
25450             
25451             case this.kDigit:
25452                 if (character >= '0' && character <= '9') {
25453                     return true;
25454                 }
25455                 break;
25456             
25457             case this.kPunctuation:
25458                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25459                     return true;
25460                 }
25461                 break;
25462             
25463             default:
25464                 return false;
25465         }
25466
25467     },
25468     // private
25469     IsLongEnough: function (pwd, size)
25470     {
25471         return !(pwd == null || isNaN(size) || pwd.length < size);
25472     },
25473     // private
25474     SpansEnoughCharacterSets: function (word, nb)
25475     {
25476         if (!this.IsLongEnough(word, nb))
25477         {
25478             return false;
25479         }
25480
25481         var characterSetChecks = new Array(
25482             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25483             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25484         );
25485         
25486         for (var index = 0; index < word.length; ++index) {
25487             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25488                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25489                     characterSetChecks[nCharSet].fResult = true;
25490                     break;
25491                 }
25492             }
25493         }
25494
25495         var nCharSets = 0;
25496         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25497             if (characterSetChecks[nCharSet].fResult) {
25498                 ++nCharSets;
25499             }
25500         }
25501
25502         if (nCharSets < nb) {
25503             return false;
25504         }
25505         return true;
25506     },
25507     // private
25508     ClientSideStrongPassword: function (pwd)
25509     {
25510         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25511     },
25512     // private
25513     ClientSideMediumPassword: function (pwd)
25514     {
25515         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25516     },
25517     // private
25518     ClientSideWeakPassword: function (pwd)
25519     {
25520         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25521     }
25522           
25523 })//<script type="text/javascript">
25524
25525 /*
25526  * Based  Ext JS Library 1.1.1
25527  * Copyright(c) 2006-2007, Ext JS, LLC.
25528  * LGPL
25529  *
25530  */
25531  
25532 /**
25533  * @class Roo.HtmlEditorCore
25534  * @extends Roo.Component
25535  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25536  *
25537  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25538  */
25539
25540 Roo.HtmlEditorCore = function(config){
25541     
25542     
25543     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25544     
25545     
25546     this.addEvents({
25547         /**
25548          * @event initialize
25549          * Fires when the editor is fully initialized (including the iframe)
25550          * @param {Roo.HtmlEditorCore} this
25551          */
25552         initialize: true,
25553         /**
25554          * @event activate
25555          * Fires when the editor is first receives the focus. Any insertion must wait
25556          * until after this event.
25557          * @param {Roo.HtmlEditorCore} this
25558          */
25559         activate: true,
25560          /**
25561          * @event beforesync
25562          * Fires before the textarea is updated with content from the editor iframe. Return false
25563          * to cancel the sync.
25564          * @param {Roo.HtmlEditorCore} this
25565          * @param {String} html
25566          */
25567         beforesync: true,
25568          /**
25569          * @event beforepush
25570          * Fires before the iframe editor is updated with content from the textarea. Return false
25571          * to cancel the push.
25572          * @param {Roo.HtmlEditorCore} this
25573          * @param {String} html
25574          */
25575         beforepush: true,
25576          /**
25577          * @event sync
25578          * Fires when the textarea is updated with content from the editor iframe.
25579          * @param {Roo.HtmlEditorCore} this
25580          * @param {String} html
25581          */
25582         sync: true,
25583          /**
25584          * @event push
25585          * Fires when the iframe editor is updated with content from the textarea.
25586          * @param {Roo.HtmlEditorCore} this
25587          * @param {String} html
25588          */
25589         push: true,
25590         
25591         /**
25592          * @event editorevent
25593          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25594          * @param {Roo.HtmlEditorCore} this
25595          */
25596         editorevent: true
25597         
25598     });
25599     
25600     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25601     
25602     // defaults : white / black...
25603     this.applyBlacklists();
25604     
25605     
25606     
25607 };
25608
25609
25610 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25611
25612
25613      /**
25614      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25615      */
25616     
25617     owner : false,
25618     
25619      /**
25620      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25621      *                        Roo.resizable.
25622      */
25623     resizable : false,
25624      /**
25625      * @cfg {Number} height (in pixels)
25626      */   
25627     height: 300,
25628    /**
25629      * @cfg {Number} width (in pixels)
25630      */   
25631     width: 500,
25632     
25633     /**
25634      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25635      * 
25636      */
25637     stylesheets: false,
25638     
25639     /**
25640      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25641      */
25642     allowComments: false,
25643     // id of frame..
25644     frameId: false,
25645     
25646     // private properties
25647     validationEvent : false,
25648     deferHeight: true,
25649     initialized : false,
25650     activated : false,
25651     sourceEditMode : false,
25652     onFocus : Roo.emptyFn,
25653     iframePad:3,
25654     hideMode:'offsets',
25655     
25656     clearUp: true,
25657     
25658     // blacklist + whitelisted elements..
25659     black: false,
25660     white: false,
25661      
25662     bodyCls : '',
25663
25664     /**
25665      * Protected method that will not generally be called directly. It
25666      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25667      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25668      */
25669     getDocMarkup : function(){
25670         // body styles..
25671         var st = '';
25672         
25673         // inherit styels from page...?? 
25674         if (this.stylesheets === false) {
25675             
25676             Roo.get(document.head).select('style').each(function(node) {
25677                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25678             });
25679             
25680             Roo.get(document.head).select('link').each(function(node) { 
25681                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25682             });
25683             
25684         } else if (!this.stylesheets.length) {
25685                 // simple..
25686                 st = '<style type="text/css">' +
25687                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25688                    '</style>';
25689         } else {
25690             for (var i in this.stylesheets) { 
25691                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25692             }
25693             
25694         }
25695         
25696         st +=  '<style type="text/css">' +
25697             'IMG { cursor: pointer } ' +
25698         '</style>';
25699
25700         var cls = 'roo-htmleditor-body';
25701         
25702         if(this.bodyCls.length){
25703             cls += ' ' + this.bodyCls;
25704         }
25705         
25706         return '<html><head>' + st  +
25707             //<style type="text/css">' +
25708             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25709             //'</style>' +
25710             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25711     },
25712
25713     // private
25714     onRender : function(ct, position)
25715     {
25716         var _t = this;
25717         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25718         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25719         
25720         
25721         this.el.dom.style.border = '0 none';
25722         this.el.dom.setAttribute('tabIndex', -1);
25723         this.el.addClass('x-hidden hide');
25724         
25725         
25726         
25727         if(Roo.isIE){ // fix IE 1px bogus margin
25728             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25729         }
25730        
25731         
25732         this.frameId = Roo.id();
25733         
25734          
25735         
25736         var iframe = this.owner.wrap.createChild({
25737             tag: 'iframe',
25738             cls: 'form-control', // bootstrap..
25739             id: this.frameId,
25740             name: this.frameId,
25741             frameBorder : 'no',
25742             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25743         }, this.el
25744         );
25745         
25746         
25747         this.iframe = iframe.dom;
25748
25749          this.assignDocWin();
25750         
25751         this.doc.designMode = 'on';
25752        
25753         this.doc.open();
25754         this.doc.write(this.getDocMarkup());
25755         this.doc.close();
25756
25757         
25758         var task = { // must defer to wait for browser to be ready
25759             run : function(){
25760                 //console.log("run task?" + this.doc.readyState);
25761                 this.assignDocWin();
25762                 if(this.doc.body || this.doc.readyState == 'complete'){
25763                     try {
25764                         this.doc.designMode="on";
25765                     } catch (e) {
25766                         return;
25767                     }
25768                     Roo.TaskMgr.stop(task);
25769                     this.initEditor.defer(10, this);
25770                 }
25771             },
25772             interval : 10,
25773             duration: 10000,
25774             scope: this
25775         };
25776         Roo.TaskMgr.start(task);
25777
25778     },
25779
25780     // private
25781     onResize : function(w, h)
25782     {
25783          Roo.log('resize: ' +w + ',' + h );
25784         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25785         if(!this.iframe){
25786             return;
25787         }
25788         if(typeof w == 'number'){
25789             
25790             this.iframe.style.width = w + 'px';
25791         }
25792         if(typeof h == 'number'){
25793             
25794             this.iframe.style.height = h + 'px';
25795             if(this.doc){
25796                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25797             }
25798         }
25799         
25800     },
25801
25802     /**
25803      * Toggles the editor between standard and source edit mode.
25804      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25805      */
25806     toggleSourceEdit : function(sourceEditMode){
25807         
25808         this.sourceEditMode = sourceEditMode === true;
25809         
25810         if(this.sourceEditMode){
25811  
25812             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25813             
25814         }else{
25815             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25816             //this.iframe.className = '';
25817             this.deferFocus();
25818         }
25819         //this.setSize(this.owner.wrap.getSize());
25820         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25821     },
25822
25823     
25824   
25825
25826     /**
25827      * Protected method that will not generally be called directly. If you need/want
25828      * custom HTML cleanup, this is the method you should override.
25829      * @param {String} html The HTML to be cleaned
25830      * return {String} The cleaned HTML
25831      */
25832     cleanHtml : function(html){
25833         html = String(html);
25834         if(html.length > 5){
25835             if(Roo.isSafari){ // strip safari nonsense
25836                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25837             }
25838         }
25839         if(html == '&nbsp;'){
25840             html = '';
25841         }
25842         return html;
25843     },
25844
25845     /**
25846      * HTML Editor -> Textarea
25847      * Protected method that will not generally be called directly. Syncs the contents
25848      * of the editor iframe with the textarea.
25849      */
25850     syncValue : function(){
25851         if(this.initialized){
25852             var bd = (this.doc.body || this.doc.documentElement);
25853             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25854             var html = bd.innerHTML;
25855             if(Roo.isSafari){
25856                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25857                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25858                 if(m && m[1]){
25859                     html = '<div style="'+m[0]+'">' + html + '</div>';
25860                 }
25861             }
25862             html = this.cleanHtml(html);
25863             // fix up the special chars.. normaly like back quotes in word...
25864             // however we do not want to do this with chinese..
25865             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25866                 
25867                 var cc = match.charCodeAt();
25868
25869                 // Get the character value, handling surrogate pairs
25870                 if (match.length == 2) {
25871                     // It's a surrogate pair, calculate the Unicode code point
25872                     var high = match.charCodeAt(0) - 0xD800;
25873                     var low  = match.charCodeAt(1) - 0xDC00;
25874                     cc = (high * 0x400) + low + 0x10000;
25875                 }  else if (
25876                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25877                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25878                     (cc >= 0xf900 && cc < 0xfb00 )
25879                 ) {
25880                         return match;
25881                 }  
25882          
25883                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25884                 return "&#" + cc + ";";
25885                 
25886                 
25887             });
25888             
25889             
25890              
25891             if(this.owner.fireEvent('beforesync', this, html) !== false){
25892                 this.el.dom.value = html;
25893                 this.owner.fireEvent('sync', this, html);
25894             }
25895         }
25896     },
25897
25898     /**
25899      * Protected method that will not generally be called directly. Pushes the value of the textarea
25900      * into the iframe editor.
25901      */
25902     pushValue : function(){
25903         if(this.initialized){
25904             var v = this.el.dom.value.trim();
25905             
25906 //            if(v.length < 1){
25907 //                v = '&#160;';
25908 //            }
25909             
25910             if(this.owner.fireEvent('beforepush', this, v) !== false){
25911                 var d = (this.doc.body || this.doc.documentElement);
25912                 d.innerHTML = v;
25913                 this.cleanUpPaste();
25914                 this.el.dom.value = d.innerHTML;
25915                 this.owner.fireEvent('push', this, v);
25916             }
25917         }
25918     },
25919
25920     // private
25921     deferFocus : function(){
25922         this.focus.defer(10, this);
25923     },
25924
25925     // doc'ed in Field
25926     focus : function(){
25927         if(this.win && !this.sourceEditMode){
25928             this.win.focus();
25929         }else{
25930             this.el.focus();
25931         }
25932     },
25933     
25934     assignDocWin: function()
25935     {
25936         var iframe = this.iframe;
25937         
25938          if(Roo.isIE){
25939             this.doc = iframe.contentWindow.document;
25940             this.win = iframe.contentWindow;
25941         } else {
25942 //            if (!Roo.get(this.frameId)) {
25943 //                return;
25944 //            }
25945 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25946 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25947             
25948             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25949                 return;
25950             }
25951             
25952             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25953             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25954         }
25955     },
25956     
25957     // private
25958     initEditor : function(){
25959         //console.log("INIT EDITOR");
25960         this.assignDocWin();
25961         
25962         
25963         
25964         this.doc.designMode="on";
25965         this.doc.open();
25966         this.doc.write(this.getDocMarkup());
25967         this.doc.close();
25968         
25969         var dbody = (this.doc.body || this.doc.documentElement);
25970         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25971         // this copies styles from the containing element into thsi one..
25972         // not sure why we need all of this..
25973         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25974         
25975         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25976         //ss['background-attachment'] = 'fixed'; // w3c
25977         dbody.bgProperties = 'fixed'; // ie
25978         //Roo.DomHelper.applyStyles(dbody, ss);
25979         Roo.EventManager.on(this.doc, {
25980             //'mousedown': this.onEditorEvent,
25981             'mouseup': this.onEditorEvent,
25982             'dblclick': this.onEditorEvent,
25983             'click': this.onEditorEvent,
25984             'keyup': this.onEditorEvent,
25985             buffer:100,
25986             scope: this
25987         });
25988         if(Roo.isGecko){
25989             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25990         }
25991         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25992             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25993         }
25994         this.initialized = true;
25995
25996         this.owner.fireEvent('initialize', this);
25997         this.pushValue();
25998     },
25999
26000     // private
26001     onDestroy : function(){
26002         
26003         
26004         
26005         if(this.rendered){
26006             
26007             //for (var i =0; i < this.toolbars.length;i++) {
26008             //    // fixme - ask toolbars for heights?
26009             //    this.toolbars[i].onDestroy();
26010            // }
26011             
26012             //this.wrap.dom.innerHTML = '';
26013             //this.wrap.remove();
26014         }
26015     },
26016
26017     // private
26018     onFirstFocus : function(){
26019         
26020         this.assignDocWin();
26021         
26022         
26023         this.activated = true;
26024          
26025     
26026         if(Roo.isGecko){ // prevent silly gecko errors
26027             this.win.focus();
26028             var s = this.win.getSelection();
26029             if(!s.focusNode || s.focusNode.nodeType != 3){
26030                 var r = s.getRangeAt(0);
26031                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26032                 r.collapse(true);
26033                 this.deferFocus();
26034             }
26035             try{
26036                 this.execCmd('useCSS', true);
26037                 this.execCmd('styleWithCSS', false);
26038             }catch(e){}
26039         }
26040         this.owner.fireEvent('activate', this);
26041     },
26042
26043     // private
26044     adjustFont: function(btn){
26045         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26046         //if(Roo.isSafari){ // safari
26047         //    adjust *= 2;
26048        // }
26049         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26050         if(Roo.isSafari){ // safari
26051             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26052             v =  (v < 10) ? 10 : v;
26053             v =  (v > 48) ? 48 : v;
26054             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26055             
26056         }
26057         
26058         
26059         v = Math.max(1, v+adjust);
26060         
26061         this.execCmd('FontSize', v  );
26062     },
26063
26064     onEditorEvent : function(e)
26065     {
26066         this.owner.fireEvent('editorevent', this, e);
26067       //  this.updateToolbar();
26068         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26069     },
26070
26071     insertTag : function(tg)
26072     {
26073         // could be a bit smarter... -> wrap the current selected tRoo..
26074         if (tg.toLowerCase() == 'span' ||
26075             tg.toLowerCase() == 'code' ||
26076             tg.toLowerCase() == 'sup' ||
26077             tg.toLowerCase() == 'sub' 
26078             ) {
26079             
26080             range = this.createRange(this.getSelection());
26081             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26082             wrappingNode.appendChild(range.extractContents());
26083             range.insertNode(wrappingNode);
26084
26085             return;
26086             
26087             
26088             
26089         }
26090         this.execCmd("formatblock",   tg);
26091         
26092     },
26093     
26094     insertText : function(txt)
26095     {
26096         
26097         
26098         var range = this.createRange();
26099         range.deleteContents();
26100                //alert(Sender.getAttribute('label'));
26101                
26102         range.insertNode(this.doc.createTextNode(txt));
26103     } ,
26104     
26105      
26106
26107     /**
26108      * Executes a Midas editor command on the editor document and performs necessary focus and
26109      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26110      * @param {String} cmd The Midas command
26111      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26112      */
26113     relayCmd : function(cmd, value){
26114         this.win.focus();
26115         this.execCmd(cmd, value);
26116         this.owner.fireEvent('editorevent', this);
26117         //this.updateToolbar();
26118         this.owner.deferFocus();
26119     },
26120
26121     /**
26122      * Executes a Midas editor command directly on the editor document.
26123      * For visual commands, you should use {@link #relayCmd} instead.
26124      * <b>This should only be called after the editor is initialized.</b>
26125      * @param {String} cmd The Midas command
26126      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26127      */
26128     execCmd : function(cmd, value){
26129         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26130         this.syncValue();
26131     },
26132  
26133  
26134    
26135     /**
26136      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26137      * to insert tRoo.
26138      * @param {String} text | dom node.. 
26139      */
26140     insertAtCursor : function(text)
26141     {
26142         
26143         if(!this.activated){
26144             return;
26145         }
26146         /*
26147         if(Roo.isIE){
26148             this.win.focus();
26149             var r = this.doc.selection.createRange();
26150             if(r){
26151                 r.collapse(true);
26152                 r.pasteHTML(text);
26153                 this.syncValue();
26154                 this.deferFocus();
26155             
26156             }
26157             return;
26158         }
26159         */
26160         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26161             this.win.focus();
26162             
26163             
26164             // from jquery ui (MIT licenced)
26165             var range, node;
26166             var win = this.win;
26167             
26168             if (win.getSelection && win.getSelection().getRangeAt) {
26169                 range = win.getSelection().getRangeAt(0);
26170                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26171                 range.insertNode(node);
26172             } else if (win.document.selection && win.document.selection.createRange) {
26173                 // no firefox support
26174                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26175                 win.document.selection.createRange().pasteHTML(txt);
26176             } else {
26177                 // no firefox support
26178                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26179                 this.execCmd('InsertHTML', txt);
26180             } 
26181             
26182             this.syncValue();
26183             
26184             this.deferFocus();
26185         }
26186     },
26187  // private
26188     mozKeyPress : function(e){
26189         if(e.ctrlKey){
26190             var c = e.getCharCode(), cmd;
26191           
26192             if(c > 0){
26193                 c = String.fromCharCode(c).toLowerCase();
26194                 switch(c){
26195                     case 'b':
26196                         cmd = 'bold';
26197                         break;
26198                     case 'i':
26199                         cmd = 'italic';
26200                         break;
26201                     
26202                     case 'u':
26203                         cmd = 'underline';
26204                         break;
26205                     
26206                     case 'v':
26207                         this.cleanUpPaste.defer(100, this);
26208                         return;
26209                         
26210                 }
26211                 if(cmd){
26212                     this.win.focus();
26213                     this.execCmd(cmd);
26214                     this.deferFocus();
26215                     e.preventDefault();
26216                 }
26217                 
26218             }
26219         }
26220     },
26221
26222     // private
26223     fixKeys : function(){ // load time branching for fastest keydown performance
26224         if(Roo.isIE){
26225             return function(e){
26226                 var k = e.getKey(), r;
26227                 if(k == e.TAB){
26228                     e.stopEvent();
26229                     r = this.doc.selection.createRange();
26230                     if(r){
26231                         r.collapse(true);
26232                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26233                         this.deferFocus();
26234                     }
26235                     return;
26236                 }
26237                 
26238                 if(k == e.ENTER){
26239                     r = this.doc.selection.createRange();
26240                     if(r){
26241                         var target = r.parentElement();
26242                         if(!target || target.tagName.toLowerCase() != 'li'){
26243                             e.stopEvent();
26244                             r.pasteHTML('<br />');
26245                             r.collapse(false);
26246                             r.select();
26247                         }
26248                     }
26249                 }
26250                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26251                     this.cleanUpPaste.defer(100, this);
26252                     return;
26253                 }
26254                 
26255                 
26256             };
26257         }else if(Roo.isOpera){
26258             return function(e){
26259                 var k = e.getKey();
26260                 if(k == e.TAB){
26261                     e.stopEvent();
26262                     this.win.focus();
26263                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26264                     this.deferFocus();
26265                 }
26266                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26267                     this.cleanUpPaste.defer(100, this);
26268                     return;
26269                 }
26270                 
26271             };
26272         }else if(Roo.isSafari){
26273             return function(e){
26274                 var k = e.getKey();
26275                 
26276                 if(k == e.TAB){
26277                     e.stopEvent();
26278                     this.execCmd('InsertText','\t');
26279                     this.deferFocus();
26280                     return;
26281                 }
26282                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26283                     this.cleanUpPaste.defer(100, this);
26284                     return;
26285                 }
26286                 
26287              };
26288         }
26289     }(),
26290     
26291     getAllAncestors: function()
26292     {
26293         var p = this.getSelectedNode();
26294         var a = [];
26295         if (!p) {
26296             a.push(p); // push blank onto stack..
26297             p = this.getParentElement();
26298         }
26299         
26300         
26301         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26302             a.push(p);
26303             p = p.parentNode;
26304         }
26305         a.push(this.doc.body);
26306         return a;
26307     },
26308     lastSel : false,
26309     lastSelNode : false,
26310     
26311     
26312     getSelection : function() 
26313     {
26314         this.assignDocWin();
26315         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26316     },
26317     
26318     getSelectedNode: function() 
26319     {
26320         // this may only work on Gecko!!!
26321         
26322         // should we cache this!!!!
26323         
26324         
26325         
26326          
26327         var range = this.createRange(this.getSelection()).cloneRange();
26328         
26329         if (Roo.isIE) {
26330             var parent = range.parentElement();
26331             while (true) {
26332                 var testRange = range.duplicate();
26333                 testRange.moveToElementText(parent);
26334                 if (testRange.inRange(range)) {
26335                     break;
26336                 }
26337                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26338                     break;
26339                 }
26340                 parent = parent.parentElement;
26341             }
26342             return parent;
26343         }
26344         
26345         // is ancestor a text element.
26346         var ac =  range.commonAncestorContainer;
26347         if (ac.nodeType == 3) {
26348             ac = ac.parentNode;
26349         }
26350         
26351         var ar = ac.childNodes;
26352          
26353         var nodes = [];
26354         var other_nodes = [];
26355         var has_other_nodes = false;
26356         for (var i=0;i<ar.length;i++) {
26357             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26358                 continue;
26359             }
26360             // fullly contained node.
26361             
26362             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26363                 nodes.push(ar[i]);
26364                 continue;
26365             }
26366             
26367             // probably selected..
26368             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26369                 other_nodes.push(ar[i]);
26370                 continue;
26371             }
26372             // outer..
26373             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26374                 continue;
26375             }
26376             
26377             
26378             has_other_nodes = true;
26379         }
26380         if (!nodes.length && other_nodes.length) {
26381             nodes= other_nodes;
26382         }
26383         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26384             return false;
26385         }
26386         
26387         return nodes[0];
26388     },
26389     createRange: function(sel)
26390     {
26391         // this has strange effects when using with 
26392         // top toolbar - not sure if it's a great idea.
26393         //this.editor.contentWindow.focus();
26394         if (typeof sel != "undefined") {
26395             try {
26396                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26397             } catch(e) {
26398                 return this.doc.createRange();
26399             }
26400         } else {
26401             return this.doc.createRange();
26402         }
26403     },
26404     getParentElement: function()
26405     {
26406         
26407         this.assignDocWin();
26408         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26409         
26410         var range = this.createRange(sel);
26411          
26412         try {
26413             var p = range.commonAncestorContainer;
26414             while (p.nodeType == 3) { // text node
26415                 p = p.parentNode;
26416             }
26417             return p;
26418         } catch (e) {
26419             return null;
26420         }
26421     
26422     },
26423     /***
26424      *
26425      * Range intersection.. the hard stuff...
26426      *  '-1' = before
26427      *  '0' = hits..
26428      *  '1' = after.
26429      *         [ -- selected range --- ]
26430      *   [fail]                        [fail]
26431      *
26432      *    basically..
26433      *      if end is before start or  hits it. fail.
26434      *      if start is after end or hits it fail.
26435      *
26436      *   if either hits (but other is outside. - then it's not 
26437      *   
26438      *    
26439      **/
26440     
26441     
26442     // @see http://www.thismuchiknow.co.uk/?p=64.
26443     rangeIntersectsNode : function(range, node)
26444     {
26445         var nodeRange = node.ownerDocument.createRange();
26446         try {
26447             nodeRange.selectNode(node);
26448         } catch (e) {
26449             nodeRange.selectNodeContents(node);
26450         }
26451     
26452         var rangeStartRange = range.cloneRange();
26453         rangeStartRange.collapse(true);
26454     
26455         var rangeEndRange = range.cloneRange();
26456         rangeEndRange.collapse(false);
26457     
26458         var nodeStartRange = nodeRange.cloneRange();
26459         nodeStartRange.collapse(true);
26460     
26461         var nodeEndRange = nodeRange.cloneRange();
26462         nodeEndRange.collapse(false);
26463     
26464         return rangeStartRange.compareBoundaryPoints(
26465                  Range.START_TO_START, nodeEndRange) == -1 &&
26466                rangeEndRange.compareBoundaryPoints(
26467                  Range.START_TO_START, nodeStartRange) == 1;
26468         
26469          
26470     },
26471     rangeCompareNode : function(range, node)
26472     {
26473         var nodeRange = node.ownerDocument.createRange();
26474         try {
26475             nodeRange.selectNode(node);
26476         } catch (e) {
26477             nodeRange.selectNodeContents(node);
26478         }
26479         
26480         
26481         range.collapse(true);
26482     
26483         nodeRange.collapse(true);
26484      
26485         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26486         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26487          
26488         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26489         
26490         var nodeIsBefore   =  ss == 1;
26491         var nodeIsAfter    = ee == -1;
26492         
26493         if (nodeIsBefore && nodeIsAfter) {
26494             return 0; // outer
26495         }
26496         if (!nodeIsBefore && nodeIsAfter) {
26497             return 1; //right trailed.
26498         }
26499         
26500         if (nodeIsBefore && !nodeIsAfter) {
26501             return 2;  // left trailed.
26502         }
26503         // fully contined.
26504         return 3;
26505     },
26506
26507     // private? - in a new class?
26508     cleanUpPaste :  function()
26509     {
26510         // cleans up the whole document..
26511         Roo.log('cleanuppaste');
26512         
26513         this.cleanUpChildren(this.doc.body);
26514         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26515         if (clean != this.doc.body.innerHTML) {
26516             this.doc.body.innerHTML = clean;
26517         }
26518         
26519     },
26520     
26521     cleanWordChars : function(input) {// change the chars to hex code
26522         var he = Roo.HtmlEditorCore;
26523         
26524         var output = input;
26525         Roo.each(he.swapCodes, function(sw) { 
26526             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26527             
26528             output = output.replace(swapper, sw[1]);
26529         });
26530         
26531         return output;
26532     },
26533     
26534     
26535     cleanUpChildren : function (n)
26536     {
26537         if (!n.childNodes.length) {
26538             return;
26539         }
26540         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26541            this.cleanUpChild(n.childNodes[i]);
26542         }
26543     },
26544     
26545     
26546         
26547     
26548     cleanUpChild : function (node)
26549     {
26550         var ed = this;
26551         //console.log(node);
26552         if (node.nodeName == "#text") {
26553             // clean up silly Windows -- stuff?
26554             return; 
26555         }
26556         if (node.nodeName == "#comment") {
26557             if (!this.allowComments) {
26558                 node.parentNode.removeChild(node);
26559             }
26560             // clean up silly Windows -- stuff?
26561             return; 
26562         }
26563         var lcname = node.tagName.toLowerCase();
26564         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26565         // whitelist of tags..
26566         
26567         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26568             // remove node.
26569             node.parentNode.removeChild(node);
26570             return;
26571             
26572         }
26573         
26574         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26575         
26576         // spans with no attributes - just remove them..
26577         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26578             remove_keep_children = true;
26579         }
26580         
26581         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26582         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26583         
26584         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26585         //    remove_keep_children = true;
26586         //}
26587         
26588         if (remove_keep_children) {
26589             this.cleanUpChildren(node);
26590             // inserts everything just before this node...
26591             while (node.childNodes.length) {
26592                 var cn = node.childNodes[0];
26593                 node.removeChild(cn);
26594                 node.parentNode.insertBefore(cn, node);
26595             }
26596             node.parentNode.removeChild(node);
26597             return;
26598         }
26599         
26600         if (!node.attributes || !node.attributes.length) {
26601             
26602           
26603             
26604             
26605             this.cleanUpChildren(node);
26606             return;
26607         }
26608         
26609         function cleanAttr(n,v)
26610         {
26611             
26612             if (v.match(/^\./) || v.match(/^\//)) {
26613                 return;
26614             }
26615             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26616                 return;
26617             }
26618             if (v.match(/^#/)) {
26619                 return;
26620             }
26621             if (v.match(/^\{/)) { // allow template editing.
26622                 return;
26623             }
26624 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26625             node.removeAttribute(n);
26626             
26627         }
26628         
26629         var cwhite = this.cwhite;
26630         var cblack = this.cblack;
26631             
26632         function cleanStyle(n,v)
26633         {
26634             if (v.match(/expression/)) { //XSS?? should we even bother..
26635                 node.removeAttribute(n);
26636                 return;
26637             }
26638             
26639             var parts = v.split(/;/);
26640             var clean = [];
26641             
26642             Roo.each(parts, function(p) {
26643                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26644                 if (!p.length) {
26645                     return true;
26646                 }
26647                 var l = p.split(':').shift().replace(/\s+/g,'');
26648                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26649                 
26650                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26651 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26652                     //node.removeAttribute(n);
26653                     return true;
26654                 }
26655                 //Roo.log()
26656                 // only allow 'c whitelisted system attributes'
26657                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26658 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26659                     //node.removeAttribute(n);
26660                     return true;
26661                 }
26662                 
26663                 
26664                  
26665                 
26666                 clean.push(p);
26667                 return true;
26668             });
26669             if (clean.length) { 
26670                 node.setAttribute(n, clean.join(';'));
26671             } else {
26672                 node.removeAttribute(n);
26673             }
26674             
26675         }
26676         
26677         
26678         for (var i = node.attributes.length-1; i > -1 ; i--) {
26679             var a = node.attributes[i];
26680             //console.log(a);
26681             
26682             if (a.name.toLowerCase().substr(0,2)=='on')  {
26683                 node.removeAttribute(a.name);
26684                 continue;
26685             }
26686             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26687                 node.removeAttribute(a.name);
26688                 continue;
26689             }
26690             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26691                 cleanAttr(a.name,a.value); // fixme..
26692                 continue;
26693             }
26694             if (a.name == 'style') {
26695                 cleanStyle(a.name,a.value);
26696                 continue;
26697             }
26698             /// clean up MS crap..
26699             // tecnically this should be a list of valid class'es..
26700             
26701             
26702             if (a.name == 'class') {
26703                 if (a.value.match(/^Mso/)) {
26704                     node.removeAttribute('class');
26705                 }
26706                 
26707                 if (a.value.match(/^body$/)) {
26708                     node.removeAttribute('class');
26709                 }
26710                 continue;
26711             }
26712             
26713             // style cleanup!?
26714             // class cleanup?
26715             
26716         }
26717         
26718         
26719         this.cleanUpChildren(node);
26720         
26721         
26722     },
26723     
26724     /**
26725      * Clean up MS wordisms...
26726      */
26727     cleanWord : function(node)
26728     {
26729         if (!node) {
26730             this.cleanWord(this.doc.body);
26731             return;
26732         }
26733         
26734         if(
26735                 node.nodeName == 'SPAN' &&
26736                 !node.hasAttributes() &&
26737                 node.childNodes.length == 1 &&
26738                 node.firstChild.nodeName == "#text"  
26739         ) {
26740             var textNode = node.firstChild;
26741             node.removeChild(textNode);
26742             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26743                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26744             }
26745             node.parentNode.insertBefore(textNode, node);
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.removeChild(node);
26750         }
26751         
26752         if (node.nodeName == "#text") {
26753             // clean up silly Windows -- stuff?
26754             return; 
26755         }
26756         if (node.nodeName == "#comment") {
26757             node.parentNode.removeChild(node);
26758             // clean up silly Windows -- stuff?
26759             return; 
26760         }
26761         
26762         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26763             node.parentNode.removeChild(node);
26764             return;
26765         }
26766         //Roo.log(node.tagName);
26767         // remove - but keep children..
26768         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26769             //Roo.log('-- removed');
26770             while (node.childNodes.length) {
26771                 var cn = node.childNodes[0];
26772                 node.removeChild(cn);
26773                 node.parentNode.insertBefore(cn, node);
26774                 // move node to parent - and clean it..
26775                 this.cleanWord(cn);
26776             }
26777             node.parentNode.removeChild(node);
26778             /// no need to iterate chidlren = it's got none..
26779             //this.iterateChildren(node, this.cleanWord);
26780             return;
26781         }
26782         // clean styles
26783         if (node.className.length) {
26784             
26785             var cn = node.className.split(/\W+/);
26786             var cna = [];
26787             Roo.each(cn, function(cls) {
26788                 if (cls.match(/Mso[a-zA-Z]+/)) {
26789                     return;
26790                 }
26791                 cna.push(cls);
26792             });
26793             node.className = cna.length ? cna.join(' ') : '';
26794             if (!cna.length) {
26795                 node.removeAttribute("class");
26796             }
26797         }
26798         
26799         if (node.hasAttribute("lang")) {
26800             node.removeAttribute("lang");
26801         }
26802         
26803         if (node.hasAttribute("style")) {
26804             
26805             var styles = node.getAttribute("style").split(";");
26806             var nstyle = [];
26807             Roo.each(styles, function(s) {
26808                 if (!s.match(/:/)) {
26809                     return;
26810                 }
26811                 var kv = s.split(":");
26812                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26813                     return;
26814                 }
26815                 // what ever is left... we allow.
26816                 nstyle.push(s);
26817             });
26818             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26819             if (!nstyle.length) {
26820                 node.removeAttribute('style');
26821             }
26822         }
26823         this.iterateChildren(node, this.cleanWord);
26824         
26825         
26826         
26827     },
26828     /**
26829      * iterateChildren of a Node, calling fn each time, using this as the scole..
26830      * @param {DomNode} node node to iterate children of.
26831      * @param {Function} fn method of this class to call on each item.
26832      */
26833     iterateChildren : function(node, fn)
26834     {
26835         if (!node.childNodes.length) {
26836                 return;
26837         }
26838         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26839            fn.call(this, node.childNodes[i])
26840         }
26841     },
26842     
26843     
26844     /**
26845      * cleanTableWidths.
26846      *
26847      * Quite often pasting from word etc.. results in tables with column and widths.
26848      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26849      *
26850      */
26851     cleanTableWidths : function(node)
26852     {
26853          
26854          
26855         if (!node) {
26856             this.cleanTableWidths(this.doc.body);
26857             return;
26858         }
26859         
26860         // ignore list...
26861         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26862             return; 
26863         }
26864         Roo.log(node.tagName);
26865         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26866             this.iterateChildren(node, this.cleanTableWidths);
26867             return;
26868         }
26869         if (node.hasAttribute('width')) {
26870             node.removeAttribute('width');
26871         }
26872         
26873          
26874         if (node.hasAttribute("style")) {
26875             // pretty basic...
26876             
26877             var styles = node.getAttribute("style").split(";");
26878             var nstyle = [];
26879             Roo.each(styles, function(s) {
26880                 if (!s.match(/:/)) {
26881                     return;
26882                 }
26883                 var kv = s.split(":");
26884                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26885                     return;
26886                 }
26887                 // what ever is left... we allow.
26888                 nstyle.push(s);
26889             });
26890             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26891             if (!nstyle.length) {
26892                 node.removeAttribute('style');
26893             }
26894         }
26895         
26896         this.iterateChildren(node, this.cleanTableWidths);
26897         
26898         
26899     },
26900     
26901     
26902     
26903     
26904     domToHTML : function(currentElement, depth, nopadtext) {
26905         
26906         depth = depth || 0;
26907         nopadtext = nopadtext || false;
26908     
26909         if (!currentElement) {
26910             return this.domToHTML(this.doc.body);
26911         }
26912         
26913         //Roo.log(currentElement);
26914         var j;
26915         var allText = false;
26916         var nodeName = currentElement.nodeName;
26917         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26918         
26919         if  (nodeName == '#text') {
26920             
26921             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26922         }
26923         
26924         
26925         var ret = '';
26926         if (nodeName != 'BODY') {
26927              
26928             var i = 0;
26929             // Prints the node tagName, such as <A>, <IMG>, etc
26930             if (tagName) {
26931                 var attr = [];
26932                 for(i = 0; i < currentElement.attributes.length;i++) {
26933                     // quoting?
26934                     var aname = currentElement.attributes.item(i).name;
26935                     if (!currentElement.attributes.item(i).value.length) {
26936                         continue;
26937                     }
26938                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26939                 }
26940                 
26941                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26942             } 
26943             else {
26944                 
26945                 // eack
26946             }
26947         } else {
26948             tagName = false;
26949         }
26950         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26951             return ret;
26952         }
26953         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26954             nopadtext = true;
26955         }
26956         
26957         
26958         // Traverse the tree
26959         i = 0;
26960         var currentElementChild = currentElement.childNodes.item(i);
26961         var allText = true;
26962         var innerHTML  = '';
26963         lastnode = '';
26964         while (currentElementChild) {
26965             // Formatting code (indent the tree so it looks nice on the screen)
26966             var nopad = nopadtext;
26967             if (lastnode == 'SPAN') {
26968                 nopad  = true;
26969             }
26970             // text
26971             if  (currentElementChild.nodeName == '#text') {
26972                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26973                 toadd = nopadtext ? toadd : toadd.trim();
26974                 if (!nopad && toadd.length > 80) {
26975                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26976                 }
26977                 innerHTML  += toadd;
26978                 
26979                 i++;
26980                 currentElementChild = currentElement.childNodes.item(i);
26981                 lastNode = '';
26982                 continue;
26983             }
26984             allText = false;
26985             
26986             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26987                 
26988             // Recursively traverse the tree structure of the child node
26989             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26990             lastnode = currentElementChild.nodeName;
26991             i++;
26992             currentElementChild=currentElement.childNodes.item(i);
26993         }
26994         
26995         ret += innerHTML;
26996         
26997         if (!allText) {
26998                 // The remaining code is mostly for formatting the tree
26999             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27000         }
27001         
27002         
27003         if (tagName) {
27004             ret+= "</"+tagName+">";
27005         }
27006         return ret;
27007         
27008     },
27009         
27010     applyBlacklists : function()
27011     {
27012         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27013         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27014         
27015         this.white = [];
27016         this.black = [];
27017         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27018             if (b.indexOf(tag) > -1) {
27019                 return;
27020             }
27021             this.white.push(tag);
27022             
27023         }, this);
27024         
27025         Roo.each(w, function(tag) {
27026             if (b.indexOf(tag) > -1) {
27027                 return;
27028             }
27029             if (this.white.indexOf(tag) > -1) {
27030                 return;
27031             }
27032             this.white.push(tag);
27033             
27034         }, this);
27035         
27036         
27037         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27038             if (w.indexOf(tag) > -1) {
27039                 return;
27040             }
27041             this.black.push(tag);
27042             
27043         }, this);
27044         
27045         Roo.each(b, function(tag) {
27046             if (w.indexOf(tag) > -1) {
27047                 return;
27048             }
27049             if (this.black.indexOf(tag) > -1) {
27050                 return;
27051             }
27052             this.black.push(tag);
27053             
27054         }, this);
27055         
27056         
27057         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27058         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27059         
27060         this.cwhite = [];
27061         this.cblack = [];
27062         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27063             if (b.indexOf(tag) > -1) {
27064                 return;
27065             }
27066             this.cwhite.push(tag);
27067             
27068         }, this);
27069         
27070         Roo.each(w, function(tag) {
27071             if (b.indexOf(tag) > -1) {
27072                 return;
27073             }
27074             if (this.cwhite.indexOf(tag) > -1) {
27075                 return;
27076             }
27077             this.cwhite.push(tag);
27078             
27079         }, this);
27080         
27081         
27082         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27083             if (w.indexOf(tag) > -1) {
27084                 return;
27085             }
27086             this.cblack.push(tag);
27087             
27088         }, this);
27089         
27090         Roo.each(b, function(tag) {
27091             if (w.indexOf(tag) > -1) {
27092                 return;
27093             }
27094             if (this.cblack.indexOf(tag) > -1) {
27095                 return;
27096             }
27097             this.cblack.push(tag);
27098             
27099         }, this);
27100     },
27101     
27102     setStylesheets : function(stylesheets)
27103     {
27104         if(typeof(stylesheets) == 'string'){
27105             Roo.get(this.iframe.contentDocument.head).createChild({
27106                 tag : 'link',
27107                 rel : 'stylesheet',
27108                 type : 'text/css',
27109                 href : stylesheets
27110             });
27111             
27112             return;
27113         }
27114         var _this = this;
27115      
27116         Roo.each(stylesheets, function(s) {
27117             if(!s.length){
27118                 return;
27119             }
27120             
27121             Roo.get(_this.iframe.contentDocument.head).createChild({
27122                 tag : 'link',
27123                 rel : 'stylesheet',
27124                 type : 'text/css',
27125                 href : s
27126             });
27127         });
27128
27129         
27130     },
27131     
27132     removeStylesheets : function()
27133     {
27134         var _this = this;
27135         
27136         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27137             s.remove();
27138         });
27139     },
27140     
27141     setStyle : function(style)
27142     {
27143         Roo.get(this.iframe.contentDocument.head).createChild({
27144             tag : 'style',
27145             type : 'text/css',
27146             html : style
27147         });
27148
27149         return;
27150     }
27151     
27152     // hide stuff that is not compatible
27153     /**
27154      * @event blur
27155      * @hide
27156      */
27157     /**
27158      * @event change
27159      * @hide
27160      */
27161     /**
27162      * @event focus
27163      * @hide
27164      */
27165     /**
27166      * @event specialkey
27167      * @hide
27168      */
27169     /**
27170      * @cfg {String} fieldClass @hide
27171      */
27172     /**
27173      * @cfg {String} focusClass @hide
27174      */
27175     /**
27176      * @cfg {String} autoCreate @hide
27177      */
27178     /**
27179      * @cfg {String} inputType @hide
27180      */
27181     /**
27182      * @cfg {String} invalidClass @hide
27183      */
27184     /**
27185      * @cfg {String} invalidText @hide
27186      */
27187     /**
27188      * @cfg {String} msgFx @hide
27189      */
27190     /**
27191      * @cfg {String} validateOnBlur @hide
27192      */
27193 });
27194
27195 Roo.HtmlEditorCore.white = [
27196         'area', 'br', 'img', 'input', 'hr', 'wbr',
27197         
27198        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27199        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27200        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27201        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27202        'table',   'ul',         'xmp', 
27203        
27204        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27205       'thead',   'tr', 
27206      
27207       'dir', 'menu', 'ol', 'ul', 'dl',
27208        
27209       'embed',  'object'
27210 ];
27211
27212
27213 Roo.HtmlEditorCore.black = [
27214     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27215         'applet', // 
27216         'base',   'basefont', 'bgsound', 'blink',  'body', 
27217         'frame',  'frameset', 'head',    'html',   'ilayer', 
27218         'iframe', 'layer',  'link',     'meta',    'object',   
27219         'script', 'style' ,'title',  'xml' // clean later..
27220 ];
27221 Roo.HtmlEditorCore.clean = [
27222     'script', 'style', 'title', 'xml'
27223 ];
27224 Roo.HtmlEditorCore.remove = [
27225     'font'
27226 ];
27227 // attributes..
27228
27229 Roo.HtmlEditorCore.ablack = [
27230     'on'
27231 ];
27232     
27233 Roo.HtmlEditorCore.aclean = [ 
27234     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27235 ];
27236
27237 // protocols..
27238 Roo.HtmlEditorCore.pwhite= [
27239         'http',  'https',  'mailto'
27240 ];
27241
27242 // white listed style attributes.
27243 Roo.HtmlEditorCore.cwhite= [
27244       //  'text-align', /// default is to allow most things..
27245       
27246          
27247 //        'font-size'//??
27248 ];
27249
27250 // black listed style attributes.
27251 Roo.HtmlEditorCore.cblack= [
27252       //  'font-size' -- this can be set by the project 
27253 ];
27254
27255
27256 Roo.HtmlEditorCore.swapCodes   =[ 
27257     [    8211, "&#8211;" ], 
27258     [    8212, "&#8212;" ], 
27259     [    8216,  "'" ],  
27260     [    8217, "'" ],  
27261     [    8220, '"' ],  
27262     [    8221, '"' ],  
27263     [    8226, "*" ],  
27264     [    8230, "..." ]
27265 ]; 
27266
27267     /*
27268  * - LGPL
27269  *
27270  * HtmlEditor
27271  * 
27272  */
27273
27274 /**
27275  * @class Roo.bootstrap.HtmlEditor
27276  * @extends Roo.bootstrap.TextArea
27277  * Bootstrap HtmlEditor class
27278
27279  * @constructor
27280  * Create a new HtmlEditor
27281  * @param {Object} config The config object
27282  */
27283
27284 Roo.bootstrap.HtmlEditor = function(config){
27285     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27286     if (!this.toolbars) {
27287         this.toolbars = [];
27288     }
27289     
27290     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27291     this.addEvents({
27292             /**
27293              * @event initialize
27294              * Fires when the editor is fully initialized (including the iframe)
27295              * @param {HtmlEditor} this
27296              */
27297             initialize: true,
27298             /**
27299              * @event activate
27300              * Fires when the editor is first receives the focus. Any insertion must wait
27301              * until after this event.
27302              * @param {HtmlEditor} this
27303              */
27304             activate: true,
27305              /**
27306              * @event beforesync
27307              * Fires before the textarea is updated with content from the editor iframe. Return false
27308              * to cancel the sync.
27309              * @param {HtmlEditor} this
27310              * @param {String} html
27311              */
27312             beforesync: true,
27313              /**
27314              * @event beforepush
27315              * Fires before the iframe editor is updated with content from the textarea. Return false
27316              * to cancel the push.
27317              * @param {HtmlEditor} this
27318              * @param {String} html
27319              */
27320             beforepush: true,
27321              /**
27322              * @event sync
27323              * Fires when the textarea is updated with content from the editor iframe.
27324              * @param {HtmlEditor} this
27325              * @param {String} html
27326              */
27327             sync: true,
27328              /**
27329              * @event push
27330              * Fires when the iframe editor is updated with content from the textarea.
27331              * @param {HtmlEditor} this
27332              * @param {String} html
27333              */
27334             push: true,
27335              /**
27336              * @event editmodechange
27337              * Fires when the editor switches edit modes
27338              * @param {HtmlEditor} this
27339              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27340              */
27341             editmodechange: true,
27342             /**
27343              * @event editorevent
27344              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27345              * @param {HtmlEditor} this
27346              */
27347             editorevent: true,
27348             /**
27349              * @event firstfocus
27350              * Fires when on first focus - needed by toolbars..
27351              * @param {HtmlEditor} this
27352              */
27353             firstfocus: true,
27354             /**
27355              * @event autosave
27356              * Auto save the htmlEditor value as a file into Events
27357              * @param {HtmlEditor} this
27358              */
27359             autosave: true,
27360             /**
27361              * @event savedpreview
27362              * preview the saved version of htmlEditor
27363              * @param {HtmlEditor} this
27364              */
27365             savedpreview: true
27366         });
27367 };
27368
27369
27370 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27371     
27372     
27373       /**
27374      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27375      */
27376     toolbars : false,
27377     
27378      /**
27379     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27380     */
27381     btns : [],
27382    
27383      /**
27384      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27385      *                        Roo.resizable.
27386      */
27387     resizable : false,
27388      /**
27389      * @cfg {Number} height (in pixels)
27390      */   
27391     height: 300,
27392    /**
27393      * @cfg {Number} width (in pixels)
27394      */   
27395     width: false,
27396     
27397     /**
27398      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27399      * 
27400      */
27401     stylesheets: false,
27402     
27403     // id of frame..
27404     frameId: false,
27405     
27406     // private properties
27407     validationEvent : false,
27408     deferHeight: true,
27409     initialized : false,
27410     activated : false,
27411     
27412     onFocus : Roo.emptyFn,
27413     iframePad:3,
27414     hideMode:'offsets',
27415     
27416     tbContainer : false,
27417     
27418     bodyCls : '',
27419     
27420     toolbarContainer :function() {
27421         return this.wrap.select('.x-html-editor-tb',true).first();
27422     },
27423
27424     /**
27425      * Protected method that will not generally be called directly. It
27426      * is called when the editor creates its toolbar. Override this method if you need to
27427      * add custom toolbar buttons.
27428      * @param {HtmlEditor} editor
27429      */
27430     createToolbar : function(){
27431         Roo.log('renewing');
27432         Roo.log("create toolbars");
27433         
27434         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27435         this.toolbars[0].render(this.toolbarContainer());
27436         
27437         return;
27438         
27439 //        if (!editor.toolbars || !editor.toolbars.length) {
27440 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27441 //        }
27442 //        
27443 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27444 //            editor.toolbars[i] = Roo.factory(
27445 //                    typeof(editor.toolbars[i]) == 'string' ?
27446 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27447 //                Roo.bootstrap.HtmlEditor);
27448 //            editor.toolbars[i].init(editor);
27449 //        }
27450     },
27451
27452      
27453     // private
27454     onRender : function(ct, position)
27455     {
27456        // Roo.log("Call onRender: " + this.xtype);
27457         var _t = this;
27458         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27459       
27460         this.wrap = this.inputEl().wrap({
27461             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27462         });
27463         
27464         this.editorcore.onRender(ct, position);
27465          
27466         if (this.resizable) {
27467             this.resizeEl = new Roo.Resizable(this.wrap, {
27468                 pinned : true,
27469                 wrap: true,
27470                 dynamic : true,
27471                 minHeight : this.height,
27472                 height: this.height,
27473                 handles : this.resizable,
27474                 width: this.width,
27475                 listeners : {
27476                     resize : function(r, w, h) {
27477                         _t.onResize(w,h); // -something
27478                     }
27479                 }
27480             });
27481             
27482         }
27483         this.createToolbar(this);
27484        
27485         
27486         if(!this.width && this.resizable){
27487             this.setSize(this.wrap.getSize());
27488         }
27489         if (this.resizeEl) {
27490             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27491             // should trigger onReize..
27492         }
27493         
27494     },
27495
27496     // private
27497     onResize : function(w, h)
27498     {
27499         Roo.log('resize: ' +w + ',' + h );
27500         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27501         var ew = false;
27502         var eh = false;
27503         
27504         if(this.inputEl() ){
27505             if(typeof w == 'number'){
27506                 var aw = w - this.wrap.getFrameWidth('lr');
27507                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27508                 ew = aw;
27509             }
27510             if(typeof h == 'number'){
27511                  var tbh = -11;  // fixme it needs to tool bar size!
27512                 for (var i =0; i < this.toolbars.length;i++) {
27513                     // fixme - ask toolbars for heights?
27514                     tbh += this.toolbars[i].el.getHeight();
27515                     //if (this.toolbars[i].footer) {
27516                     //    tbh += this.toolbars[i].footer.el.getHeight();
27517                     //}
27518                 }
27519               
27520                 
27521                 
27522                 
27523                 
27524                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27525                 ah -= 5; // knock a few pixes off for look..
27526                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27527                 var eh = ah;
27528             }
27529         }
27530         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27531         this.editorcore.onResize(ew,eh);
27532         
27533     },
27534
27535     /**
27536      * Toggles the editor between standard and source edit mode.
27537      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27538      */
27539     toggleSourceEdit : function(sourceEditMode)
27540     {
27541         this.editorcore.toggleSourceEdit(sourceEditMode);
27542         
27543         if(this.editorcore.sourceEditMode){
27544             Roo.log('editor - showing textarea');
27545             
27546 //            Roo.log('in');
27547 //            Roo.log(this.syncValue());
27548             this.syncValue();
27549             this.inputEl().removeClass(['hide', 'x-hidden']);
27550             this.inputEl().dom.removeAttribute('tabIndex');
27551             this.inputEl().focus();
27552         }else{
27553             Roo.log('editor - hiding textarea');
27554 //            Roo.log('out')
27555 //            Roo.log(this.pushValue()); 
27556             this.pushValue();
27557             
27558             this.inputEl().addClass(['hide', 'x-hidden']);
27559             this.inputEl().dom.setAttribute('tabIndex', -1);
27560             //this.deferFocus();
27561         }
27562          
27563         if(this.resizable){
27564             this.setSize(this.wrap.getSize());
27565         }
27566         
27567         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27568     },
27569  
27570     // private (for BoxComponent)
27571     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27572
27573     // private (for BoxComponent)
27574     getResizeEl : function(){
27575         return this.wrap;
27576     },
27577
27578     // private (for BoxComponent)
27579     getPositionEl : function(){
27580         return this.wrap;
27581     },
27582
27583     // private
27584     initEvents : function(){
27585         this.originalValue = this.getValue();
27586     },
27587
27588 //    /**
27589 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27590 //     * @method
27591 //     */
27592 //    markInvalid : Roo.emptyFn,
27593 //    /**
27594 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27595 //     * @method
27596 //     */
27597 //    clearInvalid : Roo.emptyFn,
27598
27599     setValue : function(v){
27600         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27601         this.editorcore.pushValue();
27602     },
27603
27604      
27605     // private
27606     deferFocus : function(){
27607         this.focus.defer(10, this);
27608     },
27609
27610     // doc'ed in Field
27611     focus : function(){
27612         this.editorcore.focus();
27613         
27614     },
27615       
27616
27617     // private
27618     onDestroy : function(){
27619         
27620         
27621         
27622         if(this.rendered){
27623             
27624             for (var i =0; i < this.toolbars.length;i++) {
27625                 // fixme - ask toolbars for heights?
27626                 this.toolbars[i].onDestroy();
27627             }
27628             
27629             this.wrap.dom.innerHTML = '';
27630             this.wrap.remove();
27631         }
27632     },
27633
27634     // private
27635     onFirstFocus : function(){
27636         //Roo.log("onFirstFocus");
27637         this.editorcore.onFirstFocus();
27638          for (var i =0; i < this.toolbars.length;i++) {
27639             this.toolbars[i].onFirstFocus();
27640         }
27641         
27642     },
27643     
27644     // private
27645     syncValue : function()
27646     {   
27647         this.editorcore.syncValue();
27648     },
27649     
27650     pushValue : function()
27651     {   
27652         this.editorcore.pushValue();
27653     }
27654      
27655     
27656     // hide stuff that is not compatible
27657     /**
27658      * @event blur
27659      * @hide
27660      */
27661     /**
27662      * @event change
27663      * @hide
27664      */
27665     /**
27666      * @event focus
27667      * @hide
27668      */
27669     /**
27670      * @event specialkey
27671      * @hide
27672      */
27673     /**
27674      * @cfg {String} fieldClass @hide
27675      */
27676     /**
27677      * @cfg {String} focusClass @hide
27678      */
27679     /**
27680      * @cfg {String} autoCreate @hide
27681      */
27682     /**
27683      * @cfg {String} inputType @hide
27684      */
27685      
27686     /**
27687      * @cfg {String} invalidText @hide
27688      */
27689     /**
27690      * @cfg {String} msgFx @hide
27691      */
27692     /**
27693      * @cfg {String} validateOnBlur @hide
27694      */
27695 });
27696  
27697     
27698    
27699    
27700    
27701       
27702 Roo.namespace('Roo.bootstrap.htmleditor');
27703 /**
27704  * @class Roo.bootstrap.HtmlEditorToolbar1
27705  * Basic Toolbar
27706  * 
27707  * @example
27708  * Usage:
27709  *
27710  new Roo.bootstrap.HtmlEditor({
27711     ....
27712     toolbars : [
27713         new Roo.bootstrap.HtmlEditorToolbar1({
27714             disable : { fonts: 1 , format: 1, ..., ... , ...],
27715             btns : [ .... ]
27716         })
27717     }
27718      
27719  * 
27720  * @cfg {Object} disable List of elements to disable..
27721  * @cfg {Array} btns List of additional buttons.
27722  * 
27723  * 
27724  * NEEDS Extra CSS? 
27725  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27726  */
27727  
27728 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27729 {
27730     
27731     Roo.apply(this, config);
27732     
27733     // default disabled, based on 'good practice'..
27734     this.disable = this.disable || {};
27735     Roo.applyIf(this.disable, {
27736         fontSize : true,
27737         colors : true,
27738         specialElements : true
27739     });
27740     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27741     
27742     this.editor = config.editor;
27743     this.editorcore = config.editor.editorcore;
27744     
27745     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27746     
27747     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27748     // dont call parent... till later.
27749 }
27750 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27751      
27752     bar : true,
27753     
27754     editor : false,
27755     editorcore : false,
27756     
27757     
27758     formats : [
27759         "p" ,  
27760         "h1","h2","h3","h4","h5","h6", 
27761         "pre", "code", 
27762         "abbr", "acronym", "address", "cite", "samp", "var",
27763         'div','span'
27764     ],
27765     
27766     onRender : function(ct, position)
27767     {
27768        // Roo.log("Call onRender: " + this.xtype);
27769         
27770        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27771        Roo.log(this.el);
27772        this.el.dom.style.marginBottom = '0';
27773        var _this = this;
27774        var editorcore = this.editorcore;
27775        var editor= this.editor;
27776        
27777        var children = [];
27778        var btn = function(id,cmd , toggle, handler, html){
27779        
27780             var  event = toggle ? 'toggle' : 'click';
27781        
27782             var a = {
27783                 size : 'sm',
27784                 xtype: 'Button',
27785                 xns: Roo.bootstrap,
27786                 //glyphicon : id,
27787                 fa: id,
27788                 cmd : id || cmd,
27789                 enableToggle:toggle !== false,
27790                 html : html || '',
27791                 pressed : toggle ? false : null,
27792                 listeners : {}
27793             };
27794             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27795                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27796             };
27797             children.push(a);
27798             return a;
27799        }
27800        
27801     //    var cb_box = function...
27802         
27803         var style = {
27804                 xtype: 'Button',
27805                 size : 'sm',
27806                 xns: Roo.bootstrap,
27807                 fa : 'font',
27808                 //html : 'submit'
27809                 menu : {
27810                     xtype: 'Menu',
27811                     xns: Roo.bootstrap,
27812                     items:  []
27813                 }
27814         };
27815         Roo.each(this.formats, function(f) {
27816             style.menu.items.push({
27817                 xtype :'MenuItem',
27818                 xns: Roo.bootstrap,
27819                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27820                 tagname : f,
27821                 listeners : {
27822                     click : function()
27823                     {
27824                         editorcore.insertTag(this.tagname);
27825                         editor.focus();
27826                     }
27827                 }
27828                 
27829             });
27830         });
27831         children.push(style);   
27832         
27833         btn('bold',false,true);
27834         btn('italic',false,true);
27835         btn('align-left', 'justifyleft',true);
27836         btn('align-center', 'justifycenter',true);
27837         btn('align-right' , 'justifyright',true);
27838         btn('link', false, false, function(btn) {
27839             //Roo.log("create link?");
27840             var url = prompt(this.createLinkText, this.defaultLinkValue);
27841             if(url && url != 'http:/'+'/'){
27842                 this.editorcore.relayCmd('createlink', url);
27843             }
27844         }),
27845         btn('list','insertunorderedlist',true);
27846         btn('pencil', false,true, function(btn){
27847                 Roo.log(this);
27848                 this.toggleSourceEdit(btn.pressed);
27849         });
27850         
27851         if (this.editor.btns.length > 0) {
27852             for (var i = 0; i<this.editor.btns.length; i++) {
27853                 children.push(this.editor.btns[i]);
27854             }
27855         }
27856         
27857         /*
27858         var cog = {
27859                 xtype: 'Button',
27860                 size : 'sm',
27861                 xns: Roo.bootstrap,
27862                 glyphicon : 'cog',
27863                 //html : 'submit'
27864                 menu : {
27865                     xtype: 'Menu',
27866                     xns: Roo.bootstrap,
27867                     items:  []
27868                 }
27869         };
27870         
27871         cog.menu.items.push({
27872             xtype :'MenuItem',
27873             xns: Roo.bootstrap,
27874             html : Clean styles,
27875             tagname : f,
27876             listeners : {
27877                 click : function()
27878                 {
27879                     editorcore.insertTag(this.tagname);
27880                     editor.focus();
27881                 }
27882             }
27883             
27884         });
27885        */
27886         
27887          
27888        this.xtype = 'NavSimplebar';
27889         
27890         for(var i=0;i< children.length;i++) {
27891             
27892             this.buttons.add(this.addxtypeChild(children[i]));
27893             
27894         }
27895         
27896         editor.on('editorevent', this.updateToolbar, this);
27897     },
27898     onBtnClick : function(id)
27899     {
27900        this.editorcore.relayCmd(id);
27901        this.editorcore.focus();
27902     },
27903     
27904     /**
27905      * Protected method that will not generally be called directly. It triggers
27906      * a toolbar update by reading the markup state of the current selection in the editor.
27907      */
27908     updateToolbar: function(){
27909
27910         if(!this.editorcore.activated){
27911             this.editor.onFirstFocus(); // is this neeed?
27912             return;
27913         }
27914
27915         var btns = this.buttons; 
27916         var doc = this.editorcore.doc;
27917         btns.get('bold').setActive(doc.queryCommandState('bold'));
27918         btns.get('italic').setActive(doc.queryCommandState('italic'));
27919         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27920         
27921         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27922         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27923         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27924         
27925         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27926         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27927          /*
27928         
27929         var ans = this.editorcore.getAllAncestors();
27930         if (this.formatCombo) {
27931             
27932             
27933             var store = this.formatCombo.store;
27934             this.formatCombo.setValue("");
27935             for (var i =0; i < ans.length;i++) {
27936                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27937                     // select it..
27938                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27939                     break;
27940                 }
27941             }
27942         }
27943         
27944         
27945         
27946         // hides menus... - so this cant be on a menu...
27947         Roo.bootstrap.MenuMgr.hideAll();
27948         */
27949         Roo.bootstrap.MenuMgr.hideAll();
27950         //this.editorsyncValue();
27951     },
27952     onFirstFocus: function() {
27953         this.buttons.each(function(item){
27954            item.enable();
27955         });
27956     },
27957     toggleSourceEdit : function(sourceEditMode){
27958         
27959           
27960         if(sourceEditMode){
27961             Roo.log("disabling buttons");
27962            this.buttons.each( function(item){
27963                 if(item.cmd != 'pencil'){
27964                     item.disable();
27965                 }
27966             });
27967           
27968         }else{
27969             Roo.log("enabling buttons");
27970             if(this.editorcore.initialized){
27971                 this.buttons.each( function(item){
27972                     item.enable();
27973                 });
27974             }
27975             
27976         }
27977         Roo.log("calling toggole on editor");
27978         // tell the editor that it's been pressed..
27979         this.editor.toggleSourceEdit(sourceEditMode);
27980        
27981     }
27982 });
27983
27984
27985
27986
27987  
27988 /*
27989  * - LGPL
27990  */
27991
27992 /**
27993  * @class Roo.bootstrap.Markdown
27994  * @extends Roo.bootstrap.TextArea
27995  * Bootstrap Showdown editable area
27996  * @cfg {string} content
27997  * 
27998  * @constructor
27999  * Create a new Showdown
28000  */
28001
28002 Roo.bootstrap.Markdown = function(config){
28003     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28004    
28005 };
28006
28007 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28008     
28009     editing :false,
28010     
28011     initEvents : function()
28012     {
28013         
28014         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28015         this.markdownEl = this.el.createChild({
28016             cls : 'roo-markdown-area'
28017         });
28018         this.inputEl().addClass('d-none');
28019         if (this.getValue() == '') {
28020             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28021             
28022         } else {
28023             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28024         }
28025         this.markdownEl.on('click', this.toggleTextEdit, this);
28026         this.on('blur', this.toggleTextEdit, this);
28027         this.on('specialkey', this.resizeTextArea, this);
28028     },
28029     
28030     toggleTextEdit : function()
28031     {
28032         var sh = this.markdownEl.getHeight();
28033         this.inputEl().addClass('d-none');
28034         this.markdownEl.addClass('d-none');
28035         if (!this.editing) {
28036             // show editor?
28037             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28038             this.inputEl().removeClass('d-none');
28039             this.inputEl().focus();
28040             this.editing = true;
28041             return;
28042         }
28043         // show showdown...
28044         this.updateMarkdown();
28045         this.markdownEl.removeClass('d-none');
28046         this.editing = false;
28047         return;
28048     },
28049     updateMarkdown : function()
28050     {
28051         if (this.getValue() == '') {
28052             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28053             return;
28054         }
28055  
28056         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28057     },
28058     
28059     resizeTextArea: function () {
28060         
28061         var sh = 100;
28062         Roo.log([sh, this.getValue().split("\n").length * 30]);
28063         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28064     },
28065     setValue : function(val)
28066     {
28067         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28068         if (!this.editing) {
28069             this.updateMarkdown();
28070         }
28071         
28072     },
28073     focus : function()
28074     {
28075         if (!this.editing) {
28076             this.toggleTextEdit();
28077         }
28078         
28079     }
28080
28081
28082 });/*
28083  * Based on:
28084  * Ext JS Library 1.1.1
28085  * Copyright(c) 2006-2007, Ext JS, LLC.
28086  *
28087  * Originally Released Under LGPL - original licence link has changed is not relivant.
28088  *
28089  * Fork - LGPL
28090  * <script type="text/javascript">
28091  */
28092  
28093 /**
28094  * @class Roo.bootstrap.PagingToolbar
28095  * @extends Roo.bootstrap.NavSimplebar
28096  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28097  * @constructor
28098  * Create a new PagingToolbar
28099  * @param {Object} config The config object
28100  * @param {Roo.data.Store} store
28101  */
28102 Roo.bootstrap.PagingToolbar = function(config)
28103 {
28104     // old args format still supported... - xtype is prefered..
28105         // created from xtype...
28106     
28107     this.ds = config.dataSource;
28108     
28109     if (config.store && !this.ds) {
28110         this.store= Roo.factory(config.store, Roo.data);
28111         this.ds = this.store;
28112         this.ds.xmodule = this.xmodule || false;
28113     }
28114     
28115     this.toolbarItems = [];
28116     if (config.items) {
28117         this.toolbarItems = config.items;
28118     }
28119     
28120     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28121     
28122     this.cursor = 0;
28123     
28124     if (this.ds) { 
28125         this.bind(this.ds);
28126     }
28127     
28128     if (Roo.bootstrap.version == 4) {
28129         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28130     } else {
28131         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28132     }
28133     
28134 };
28135
28136 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28137     /**
28138      * @cfg {Roo.data.Store} dataSource
28139      * The underlying data store providing the paged data
28140      */
28141     /**
28142      * @cfg {String/HTMLElement/Element} container
28143      * container The id or element that will contain the toolbar
28144      */
28145     /**
28146      * @cfg {Boolean} displayInfo
28147      * True to display the displayMsg (defaults to false)
28148      */
28149     /**
28150      * @cfg {Number} pageSize
28151      * The number of records to display per page (defaults to 20)
28152      */
28153     pageSize: 20,
28154     /**
28155      * @cfg {String} displayMsg
28156      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28157      */
28158     displayMsg : 'Displaying {0} - {1} of {2}',
28159     /**
28160      * @cfg {String} emptyMsg
28161      * The message to display when no records are found (defaults to "No data to display")
28162      */
28163     emptyMsg : 'No data to display',
28164     /**
28165      * Customizable piece of the default paging text (defaults to "Page")
28166      * @type String
28167      */
28168     beforePageText : "Page",
28169     /**
28170      * Customizable piece of the default paging text (defaults to "of %0")
28171      * @type String
28172      */
28173     afterPageText : "of {0}",
28174     /**
28175      * Customizable piece of the default paging text (defaults to "First Page")
28176      * @type String
28177      */
28178     firstText : "First Page",
28179     /**
28180      * Customizable piece of the default paging text (defaults to "Previous Page")
28181      * @type String
28182      */
28183     prevText : "Previous Page",
28184     /**
28185      * Customizable piece of the default paging text (defaults to "Next Page")
28186      * @type String
28187      */
28188     nextText : "Next Page",
28189     /**
28190      * Customizable piece of the default paging text (defaults to "Last Page")
28191      * @type String
28192      */
28193     lastText : "Last Page",
28194     /**
28195      * Customizable piece of the default paging text (defaults to "Refresh")
28196      * @type String
28197      */
28198     refreshText : "Refresh",
28199
28200     buttons : false,
28201     // private
28202     onRender : function(ct, position) 
28203     {
28204         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28205         this.navgroup.parentId = this.id;
28206         this.navgroup.onRender(this.el, null);
28207         // add the buttons to the navgroup
28208         
28209         if(this.displayInfo){
28210             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28211             this.displayEl = this.el.select('.x-paging-info', true).first();
28212 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28213 //            this.displayEl = navel.el.select('span',true).first();
28214         }
28215         
28216         var _this = this;
28217         
28218         if(this.buttons){
28219             Roo.each(_this.buttons, function(e){ // this might need to use render????
28220                Roo.factory(e).render(_this.el);
28221             });
28222         }
28223             
28224         Roo.each(_this.toolbarItems, function(e) {
28225             _this.navgroup.addItem(e);
28226         });
28227         
28228         
28229         this.first = this.navgroup.addItem({
28230             tooltip: this.firstText,
28231             cls: "prev btn-outline-secondary",
28232             html : ' <i class="fa fa-step-backward"></i>',
28233             disabled: true,
28234             preventDefault: true,
28235             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28236         });
28237         
28238         this.prev =  this.navgroup.addItem({
28239             tooltip: this.prevText,
28240             cls: "prev btn-outline-secondary",
28241             html : ' <i class="fa fa-backward"></i>',
28242             disabled: true,
28243             preventDefault: true,
28244             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28245         });
28246     //this.addSeparator();
28247         
28248         
28249         var field = this.navgroup.addItem( {
28250             tagtype : 'span',
28251             cls : 'x-paging-position  btn-outline-secondary',
28252              disabled: true,
28253             html : this.beforePageText  +
28254                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28255                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28256          } ); //?? escaped?
28257         
28258         this.field = field.el.select('input', true).first();
28259         this.field.on("keydown", this.onPagingKeydown, this);
28260         this.field.on("focus", function(){this.dom.select();});
28261     
28262     
28263         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28264         //this.field.setHeight(18);
28265         //this.addSeparator();
28266         this.next = this.navgroup.addItem({
28267             tooltip: this.nextText,
28268             cls: "next btn-outline-secondary",
28269             html : ' <i class="fa fa-forward"></i>',
28270             disabled: true,
28271             preventDefault: true,
28272             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28273         });
28274         this.last = this.navgroup.addItem({
28275             tooltip: this.lastText,
28276             html : ' <i class="fa fa-step-forward"></i>',
28277             cls: "next btn-outline-secondary",
28278             disabled: true,
28279             preventDefault: true,
28280             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28281         });
28282     //this.addSeparator();
28283         this.loading = this.navgroup.addItem({
28284             tooltip: this.refreshText,
28285             cls: "btn-outline-secondary",
28286             html : ' <i class="fa fa-refresh"></i>',
28287             preventDefault: true,
28288             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28289         });
28290         
28291     },
28292
28293     // private
28294     updateInfo : function(){
28295         if(this.displayEl){
28296             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28297             var msg = count == 0 ?
28298                 this.emptyMsg :
28299                 String.format(
28300                     this.displayMsg,
28301                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28302                 );
28303             this.displayEl.update(msg);
28304         }
28305     },
28306
28307     // private
28308     onLoad : function(ds, r, o)
28309     {
28310         this.cursor = o.params && o.params.start ? o.params.start : 0;
28311         
28312         var d = this.getPageData(),
28313             ap = d.activePage,
28314             ps = d.pages;
28315         
28316         
28317         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28318         this.field.dom.value = ap;
28319         this.first.setDisabled(ap == 1);
28320         this.prev.setDisabled(ap == 1);
28321         this.next.setDisabled(ap == ps);
28322         this.last.setDisabled(ap == ps);
28323         this.loading.enable();
28324         this.updateInfo();
28325     },
28326
28327     // private
28328     getPageData : function(){
28329         var total = this.ds.getTotalCount();
28330         return {
28331             total : total,
28332             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28333             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28334         };
28335     },
28336
28337     // private
28338     onLoadError : function(){
28339         this.loading.enable();
28340     },
28341
28342     // private
28343     onPagingKeydown : function(e){
28344         var k = e.getKey();
28345         var d = this.getPageData();
28346         if(k == e.RETURN){
28347             var v = this.field.dom.value, pageNum;
28348             if(!v || isNaN(pageNum = parseInt(v, 10))){
28349                 this.field.dom.value = d.activePage;
28350                 return;
28351             }
28352             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28353             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28354             e.stopEvent();
28355         }
28356         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))
28357         {
28358           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28359           this.field.dom.value = pageNum;
28360           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28361           e.stopEvent();
28362         }
28363         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28364         {
28365           var v = this.field.dom.value, pageNum; 
28366           var increment = (e.shiftKey) ? 10 : 1;
28367           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28368                 increment *= -1;
28369           }
28370           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28371             this.field.dom.value = d.activePage;
28372             return;
28373           }
28374           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28375           {
28376             this.field.dom.value = parseInt(v, 10) + increment;
28377             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28378             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28379           }
28380           e.stopEvent();
28381         }
28382     },
28383
28384     // private
28385     beforeLoad : function(){
28386         if(this.loading){
28387             this.loading.disable();
28388         }
28389     },
28390
28391     // private
28392     onClick : function(which){
28393         
28394         var ds = this.ds;
28395         if (!ds) {
28396             return;
28397         }
28398         
28399         switch(which){
28400             case "first":
28401                 ds.load({params:{start: 0, limit: this.pageSize}});
28402             break;
28403             case "prev":
28404                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28405             break;
28406             case "next":
28407                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28408             break;
28409             case "last":
28410                 var total = ds.getTotalCount();
28411                 var extra = total % this.pageSize;
28412                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28413                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28414             break;
28415             case "refresh":
28416                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28417             break;
28418         }
28419     },
28420
28421     /**
28422      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28423      * @param {Roo.data.Store} store The data store to unbind
28424      */
28425     unbind : function(ds){
28426         ds.un("beforeload", this.beforeLoad, this);
28427         ds.un("load", this.onLoad, this);
28428         ds.un("loadexception", this.onLoadError, this);
28429         ds.un("remove", this.updateInfo, this);
28430         ds.un("add", this.updateInfo, this);
28431         this.ds = undefined;
28432     },
28433
28434     /**
28435      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28436      * @param {Roo.data.Store} store The data store to bind
28437      */
28438     bind : function(ds){
28439         ds.on("beforeload", this.beforeLoad, this);
28440         ds.on("load", this.onLoad, this);
28441         ds.on("loadexception", this.onLoadError, this);
28442         ds.on("remove", this.updateInfo, this);
28443         ds.on("add", this.updateInfo, this);
28444         this.ds = ds;
28445     }
28446 });/*
28447  * - LGPL
28448  *
28449  * element
28450  * 
28451  */
28452
28453 /**
28454  * @class Roo.bootstrap.MessageBar
28455  * @extends Roo.bootstrap.Component
28456  * Bootstrap MessageBar class
28457  * @cfg {String} html contents of the MessageBar
28458  * @cfg {String} weight (info | success | warning | danger) default info
28459  * @cfg {String} beforeClass insert the bar before the given class
28460  * @cfg {Boolean} closable (true | false) default false
28461  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28462  * 
28463  * @constructor
28464  * Create a new Element
28465  * @param {Object} config The config object
28466  */
28467
28468 Roo.bootstrap.MessageBar = function(config){
28469     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28470 };
28471
28472 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28473     
28474     html: '',
28475     weight: 'info',
28476     closable: false,
28477     fixed: false,
28478     beforeClass: 'bootstrap-sticky-wrap',
28479     
28480     getAutoCreate : function(){
28481         
28482         var cfg = {
28483             tag: 'div',
28484             cls: 'alert alert-dismissable alert-' + this.weight,
28485             cn: [
28486                 {
28487                     tag: 'span',
28488                     cls: 'message',
28489                     html: this.html || ''
28490                 }
28491             ]
28492         };
28493         
28494         if(this.fixed){
28495             cfg.cls += ' alert-messages-fixed';
28496         }
28497         
28498         if(this.closable){
28499             cfg.cn.push({
28500                 tag: 'button',
28501                 cls: 'close',
28502                 html: 'x'
28503             });
28504         }
28505         
28506         return cfg;
28507     },
28508     
28509     onRender : function(ct, position)
28510     {
28511         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28512         
28513         if(!this.el){
28514             var cfg = Roo.apply({},  this.getAutoCreate());
28515             cfg.id = Roo.id();
28516             
28517             if (this.cls) {
28518                 cfg.cls += ' ' + this.cls;
28519             }
28520             if (this.style) {
28521                 cfg.style = this.style;
28522             }
28523             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28524             
28525             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28526         }
28527         
28528         this.el.select('>button.close').on('click', this.hide, this);
28529         
28530     },
28531     
28532     show : function()
28533     {
28534         if (!this.rendered) {
28535             this.render();
28536         }
28537         
28538         this.el.show();
28539         
28540         this.fireEvent('show', this);
28541         
28542     },
28543     
28544     hide : function()
28545     {
28546         if (!this.rendered) {
28547             this.render();
28548         }
28549         
28550         this.el.hide();
28551         
28552         this.fireEvent('hide', this);
28553     },
28554     
28555     update : function()
28556     {
28557 //        var e = this.el.dom.firstChild;
28558 //        
28559 //        if(this.closable){
28560 //            e = e.nextSibling;
28561 //        }
28562 //        
28563 //        e.data = this.html || '';
28564
28565         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28566     }
28567    
28568 });
28569
28570  
28571
28572      /*
28573  * - LGPL
28574  *
28575  * Graph
28576  * 
28577  */
28578
28579
28580 /**
28581  * @class Roo.bootstrap.Graph
28582  * @extends Roo.bootstrap.Component
28583  * Bootstrap Graph class
28584 > Prameters
28585  -sm {number} sm 4
28586  -md {number} md 5
28587  @cfg {String} graphtype  bar | vbar | pie
28588  @cfg {number} g_x coodinator | centre x (pie)
28589  @cfg {number} g_y coodinator | centre y (pie)
28590  @cfg {number} g_r radius (pie)
28591  @cfg {number} g_height height of the chart (respected by all elements in the set)
28592  @cfg {number} g_width width of the chart (respected by all elements in the set)
28593  @cfg {Object} title The title of the chart
28594     
28595  -{Array}  values
28596  -opts (object) options for the chart 
28597      o {
28598      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28599      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28600      o vgutter (number)
28601      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.
28602      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28603      o to
28604      o stretch (boolean)
28605      o }
28606  -opts (object) options for the pie
28607      o{
28608      o cut
28609      o startAngle (number)
28610      o endAngle (number)
28611      } 
28612  *
28613  * @constructor
28614  * Create a new Input
28615  * @param {Object} config The config object
28616  */
28617
28618 Roo.bootstrap.Graph = function(config){
28619     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28620     
28621     this.addEvents({
28622         // img events
28623         /**
28624          * @event click
28625          * The img click event for the img.
28626          * @param {Roo.EventObject} e
28627          */
28628         "click" : true
28629     });
28630 };
28631
28632 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28633     
28634     sm: 4,
28635     md: 5,
28636     graphtype: 'bar',
28637     g_height: 250,
28638     g_width: 400,
28639     g_x: 50,
28640     g_y: 50,
28641     g_r: 30,
28642     opts:{
28643         //g_colors: this.colors,
28644         g_type: 'soft',
28645         g_gutter: '20%'
28646
28647     },
28648     title : false,
28649
28650     getAutoCreate : function(){
28651         
28652         var cfg = {
28653             tag: 'div',
28654             html : null
28655         };
28656         
28657         
28658         return  cfg;
28659     },
28660
28661     onRender : function(ct,position){
28662         
28663         
28664         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28665         
28666         if (typeof(Raphael) == 'undefined') {
28667             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28668             return;
28669         }
28670         
28671         this.raphael = Raphael(this.el.dom);
28672         
28673                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28674                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28675                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28676                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28677                 /*
28678                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28679                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28680                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28681                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28682                 
28683                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28684                 r.barchart(330, 10, 300, 220, data1);
28685                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28686                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28687                 */
28688                 
28689                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28690                 // r.barchart(30, 30, 560, 250,  xdata, {
28691                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28692                 //     axis : "0 0 1 1",
28693                 //     axisxlabels :  xdata
28694                 //     //yvalues : cols,
28695                    
28696                 // });
28697 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28698 //        
28699 //        this.load(null,xdata,{
28700 //                axis : "0 0 1 1",
28701 //                axisxlabels :  xdata
28702 //                });
28703
28704     },
28705
28706     load : function(graphtype,xdata,opts)
28707     {
28708         this.raphael.clear();
28709         if(!graphtype) {
28710             graphtype = this.graphtype;
28711         }
28712         if(!opts){
28713             opts = this.opts;
28714         }
28715         var r = this.raphael,
28716             fin = function () {
28717                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28718             },
28719             fout = function () {
28720                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28721             },
28722             pfin = function() {
28723                 this.sector.stop();
28724                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28725
28726                 if (this.label) {
28727                     this.label[0].stop();
28728                     this.label[0].attr({ r: 7.5 });
28729                     this.label[1].attr({ "font-weight": 800 });
28730                 }
28731             },
28732             pfout = function() {
28733                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28734
28735                 if (this.label) {
28736                     this.label[0].animate({ r: 5 }, 500, "bounce");
28737                     this.label[1].attr({ "font-weight": 400 });
28738                 }
28739             };
28740
28741         switch(graphtype){
28742             case 'bar':
28743                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28744                 break;
28745             case 'hbar':
28746                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28747                 break;
28748             case 'pie':
28749 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28750 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28751 //            
28752                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28753                 
28754                 break;
28755
28756         }
28757         
28758         if(this.title){
28759             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28760         }
28761         
28762     },
28763     
28764     setTitle: function(o)
28765     {
28766         this.title = o;
28767     },
28768     
28769     initEvents: function() {
28770         
28771         if(!this.href){
28772             this.el.on('click', this.onClick, this);
28773         }
28774     },
28775     
28776     onClick : function(e)
28777     {
28778         Roo.log('img onclick');
28779         this.fireEvent('click', this, e);
28780     }
28781    
28782 });
28783
28784  
28785 /*
28786  * - LGPL
28787  *
28788  * numberBox
28789  * 
28790  */
28791 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28792
28793 /**
28794  * @class Roo.bootstrap.dash.NumberBox
28795  * @extends Roo.bootstrap.Component
28796  * Bootstrap NumberBox class
28797  * @cfg {String} headline Box headline
28798  * @cfg {String} content Box content
28799  * @cfg {String} icon Box icon
28800  * @cfg {String} footer Footer text
28801  * @cfg {String} fhref Footer href
28802  * 
28803  * @constructor
28804  * Create a new NumberBox
28805  * @param {Object} config The config object
28806  */
28807
28808
28809 Roo.bootstrap.dash.NumberBox = function(config){
28810     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28811     
28812 };
28813
28814 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28815     
28816     headline : '',
28817     content : '',
28818     icon : '',
28819     footer : '',
28820     fhref : '',
28821     ficon : '',
28822     
28823     getAutoCreate : function(){
28824         
28825         var cfg = {
28826             tag : 'div',
28827             cls : 'small-box ',
28828             cn : [
28829                 {
28830                     tag : 'div',
28831                     cls : 'inner',
28832                     cn :[
28833                         {
28834                             tag : 'h3',
28835                             cls : 'roo-headline',
28836                             html : this.headline
28837                         },
28838                         {
28839                             tag : 'p',
28840                             cls : 'roo-content',
28841                             html : this.content
28842                         }
28843                     ]
28844                 }
28845             ]
28846         };
28847         
28848         if(this.icon){
28849             cfg.cn.push({
28850                 tag : 'div',
28851                 cls : 'icon',
28852                 cn :[
28853                     {
28854                         tag : 'i',
28855                         cls : 'ion ' + this.icon
28856                     }
28857                 ]
28858             });
28859         }
28860         
28861         if(this.footer){
28862             var footer = {
28863                 tag : 'a',
28864                 cls : 'small-box-footer',
28865                 href : this.fhref || '#',
28866                 html : this.footer
28867             };
28868             
28869             cfg.cn.push(footer);
28870             
28871         }
28872         
28873         return  cfg;
28874     },
28875
28876     onRender : function(ct,position){
28877         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28878
28879
28880        
28881                 
28882     },
28883
28884     setHeadline: function (value)
28885     {
28886         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28887     },
28888     
28889     setFooter: function (value, href)
28890     {
28891         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28892         
28893         if(href){
28894             this.el.select('a.small-box-footer',true).first().attr('href', href);
28895         }
28896         
28897     },
28898
28899     setContent: function (value)
28900     {
28901         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28902     },
28903
28904     initEvents: function() 
28905     {   
28906         
28907     }
28908     
28909 });
28910
28911  
28912 /*
28913  * - LGPL
28914  *
28915  * TabBox
28916  * 
28917  */
28918 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28919
28920 /**
28921  * @class Roo.bootstrap.dash.TabBox
28922  * @extends Roo.bootstrap.Component
28923  * Bootstrap TabBox class
28924  * @cfg {String} title Title of the TabBox
28925  * @cfg {String} icon Icon of the TabBox
28926  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28927  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28928  * 
28929  * @constructor
28930  * Create a new TabBox
28931  * @param {Object} config The config object
28932  */
28933
28934
28935 Roo.bootstrap.dash.TabBox = function(config){
28936     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28937     this.addEvents({
28938         // raw events
28939         /**
28940          * @event addpane
28941          * When a pane is added
28942          * @param {Roo.bootstrap.dash.TabPane} pane
28943          */
28944         "addpane" : true,
28945         /**
28946          * @event activatepane
28947          * When a pane is activated
28948          * @param {Roo.bootstrap.dash.TabPane} pane
28949          */
28950         "activatepane" : true
28951         
28952          
28953     });
28954     
28955     this.panes = [];
28956 };
28957
28958 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28959
28960     title : '',
28961     icon : false,
28962     showtabs : true,
28963     tabScrollable : false,
28964     
28965     getChildContainer : function()
28966     {
28967         return this.el.select('.tab-content', true).first();
28968     },
28969     
28970     getAutoCreate : function(){
28971         
28972         var header = {
28973             tag: 'li',
28974             cls: 'pull-left header',
28975             html: this.title,
28976             cn : []
28977         };
28978         
28979         if(this.icon){
28980             header.cn.push({
28981                 tag: 'i',
28982                 cls: 'fa ' + this.icon
28983             });
28984         }
28985         
28986         var h = {
28987             tag: 'ul',
28988             cls: 'nav nav-tabs pull-right',
28989             cn: [
28990                 header
28991             ]
28992         };
28993         
28994         if(this.tabScrollable){
28995             h = {
28996                 tag: 'div',
28997                 cls: 'tab-header',
28998                 cn: [
28999                     {
29000                         tag: 'ul',
29001                         cls: 'nav nav-tabs pull-right',
29002                         cn: [
29003                             header
29004                         ]
29005                     }
29006                 ]
29007             };
29008         }
29009         
29010         var cfg = {
29011             tag: 'div',
29012             cls: 'nav-tabs-custom',
29013             cn: [
29014                 h,
29015                 {
29016                     tag: 'div',
29017                     cls: 'tab-content no-padding',
29018                     cn: []
29019                 }
29020             ]
29021         };
29022
29023         return  cfg;
29024     },
29025     initEvents : function()
29026     {
29027         //Roo.log('add add pane handler');
29028         this.on('addpane', this.onAddPane, this);
29029     },
29030      /**
29031      * Updates the box title
29032      * @param {String} html to set the title to.
29033      */
29034     setTitle : function(value)
29035     {
29036         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29037     },
29038     onAddPane : function(pane)
29039     {
29040         this.panes.push(pane);
29041         //Roo.log('addpane');
29042         //Roo.log(pane);
29043         // tabs are rendere left to right..
29044         if(!this.showtabs){
29045             return;
29046         }
29047         
29048         var ctr = this.el.select('.nav-tabs', true).first();
29049          
29050          
29051         var existing = ctr.select('.nav-tab',true);
29052         var qty = existing.getCount();;
29053         
29054         
29055         var tab = ctr.createChild({
29056             tag : 'li',
29057             cls : 'nav-tab' + (qty ? '' : ' active'),
29058             cn : [
29059                 {
29060                     tag : 'a',
29061                     href:'#',
29062                     html : pane.title
29063                 }
29064             ]
29065         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29066         pane.tab = tab;
29067         
29068         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29069         if (!qty) {
29070             pane.el.addClass('active');
29071         }
29072         
29073                 
29074     },
29075     onTabClick : function(ev,un,ob,pane)
29076     {
29077         //Roo.log('tab - prev default');
29078         ev.preventDefault();
29079         
29080         
29081         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29082         pane.tab.addClass('active');
29083         //Roo.log(pane.title);
29084         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29085         // technically we should have a deactivate event.. but maybe add later.
29086         // and it should not de-activate the selected tab...
29087         this.fireEvent('activatepane', pane);
29088         pane.el.addClass('active');
29089         pane.fireEvent('activate');
29090         
29091         
29092     },
29093     
29094     getActivePane : function()
29095     {
29096         var r = false;
29097         Roo.each(this.panes, function(p) {
29098             if(p.el.hasClass('active')){
29099                 r = p;
29100                 return false;
29101             }
29102             
29103             return;
29104         });
29105         
29106         return r;
29107     }
29108     
29109     
29110 });
29111
29112  
29113 /*
29114  * - LGPL
29115  *
29116  * Tab pane
29117  * 
29118  */
29119 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29120 /**
29121  * @class Roo.bootstrap.TabPane
29122  * @extends Roo.bootstrap.Component
29123  * Bootstrap TabPane class
29124  * @cfg {Boolean} active (false | true) Default false
29125  * @cfg {String} title title of panel
29126
29127  * 
29128  * @constructor
29129  * Create a new TabPane
29130  * @param {Object} config The config object
29131  */
29132
29133 Roo.bootstrap.dash.TabPane = function(config){
29134     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29135     
29136     this.addEvents({
29137         // raw events
29138         /**
29139          * @event activate
29140          * When a pane is activated
29141          * @param {Roo.bootstrap.dash.TabPane} pane
29142          */
29143         "activate" : true
29144          
29145     });
29146 };
29147
29148 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29149     
29150     active : false,
29151     title : '',
29152     
29153     // the tabBox that this is attached to.
29154     tab : false,
29155      
29156     getAutoCreate : function() 
29157     {
29158         var cfg = {
29159             tag: 'div',
29160             cls: 'tab-pane'
29161         };
29162         
29163         if(this.active){
29164             cfg.cls += ' active';
29165         }
29166         
29167         return cfg;
29168     },
29169     initEvents  : function()
29170     {
29171         //Roo.log('trigger add pane handler');
29172         this.parent().fireEvent('addpane', this)
29173     },
29174     
29175      /**
29176      * Updates the tab title 
29177      * @param {String} html to set the title to.
29178      */
29179     setTitle: function(str)
29180     {
29181         if (!this.tab) {
29182             return;
29183         }
29184         this.title = str;
29185         this.tab.select('a', true).first().dom.innerHTML = str;
29186         
29187     }
29188     
29189     
29190     
29191 });
29192
29193  
29194
29195
29196  /*
29197  * - LGPL
29198  *
29199  * menu
29200  * 
29201  */
29202 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29203
29204 /**
29205  * @class Roo.bootstrap.menu.Menu
29206  * @extends Roo.bootstrap.Component
29207  * Bootstrap Menu class - container for Menu
29208  * @cfg {String} html Text of the menu
29209  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29210  * @cfg {String} icon Font awesome icon
29211  * @cfg {String} pos Menu align to (top | bottom) default bottom
29212  * 
29213  * 
29214  * @constructor
29215  * Create a new Menu
29216  * @param {Object} config The config object
29217  */
29218
29219
29220 Roo.bootstrap.menu.Menu = function(config){
29221     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29222     
29223     this.addEvents({
29224         /**
29225          * @event beforeshow
29226          * Fires before this menu is displayed
29227          * @param {Roo.bootstrap.menu.Menu} this
29228          */
29229         beforeshow : true,
29230         /**
29231          * @event beforehide
29232          * Fires before this menu is hidden
29233          * @param {Roo.bootstrap.menu.Menu} this
29234          */
29235         beforehide : true,
29236         /**
29237          * @event show
29238          * Fires after this menu is displayed
29239          * @param {Roo.bootstrap.menu.Menu} this
29240          */
29241         show : true,
29242         /**
29243          * @event hide
29244          * Fires after this menu is hidden
29245          * @param {Roo.bootstrap.menu.Menu} this
29246          */
29247         hide : true,
29248         /**
29249          * @event click
29250          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29251          * @param {Roo.bootstrap.menu.Menu} this
29252          * @param {Roo.EventObject} e
29253          */
29254         click : true
29255     });
29256     
29257 };
29258
29259 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29260     
29261     submenu : false,
29262     html : '',
29263     weight : 'default',
29264     icon : false,
29265     pos : 'bottom',
29266     
29267     
29268     getChildContainer : function() {
29269         if(this.isSubMenu){
29270             return this.el;
29271         }
29272         
29273         return this.el.select('ul.dropdown-menu', true).first();  
29274     },
29275     
29276     getAutoCreate : function()
29277     {
29278         var text = [
29279             {
29280                 tag : 'span',
29281                 cls : 'roo-menu-text',
29282                 html : this.html
29283             }
29284         ];
29285         
29286         if(this.icon){
29287             text.unshift({
29288                 tag : 'i',
29289                 cls : 'fa ' + this.icon
29290             })
29291         }
29292         
29293         
29294         var cfg = {
29295             tag : 'div',
29296             cls : 'btn-group',
29297             cn : [
29298                 {
29299                     tag : 'button',
29300                     cls : 'dropdown-button btn btn-' + this.weight,
29301                     cn : text
29302                 },
29303                 {
29304                     tag : 'button',
29305                     cls : 'dropdown-toggle btn btn-' + this.weight,
29306                     cn : [
29307                         {
29308                             tag : 'span',
29309                             cls : 'caret'
29310                         }
29311                     ]
29312                 },
29313                 {
29314                     tag : 'ul',
29315                     cls : 'dropdown-menu'
29316                 }
29317             ]
29318             
29319         };
29320         
29321         if(this.pos == 'top'){
29322             cfg.cls += ' dropup';
29323         }
29324         
29325         if(this.isSubMenu){
29326             cfg = {
29327                 tag : 'ul',
29328                 cls : 'dropdown-menu'
29329             }
29330         }
29331         
29332         return cfg;
29333     },
29334     
29335     onRender : function(ct, position)
29336     {
29337         this.isSubMenu = ct.hasClass('dropdown-submenu');
29338         
29339         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29340     },
29341     
29342     initEvents : function() 
29343     {
29344         if(this.isSubMenu){
29345             return;
29346         }
29347         
29348         this.hidden = true;
29349         
29350         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29351         this.triggerEl.on('click', this.onTriggerPress, this);
29352         
29353         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29354         this.buttonEl.on('click', this.onClick, this);
29355         
29356     },
29357     
29358     list : function()
29359     {
29360         if(this.isSubMenu){
29361             return this.el;
29362         }
29363         
29364         return this.el.select('ul.dropdown-menu', true).first();
29365     },
29366     
29367     onClick : function(e)
29368     {
29369         this.fireEvent("click", this, e);
29370     },
29371     
29372     onTriggerPress  : function(e)
29373     {   
29374         if (this.isVisible()) {
29375             this.hide();
29376         } else {
29377             this.show();
29378         }
29379     },
29380     
29381     isVisible : function(){
29382         return !this.hidden;
29383     },
29384     
29385     show : function()
29386     {
29387         this.fireEvent("beforeshow", this);
29388         
29389         this.hidden = false;
29390         this.el.addClass('open');
29391         
29392         Roo.get(document).on("mouseup", this.onMouseUp, this);
29393         
29394         this.fireEvent("show", this);
29395         
29396         
29397     },
29398     
29399     hide : function()
29400     {
29401         this.fireEvent("beforehide", this);
29402         
29403         this.hidden = true;
29404         this.el.removeClass('open');
29405         
29406         Roo.get(document).un("mouseup", this.onMouseUp);
29407         
29408         this.fireEvent("hide", this);
29409     },
29410     
29411     onMouseUp : function()
29412     {
29413         this.hide();
29414     }
29415     
29416 });
29417
29418  
29419  /*
29420  * - LGPL
29421  *
29422  * menu item
29423  * 
29424  */
29425 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29426
29427 /**
29428  * @class Roo.bootstrap.menu.Item
29429  * @extends Roo.bootstrap.Component
29430  * Bootstrap MenuItem class
29431  * @cfg {Boolean} submenu (true | false) default false
29432  * @cfg {String} html text of the item
29433  * @cfg {String} href the link
29434  * @cfg {Boolean} disable (true | false) default false
29435  * @cfg {Boolean} preventDefault (true | false) default true
29436  * @cfg {String} icon Font awesome icon
29437  * @cfg {String} pos Submenu align to (left | right) default right 
29438  * 
29439  * 
29440  * @constructor
29441  * Create a new Item
29442  * @param {Object} config The config object
29443  */
29444
29445
29446 Roo.bootstrap.menu.Item = function(config){
29447     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29448     this.addEvents({
29449         /**
29450          * @event mouseover
29451          * Fires when the mouse is hovering over this menu
29452          * @param {Roo.bootstrap.menu.Item} this
29453          * @param {Roo.EventObject} e
29454          */
29455         mouseover : true,
29456         /**
29457          * @event mouseout
29458          * Fires when the mouse exits this menu
29459          * @param {Roo.bootstrap.menu.Item} this
29460          * @param {Roo.EventObject} e
29461          */
29462         mouseout : true,
29463         // raw events
29464         /**
29465          * @event click
29466          * The raw click event for the entire grid.
29467          * @param {Roo.EventObject} e
29468          */
29469         click : true
29470     });
29471 };
29472
29473 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29474     
29475     submenu : false,
29476     href : '',
29477     html : '',
29478     preventDefault: true,
29479     disable : false,
29480     icon : false,
29481     pos : 'right',
29482     
29483     getAutoCreate : function()
29484     {
29485         var text = [
29486             {
29487                 tag : 'span',
29488                 cls : 'roo-menu-item-text',
29489                 html : this.html
29490             }
29491         ];
29492         
29493         if(this.icon){
29494             text.unshift({
29495                 tag : 'i',
29496                 cls : 'fa ' + this.icon
29497             })
29498         }
29499         
29500         var cfg = {
29501             tag : 'li',
29502             cn : [
29503                 {
29504                     tag : 'a',
29505                     href : this.href || '#',
29506                     cn : text
29507                 }
29508             ]
29509         };
29510         
29511         if(this.disable){
29512             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29513         }
29514         
29515         if(this.submenu){
29516             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29517             
29518             if(this.pos == 'left'){
29519                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29520             }
29521         }
29522         
29523         return cfg;
29524     },
29525     
29526     initEvents : function() 
29527     {
29528         this.el.on('mouseover', this.onMouseOver, this);
29529         this.el.on('mouseout', this.onMouseOut, this);
29530         
29531         this.el.select('a', true).first().on('click', this.onClick, this);
29532         
29533     },
29534     
29535     onClick : function(e)
29536     {
29537         if(this.preventDefault){
29538             e.preventDefault();
29539         }
29540         
29541         this.fireEvent("click", this, e);
29542     },
29543     
29544     onMouseOver : function(e)
29545     {
29546         if(this.submenu && this.pos == 'left'){
29547             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29548         }
29549         
29550         this.fireEvent("mouseover", this, e);
29551     },
29552     
29553     onMouseOut : function(e)
29554     {
29555         this.fireEvent("mouseout", this, e);
29556     }
29557 });
29558
29559  
29560
29561  /*
29562  * - LGPL
29563  *
29564  * menu separator
29565  * 
29566  */
29567 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29568
29569 /**
29570  * @class Roo.bootstrap.menu.Separator
29571  * @extends Roo.bootstrap.Component
29572  * Bootstrap Separator class
29573  * 
29574  * @constructor
29575  * Create a new Separator
29576  * @param {Object} config The config object
29577  */
29578
29579
29580 Roo.bootstrap.menu.Separator = function(config){
29581     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29582 };
29583
29584 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29585     
29586     getAutoCreate : function(){
29587         var cfg = {
29588             tag : 'li',
29589             cls: 'dropdown-divider divider'
29590         };
29591         
29592         return cfg;
29593     }
29594    
29595 });
29596
29597  
29598
29599  /*
29600  * - LGPL
29601  *
29602  * Tooltip
29603  * 
29604  */
29605
29606 /**
29607  * @class Roo.bootstrap.Tooltip
29608  * Bootstrap Tooltip class
29609  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29610  * to determine which dom element triggers the tooltip.
29611  * 
29612  * It needs to add support for additional attributes like tooltip-position
29613  * 
29614  * @constructor
29615  * Create a new Toolti
29616  * @param {Object} config The config object
29617  */
29618
29619 Roo.bootstrap.Tooltip = function(config){
29620     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29621     
29622     this.alignment = Roo.bootstrap.Tooltip.alignment;
29623     
29624     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29625         this.alignment = config.alignment;
29626     }
29627     
29628 };
29629
29630 Roo.apply(Roo.bootstrap.Tooltip, {
29631     /**
29632      * @function init initialize tooltip monitoring.
29633      * @static
29634      */
29635     currentEl : false,
29636     currentTip : false,
29637     currentRegion : false,
29638     
29639     //  init : delay?
29640     
29641     init : function()
29642     {
29643         Roo.get(document).on('mouseover', this.enter ,this);
29644         Roo.get(document).on('mouseout', this.leave, this);
29645          
29646         
29647         this.currentTip = new Roo.bootstrap.Tooltip();
29648     },
29649     
29650     enter : function(ev)
29651     {
29652         var dom = ev.getTarget();
29653         
29654         //Roo.log(['enter',dom]);
29655         var el = Roo.fly(dom);
29656         if (this.currentEl) {
29657             //Roo.log(dom);
29658             //Roo.log(this.currentEl);
29659             //Roo.log(this.currentEl.contains(dom));
29660             if (this.currentEl == el) {
29661                 return;
29662             }
29663             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29664                 return;
29665             }
29666
29667         }
29668         
29669         if (this.currentTip.el) {
29670             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29671         }    
29672         //Roo.log(ev);
29673         
29674         if(!el || el.dom == document){
29675             return;
29676         }
29677         
29678         var bindEl = el; 
29679         var pel = false;
29680         if (!el.attr('tooltip')) {
29681             pel = el.findParent("[tooltip]");
29682             if (pel) {
29683                 bindEl = Roo.get(pel);
29684             }
29685         }
29686         
29687        
29688         
29689         // you can not look for children, as if el is the body.. then everythign is the child..
29690         if (!pel && !el.attr('tooltip')) { //
29691             if (!el.select("[tooltip]").elements.length) {
29692                 return;
29693             }
29694             // is the mouse over this child...?
29695             bindEl = el.select("[tooltip]").first();
29696             var xy = ev.getXY();
29697             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29698                 //Roo.log("not in region.");
29699                 return;
29700             }
29701             //Roo.log("child element over..");
29702             
29703         }
29704         this.currentEl = el;
29705         this.currentTip.bind(bindEl);
29706         this.currentRegion = Roo.lib.Region.getRegion(dom);
29707         this.currentTip.enter();
29708         
29709     },
29710     leave : function(ev)
29711     {
29712         var dom = ev.getTarget();
29713         //Roo.log(['leave',dom]);
29714         if (!this.currentEl) {
29715             return;
29716         }
29717         
29718         
29719         if (dom != this.currentEl.dom) {
29720             return;
29721         }
29722         var xy = ev.getXY();
29723         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29724             return;
29725         }
29726         // only activate leave if mouse cursor is outside... bounding box..
29727         
29728         
29729         
29730         
29731         if (this.currentTip) {
29732             this.currentTip.leave();
29733         }
29734         //Roo.log('clear currentEl');
29735         this.currentEl = false;
29736         
29737         
29738     },
29739     alignment : {
29740         'left' : ['r-l', [-2,0], 'right'],
29741         'right' : ['l-r', [2,0], 'left'],
29742         'bottom' : ['t-b', [0,2], 'top'],
29743         'top' : [ 'b-t', [0,-2], 'bottom']
29744     }
29745     
29746 });
29747
29748
29749 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29750     
29751     
29752     bindEl : false,
29753     
29754     delay : null, // can be { show : 300 , hide: 500}
29755     
29756     timeout : null,
29757     
29758     hoverState : null, //???
29759     
29760     placement : 'bottom', 
29761     
29762     alignment : false,
29763     
29764     getAutoCreate : function(){
29765     
29766         var cfg = {
29767            cls : 'tooltip',   
29768            role : 'tooltip',
29769            cn : [
29770                 {
29771                     cls : 'tooltip-arrow arrow'
29772                 },
29773                 {
29774                     cls : 'tooltip-inner'
29775                 }
29776            ]
29777         };
29778         
29779         return cfg;
29780     },
29781     bind : function(el)
29782     {
29783         this.bindEl = el;
29784     },
29785     
29786     initEvents : function()
29787     {
29788         this.arrowEl = this.el.select('.arrow', true).first();
29789         this.innerEl = this.el.select('.tooltip-inner', true).first();
29790     },
29791     
29792     enter : function () {
29793        
29794         if (this.timeout != null) {
29795             clearTimeout(this.timeout);
29796         }
29797         
29798         this.hoverState = 'in';
29799          //Roo.log("enter - show");
29800         if (!this.delay || !this.delay.show) {
29801             this.show();
29802             return;
29803         }
29804         var _t = this;
29805         this.timeout = setTimeout(function () {
29806             if (_t.hoverState == 'in') {
29807                 _t.show();
29808             }
29809         }, this.delay.show);
29810     },
29811     leave : function()
29812     {
29813         clearTimeout(this.timeout);
29814     
29815         this.hoverState = 'out';
29816          if (!this.delay || !this.delay.hide) {
29817             this.hide();
29818             return;
29819         }
29820        
29821         var _t = this;
29822         this.timeout = setTimeout(function () {
29823             //Roo.log("leave - timeout");
29824             
29825             if (_t.hoverState == 'out') {
29826                 _t.hide();
29827                 Roo.bootstrap.Tooltip.currentEl = false;
29828             }
29829         }, delay);
29830     },
29831     
29832     show : function (msg)
29833     {
29834         if (!this.el) {
29835             this.render(document.body);
29836         }
29837         // set content.
29838         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29839         
29840         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29841         
29842         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29843         
29844         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29845                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29846         
29847         var placement = typeof this.placement == 'function' ?
29848             this.placement.call(this, this.el, on_el) :
29849             this.placement;
29850             
29851         var autoToken = /\s?auto?\s?/i;
29852         var autoPlace = autoToken.test(placement);
29853         if (autoPlace) {
29854             placement = placement.replace(autoToken, '') || 'top';
29855         }
29856         
29857         //this.el.detach()
29858         //this.el.setXY([0,0]);
29859         this.el.show();
29860         //this.el.dom.style.display='block';
29861         
29862         //this.el.appendTo(on_el);
29863         
29864         var p = this.getPosition();
29865         var box = this.el.getBox();
29866         
29867         if (autoPlace) {
29868             // fixme..
29869         }
29870         
29871         var align = this.alignment[placement];
29872         
29873         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29874         
29875         if(placement == 'top' || placement == 'bottom'){
29876             if(xy[0] < 0){
29877                 placement = 'right';
29878             }
29879             
29880             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29881                 placement = 'left';
29882             }
29883             
29884             var scroll = Roo.select('body', true).first().getScroll();
29885             
29886             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29887                 placement = 'top';
29888             }
29889             
29890             align = this.alignment[placement];
29891             
29892             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29893             
29894         }
29895         
29896         var elems = document.getElementsByTagName('div');
29897         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29898         for (var i = 0; i < elems.length; i++) {
29899           var zindex = Number.parseInt(
29900                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29901                 10
29902           );
29903           if (zindex > highest) {
29904             highest = zindex;
29905           }
29906         }
29907         
29908         
29909         
29910         this.el.dom.style.zIndex = highest;
29911         
29912         this.el.alignTo(this.bindEl, align[0],align[1]);
29913         //var arrow = this.el.select('.arrow',true).first();
29914         //arrow.set(align[2], 
29915         
29916         this.el.addClass(placement);
29917         this.el.addClass("bs-tooltip-"+ placement);
29918         
29919         this.el.addClass('in fade show');
29920         
29921         this.hoverState = null;
29922         
29923         if (this.el.hasClass('fade')) {
29924             // fade it?
29925         }
29926         
29927         
29928         
29929         
29930         
29931     },
29932     hide : function()
29933     {
29934          
29935         if (!this.el) {
29936             return;
29937         }
29938         //this.el.setXY([0,0]);
29939         this.el.removeClass(['show', 'in']);
29940         //this.el.hide();
29941         
29942     }
29943     
29944 });
29945  
29946
29947  /*
29948  * - LGPL
29949  *
29950  * Location Picker
29951  * 
29952  */
29953
29954 /**
29955  * @class Roo.bootstrap.LocationPicker
29956  * @extends Roo.bootstrap.Component
29957  * Bootstrap LocationPicker class
29958  * @cfg {Number} latitude Position when init default 0
29959  * @cfg {Number} longitude Position when init default 0
29960  * @cfg {Number} zoom default 15
29961  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29962  * @cfg {Boolean} mapTypeControl default false
29963  * @cfg {Boolean} disableDoubleClickZoom default false
29964  * @cfg {Boolean} scrollwheel default true
29965  * @cfg {Boolean} streetViewControl default false
29966  * @cfg {Number} radius default 0
29967  * @cfg {String} locationName
29968  * @cfg {Boolean} draggable default true
29969  * @cfg {Boolean} enableAutocomplete default false
29970  * @cfg {Boolean} enableReverseGeocode default true
29971  * @cfg {String} markerTitle
29972  * 
29973  * @constructor
29974  * Create a new LocationPicker
29975  * @param {Object} config The config object
29976  */
29977
29978
29979 Roo.bootstrap.LocationPicker = function(config){
29980     
29981     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29982     
29983     this.addEvents({
29984         /**
29985          * @event initial
29986          * Fires when the picker initialized.
29987          * @param {Roo.bootstrap.LocationPicker} this
29988          * @param {Google Location} location
29989          */
29990         initial : true,
29991         /**
29992          * @event positionchanged
29993          * Fires when the picker position changed.
29994          * @param {Roo.bootstrap.LocationPicker} this
29995          * @param {Google Location} location
29996          */
29997         positionchanged : true,
29998         /**
29999          * @event resize
30000          * Fires when the map resize.
30001          * @param {Roo.bootstrap.LocationPicker} this
30002          */
30003         resize : true,
30004         /**
30005          * @event show
30006          * Fires when the map show.
30007          * @param {Roo.bootstrap.LocationPicker} this
30008          */
30009         show : true,
30010         /**
30011          * @event hide
30012          * Fires when the map hide.
30013          * @param {Roo.bootstrap.LocationPicker} this
30014          */
30015         hide : true,
30016         /**
30017          * @event mapClick
30018          * Fires when click the map.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Map event} e
30021          */
30022         mapClick : true,
30023         /**
30024          * @event mapRightClick
30025          * Fires when right click the map.
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Map event} e
30028          */
30029         mapRightClick : true,
30030         /**
30031          * @event markerClick
30032          * Fires when click the marker.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          * @param {Map event} e
30035          */
30036         markerClick : true,
30037         /**
30038          * @event markerRightClick
30039          * Fires when right click the marker.
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          * @param {Map event} e
30042          */
30043         markerRightClick : true,
30044         /**
30045          * @event OverlayViewDraw
30046          * Fires when OverlayView Draw
30047          * @param {Roo.bootstrap.LocationPicker} this
30048          */
30049         OverlayViewDraw : true,
30050         /**
30051          * @event OverlayViewOnAdd
30052          * Fires when OverlayView Draw
30053          * @param {Roo.bootstrap.LocationPicker} this
30054          */
30055         OverlayViewOnAdd : true,
30056         /**
30057          * @event OverlayViewOnRemove
30058          * Fires when OverlayView Draw
30059          * @param {Roo.bootstrap.LocationPicker} this
30060          */
30061         OverlayViewOnRemove : true,
30062         /**
30063          * @event OverlayViewShow
30064          * Fires when OverlayView Draw
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          * @param {Pixel} cpx
30067          */
30068         OverlayViewShow : true,
30069         /**
30070          * @event OverlayViewHide
30071          * Fires when OverlayView Draw
30072          * @param {Roo.bootstrap.LocationPicker} this
30073          */
30074         OverlayViewHide : true,
30075         /**
30076          * @event loadexception
30077          * Fires when load google lib failed.
30078          * @param {Roo.bootstrap.LocationPicker} this
30079          */
30080         loadexception : true
30081     });
30082         
30083 };
30084
30085 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30086     
30087     gMapContext: false,
30088     
30089     latitude: 0,
30090     longitude: 0,
30091     zoom: 15,
30092     mapTypeId: false,
30093     mapTypeControl: false,
30094     disableDoubleClickZoom: false,
30095     scrollwheel: true,
30096     streetViewControl: false,
30097     radius: 0,
30098     locationName: '',
30099     draggable: true,
30100     enableAutocomplete: false,
30101     enableReverseGeocode: true,
30102     markerTitle: '',
30103     
30104     getAutoCreate: function()
30105     {
30106
30107         var cfg = {
30108             tag: 'div',
30109             cls: 'roo-location-picker'
30110         };
30111         
30112         return cfg
30113     },
30114     
30115     initEvents: function(ct, position)
30116     {       
30117         if(!this.el.getWidth() || this.isApplied()){
30118             return;
30119         }
30120         
30121         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30122         
30123         this.initial();
30124     },
30125     
30126     initial: function()
30127     {
30128         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30129             this.fireEvent('loadexception', this);
30130             return;
30131         }
30132         
30133         if(!this.mapTypeId){
30134             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30135         }
30136         
30137         this.gMapContext = this.GMapContext();
30138         
30139         this.initOverlayView();
30140         
30141         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30142         
30143         var _this = this;
30144                 
30145         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30146             _this.setPosition(_this.gMapContext.marker.position);
30147         });
30148         
30149         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30150             _this.fireEvent('mapClick', this, event);
30151             
30152         });
30153
30154         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30155             _this.fireEvent('mapRightClick', this, event);
30156             
30157         });
30158         
30159         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30160             _this.fireEvent('markerClick', this, event);
30161             
30162         });
30163
30164         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30165             _this.fireEvent('markerRightClick', this, event);
30166             
30167         });
30168         
30169         this.setPosition(this.gMapContext.location);
30170         
30171         this.fireEvent('initial', this, this.gMapContext.location);
30172     },
30173     
30174     initOverlayView: function()
30175     {
30176         var _this = this;
30177         
30178         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30179             
30180             draw: function()
30181             {
30182                 _this.fireEvent('OverlayViewDraw', _this);
30183             },
30184             
30185             onAdd: function()
30186             {
30187                 _this.fireEvent('OverlayViewOnAdd', _this);
30188             },
30189             
30190             onRemove: function()
30191             {
30192                 _this.fireEvent('OverlayViewOnRemove', _this);
30193             },
30194             
30195             show: function(cpx)
30196             {
30197                 _this.fireEvent('OverlayViewShow', _this, cpx);
30198             },
30199             
30200             hide: function()
30201             {
30202                 _this.fireEvent('OverlayViewHide', _this);
30203             }
30204             
30205         });
30206     },
30207     
30208     fromLatLngToContainerPixel: function(event)
30209     {
30210         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30211     },
30212     
30213     isApplied: function() 
30214     {
30215         return this.getGmapContext() == false ? false : true;
30216     },
30217     
30218     getGmapContext: function() 
30219     {
30220         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30221     },
30222     
30223     GMapContext: function() 
30224     {
30225         var position = new google.maps.LatLng(this.latitude, this.longitude);
30226         
30227         var _map = new google.maps.Map(this.el.dom, {
30228             center: position,
30229             zoom: this.zoom,
30230             mapTypeId: this.mapTypeId,
30231             mapTypeControl: this.mapTypeControl,
30232             disableDoubleClickZoom: this.disableDoubleClickZoom,
30233             scrollwheel: this.scrollwheel,
30234             streetViewControl: this.streetViewControl,
30235             locationName: this.locationName,
30236             draggable: this.draggable,
30237             enableAutocomplete: this.enableAutocomplete,
30238             enableReverseGeocode: this.enableReverseGeocode
30239         });
30240         
30241         var _marker = new google.maps.Marker({
30242             position: position,
30243             map: _map,
30244             title: this.markerTitle,
30245             draggable: this.draggable
30246         });
30247         
30248         return {
30249             map: _map,
30250             marker: _marker,
30251             circle: null,
30252             location: position,
30253             radius: this.radius,
30254             locationName: this.locationName,
30255             addressComponents: {
30256                 formatted_address: null,
30257                 addressLine1: null,
30258                 addressLine2: null,
30259                 streetName: null,
30260                 streetNumber: null,
30261                 city: null,
30262                 district: null,
30263                 state: null,
30264                 stateOrProvince: null
30265             },
30266             settings: this,
30267             domContainer: this.el.dom,
30268             geodecoder: new google.maps.Geocoder()
30269         };
30270     },
30271     
30272     drawCircle: function(center, radius, options) 
30273     {
30274         if (this.gMapContext.circle != null) {
30275             this.gMapContext.circle.setMap(null);
30276         }
30277         if (radius > 0) {
30278             radius *= 1;
30279             options = Roo.apply({}, options, {
30280                 strokeColor: "#0000FF",
30281                 strokeOpacity: .35,
30282                 strokeWeight: 2,
30283                 fillColor: "#0000FF",
30284                 fillOpacity: .2
30285             });
30286             
30287             options.map = this.gMapContext.map;
30288             options.radius = radius;
30289             options.center = center;
30290             this.gMapContext.circle = new google.maps.Circle(options);
30291             return this.gMapContext.circle;
30292         }
30293         
30294         return null;
30295     },
30296     
30297     setPosition: function(location) 
30298     {
30299         this.gMapContext.location = location;
30300         this.gMapContext.marker.setPosition(location);
30301         this.gMapContext.map.panTo(location);
30302         this.drawCircle(location, this.gMapContext.radius, {});
30303         
30304         var _this = this;
30305         
30306         if (this.gMapContext.settings.enableReverseGeocode) {
30307             this.gMapContext.geodecoder.geocode({
30308                 latLng: this.gMapContext.location
30309             }, function(results, status) {
30310                 
30311                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30312                     _this.gMapContext.locationName = results[0].formatted_address;
30313                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30314                     
30315                     _this.fireEvent('positionchanged', this, location);
30316                 }
30317             });
30318             
30319             return;
30320         }
30321         
30322         this.fireEvent('positionchanged', this, location);
30323     },
30324     
30325     resize: function()
30326     {
30327         google.maps.event.trigger(this.gMapContext.map, "resize");
30328         
30329         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30330         
30331         this.fireEvent('resize', this);
30332     },
30333     
30334     setPositionByLatLng: function(latitude, longitude)
30335     {
30336         this.setPosition(new google.maps.LatLng(latitude, longitude));
30337     },
30338     
30339     getCurrentPosition: function() 
30340     {
30341         return {
30342             latitude: this.gMapContext.location.lat(),
30343             longitude: this.gMapContext.location.lng()
30344         };
30345     },
30346     
30347     getAddressName: function() 
30348     {
30349         return this.gMapContext.locationName;
30350     },
30351     
30352     getAddressComponents: function() 
30353     {
30354         return this.gMapContext.addressComponents;
30355     },
30356     
30357     address_component_from_google_geocode: function(address_components) 
30358     {
30359         var result = {};
30360         
30361         for (var i = 0; i < address_components.length; i++) {
30362             var component = address_components[i];
30363             if (component.types.indexOf("postal_code") >= 0) {
30364                 result.postalCode = component.short_name;
30365             } else if (component.types.indexOf("street_number") >= 0) {
30366                 result.streetNumber = component.short_name;
30367             } else if (component.types.indexOf("route") >= 0) {
30368                 result.streetName = component.short_name;
30369             } else if (component.types.indexOf("neighborhood") >= 0) {
30370                 result.city = component.short_name;
30371             } else if (component.types.indexOf("locality") >= 0) {
30372                 result.city = component.short_name;
30373             } else if (component.types.indexOf("sublocality") >= 0) {
30374                 result.district = component.short_name;
30375             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30376                 result.stateOrProvince = component.short_name;
30377             } else if (component.types.indexOf("country") >= 0) {
30378                 result.country = component.short_name;
30379             }
30380         }
30381         
30382         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30383         result.addressLine2 = "";
30384         return result;
30385     },
30386     
30387     setZoomLevel: function(zoom)
30388     {
30389         this.gMapContext.map.setZoom(zoom);
30390     },
30391     
30392     show: function()
30393     {
30394         if(!this.el){
30395             return;
30396         }
30397         
30398         this.el.show();
30399         
30400         this.resize();
30401         
30402         this.fireEvent('show', this);
30403     },
30404     
30405     hide: function()
30406     {
30407         if(!this.el){
30408             return;
30409         }
30410         
30411         this.el.hide();
30412         
30413         this.fireEvent('hide', this);
30414     }
30415     
30416 });
30417
30418 Roo.apply(Roo.bootstrap.LocationPicker, {
30419     
30420     OverlayView : function(map, options)
30421     {
30422         options = options || {};
30423         
30424         this.setMap(map);
30425     }
30426     
30427     
30428 });/**
30429  * @class Roo.bootstrap.Alert
30430  * @extends Roo.bootstrap.Component
30431  * Bootstrap Alert class - shows an alert area box
30432  * eg
30433  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30434   Enter a valid email address
30435 </div>
30436  * @licence LGPL
30437  * @cfg {String} title The title of alert
30438  * @cfg {String} html The content of alert
30439  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30440  * @cfg {String} fa font-awesomeicon
30441  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30442  * @cfg {Boolean} close true to show a x closer
30443  * 
30444  * 
30445  * @constructor
30446  * Create a new alert
30447  * @param {Object} config The config object
30448  */
30449
30450
30451 Roo.bootstrap.Alert = function(config){
30452     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30453     
30454 };
30455
30456 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30457     
30458     title: '',
30459     html: '',
30460     weight: false,
30461     fa: false,
30462     faicon: false, // BC
30463     close : false,
30464     
30465     
30466     getAutoCreate : function()
30467     {
30468         
30469         var cfg = {
30470             tag : 'div',
30471             cls : 'alert',
30472             cn : [
30473                 {
30474                     tag: 'button',
30475                     type :  "button",
30476                     cls: "close",
30477                     html : '×',
30478                     style : this.close ? '' : 'display:none'
30479                 },
30480                 {
30481                     tag : 'i',
30482                     cls : 'roo-alert-icon'
30483                     
30484                 },
30485                 {
30486                     tag : 'b',
30487                     cls : 'roo-alert-title',
30488                     html : this.title
30489                 },
30490                 {
30491                     tag : 'span',
30492                     cls : 'roo-alert-text',
30493                     html : this.html
30494                 }
30495             ]
30496         };
30497         
30498         if(this.faicon){
30499             cfg.cn[0].cls += ' fa ' + this.faicon;
30500         }
30501         if(this.fa){
30502             cfg.cn[0].cls += ' fa ' + this.fa;
30503         }
30504         
30505         if(this.weight){
30506             cfg.cls += ' alert-' + this.weight;
30507         }
30508         
30509         return cfg;
30510     },
30511     
30512     initEvents: function() 
30513     {
30514         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30515         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30516         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30517         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30518         if (this.seconds > 0) {
30519             this.hide.defer(this.seconds, this);
30520         }
30521     },
30522     /**
30523      * Set the Title Message HTML
30524      * @param {String} html
30525      */
30526     setTitle : function(str)
30527     {
30528         this.titleEl.dom.innerHTML = str;
30529     },
30530      
30531      /**
30532      * Set the Body Message HTML
30533      * @param {String} html
30534      */
30535     setHtml : function(str)
30536     {
30537         this.htmlEl.dom.innerHTML = str;
30538     },
30539     /**
30540      * Set the Weight of the alert
30541      * @param {String} (success|info|warning|danger) weight
30542      */
30543     
30544     setWeight : function(weight)
30545     {
30546         if(this.weight){
30547             this.el.removeClass('alert-' + this.weight);
30548         }
30549         
30550         this.weight = weight;
30551         
30552         this.el.addClass('alert-' + this.weight);
30553     },
30554       /**
30555      * Set the Icon of the alert
30556      * @param {String} see fontawsome names (name without the 'fa-' bit)
30557      */
30558     setIcon : function(icon)
30559     {
30560         if(this.faicon){
30561             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30562         }
30563         
30564         this.faicon = icon;
30565         
30566         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30567     },
30568     /**
30569      * Hide the Alert
30570      */
30571     hide: function() 
30572     {
30573         this.el.hide();   
30574     },
30575     /**
30576      * Show the Alert
30577      */
30578     show: function() 
30579     {  
30580         this.el.show();   
30581     }
30582     
30583 });
30584
30585  
30586 /*
30587 * Licence: LGPL
30588 */
30589
30590 /**
30591  * @class Roo.bootstrap.UploadCropbox
30592  * @extends Roo.bootstrap.Component
30593  * Bootstrap UploadCropbox class
30594  * @cfg {String} emptyText show when image has been loaded
30595  * @cfg {String} rotateNotify show when image too small to rotate
30596  * @cfg {Number} errorTimeout default 3000
30597  * @cfg {Number} minWidth default 300
30598  * @cfg {Number} minHeight default 300
30599  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30600  * @cfg {Boolean} isDocument (true|false) default false
30601  * @cfg {String} url action url
30602  * @cfg {String} paramName default 'imageUpload'
30603  * @cfg {String} method default POST
30604  * @cfg {Boolean} loadMask (true|false) default true
30605  * @cfg {Boolean} loadingText default 'Loading...'
30606  * 
30607  * @constructor
30608  * Create a new UploadCropbox
30609  * @param {Object} config The config object
30610  */
30611
30612 Roo.bootstrap.UploadCropbox = function(config){
30613     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30614     
30615     this.addEvents({
30616         /**
30617          * @event beforeselectfile
30618          * Fire before select file
30619          * @param {Roo.bootstrap.UploadCropbox} this
30620          */
30621         "beforeselectfile" : true,
30622         /**
30623          * @event initial
30624          * Fire after initEvent
30625          * @param {Roo.bootstrap.UploadCropbox} this
30626          */
30627         "initial" : true,
30628         /**
30629          * @event crop
30630          * Fire after initEvent
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          * @param {String} data
30633          */
30634         "crop" : true,
30635         /**
30636          * @event prepare
30637          * Fire when preparing the file data
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          * @param {Object} file
30640          */
30641         "prepare" : true,
30642         /**
30643          * @event exception
30644          * Fire when get exception
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          * @param {XMLHttpRequest} xhr
30647          */
30648         "exception" : true,
30649         /**
30650          * @event beforeloadcanvas
30651          * Fire before load the canvas
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          * @param {String} src
30654          */
30655         "beforeloadcanvas" : true,
30656         /**
30657          * @event trash
30658          * Fire when trash image
30659          * @param {Roo.bootstrap.UploadCropbox} this
30660          */
30661         "trash" : true,
30662         /**
30663          * @event download
30664          * Fire when download the image
30665          * @param {Roo.bootstrap.UploadCropbox} this
30666          */
30667         "download" : true,
30668         /**
30669          * @event footerbuttonclick
30670          * Fire when footerbuttonclick
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          * @param {String} type
30673          */
30674         "footerbuttonclick" : true,
30675         /**
30676          * @event resize
30677          * Fire when resize
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          */
30680         "resize" : true,
30681         /**
30682          * @event rotate
30683          * Fire when rotate the image
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {String} pos
30686          */
30687         "rotate" : true,
30688         /**
30689          * @event inspect
30690          * Fire when inspect the file
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          * @param {Object} file
30693          */
30694         "inspect" : true,
30695         /**
30696          * @event upload
30697          * Fire when xhr upload the file
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          * @param {Object} data
30700          */
30701         "upload" : true,
30702         /**
30703          * @event arrange
30704          * Fire when arrange the file data
30705          * @param {Roo.bootstrap.UploadCropbox} this
30706          * @param {Object} formData
30707          */
30708         "arrange" : true
30709     });
30710     
30711     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30712 };
30713
30714 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30715     
30716     emptyText : 'Click to upload image',
30717     rotateNotify : 'Image is too small to rotate',
30718     errorTimeout : 3000,
30719     scale : 0,
30720     baseScale : 1,
30721     rotate : 0,
30722     dragable : false,
30723     pinching : false,
30724     mouseX : 0,
30725     mouseY : 0,
30726     cropData : false,
30727     minWidth : 300,
30728     minHeight : 300,
30729     file : false,
30730     exif : {},
30731     baseRotate : 1,
30732     cropType : 'image/jpeg',
30733     buttons : false,
30734     canvasLoaded : false,
30735     isDocument : false,
30736     method : 'POST',
30737     paramName : 'imageUpload',
30738     loadMask : true,
30739     loadingText : 'Loading...',
30740     maskEl : false,
30741     
30742     getAutoCreate : function()
30743     {
30744         var cfg = {
30745             tag : 'div',
30746             cls : 'roo-upload-cropbox',
30747             cn : [
30748                 {
30749                     tag : 'input',
30750                     cls : 'roo-upload-cropbox-selector',
30751                     type : 'file'
30752                 },
30753                 {
30754                     tag : 'div',
30755                     cls : 'roo-upload-cropbox-body',
30756                     style : 'cursor:pointer',
30757                     cn : [
30758                         {
30759                             tag : 'div',
30760                             cls : 'roo-upload-cropbox-preview'
30761                         },
30762                         {
30763                             tag : 'div',
30764                             cls : 'roo-upload-cropbox-thumb'
30765                         },
30766                         {
30767                             tag : 'div',
30768                             cls : 'roo-upload-cropbox-empty-notify',
30769                             html : this.emptyText
30770                         },
30771                         {
30772                             tag : 'div',
30773                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30774                             html : this.rotateNotify
30775                         }
30776                     ]
30777                 },
30778                 {
30779                     tag : 'div',
30780                     cls : 'roo-upload-cropbox-footer',
30781                     cn : {
30782                         tag : 'div',
30783                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30784                         cn : []
30785                     }
30786                 }
30787             ]
30788         };
30789         
30790         return cfg;
30791     },
30792     
30793     onRender : function(ct, position)
30794     {
30795         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30796         
30797         if (this.buttons.length) {
30798             
30799             Roo.each(this.buttons, function(bb) {
30800                 
30801                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30802                 
30803                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30804                 
30805             }, this);
30806         }
30807         
30808         if(this.loadMask){
30809             this.maskEl = this.el;
30810         }
30811     },
30812     
30813     initEvents : function()
30814     {
30815         this.urlAPI = (window.createObjectURL && window) || 
30816                                 (window.URL && URL.revokeObjectURL && URL) || 
30817                                 (window.webkitURL && webkitURL);
30818                         
30819         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30820         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30821         
30822         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30823         this.selectorEl.hide();
30824         
30825         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30826         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30827         
30828         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30829         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30830         this.thumbEl.hide();
30831         
30832         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30833         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30834         
30835         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30836         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30837         this.errorEl.hide();
30838         
30839         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30840         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30841         this.footerEl.hide();
30842         
30843         this.setThumbBoxSize();
30844         
30845         this.bind();
30846         
30847         this.resize();
30848         
30849         this.fireEvent('initial', this);
30850     },
30851
30852     bind : function()
30853     {
30854         var _this = this;
30855         
30856         window.addEventListener("resize", function() { _this.resize(); } );
30857         
30858         this.bodyEl.on('click', this.beforeSelectFile, this);
30859         
30860         if(Roo.isTouch){
30861             this.bodyEl.on('touchstart', this.onTouchStart, this);
30862             this.bodyEl.on('touchmove', this.onTouchMove, this);
30863             this.bodyEl.on('touchend', this.onTouchEnd, this);
30864         }
30865         
30866         if(!Roo.isTouch){
30867             this.bodyEl.on('mousedown', this.onMouseDown, this);
30868             this.bodyEl.on('mousemove', this.onMouseMove, this);
30869             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30870             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30871             Roo.get(document).on('mouseup', this.onMouseUp, this);
30872         }
30873         
30874         this.selectorEl.on('change', this.onFileSelected, this);
30875     },
30876     
30877     reset : function()
30878     {    
30879         this.scale = 0;
30880         this.baseScale = 1;
30881         this.rotate = 0;
30882         this.baseRotate = 1;
30883         this.dragable = false;
30884         this.pinching = false;
30885         this.mouseX = 0;
30886         this.mouseY = 0;
30887         this.cropData = false;
30888         this.notifyEl.dom.innerHTML = this.emptyText;
30889         
30890         this.selectorEl.dom.value = '';
30891         
30892     },
30893     
30894     resize : function()
30895     {
30896         if(this.fireEvent('resize', this) != false){
30897             this.setThumbBoxPosition();
30898             this.setCanvasPosition();
30899         }
30900     },
30901     
30902     onFooterButtonClick : function(e, el, o, type)
30903     {
30904         switch (type) {
30905             case 'rotate-left' :
30906                 this.onRotateLeft(e);
30907                 break;
30908             case 'rotate-right' :
30909                 this.onRotateRight(e);
30910                 break;
30911             case 'picture' :
30912                 this.beforeSelectFile(e);
30913                 break;
30914             case 'trash' :
30915                 this.trash(e);
30916                 break;
30917             case 'crop' :
30918                 this.crop(e);
30919                 break;
30920             case 'download' :
30921                 this.download(e);
30922                 break;
30923             default :
30924                 break;
30925         }
30926         
30927         this.fireEvent('footerbuttonclick', this, type);
30928     },
30929     
30930     beforeSelectFile : function(e)
30931     {
30932         e.preventDefault();
30933         
30934         if(this.fireEvent('beforeselectfile', this) != false){
30935             this.selectorEl.dom.click();
30936         }
30937     },
30938     
30939     onFileSelected : function(e)
30940     {
30941         e.preventDefault();
30942         
30943         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30944             return;
30945         }
30946         
30947         var file = this.selectorEl.dom.files[0];
30948         
30949         if(this.fireEvent('inspect', this, file) != false){
30950             this.prepare(file);
30951         }
30952         
30953     },
30954     
30955     trash : function(e)
30956     {
30957         this.fireEvent('trash', this);
30958     },
30959     
30960     download : function(e)
30961     {
30962         this.fireEvent('download', this);
30963     },
30964     
30965     loadCanvas : function(src)
30966     {   
30967         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30968             
30969             this.reset();
30970             
30971             this.imageEl = document.createElement('img');
30972             
30973             var _this = this;
30974             
30975             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30976             
30977             this.imageEl.src = src;
30978         }
30979     },
30980     
30981     onLoadCanvas : function()
30982     {   
30983         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30984         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30985         
30986         this.bodyEl.un('click', this.beforeSelectFile, this);
30987         
30988         this.notifyEl.hide();
30989         this.thumbEl.show();
30990         this.footerEl.show();
30991         
30992         this.baseRotateLevel();
30993         
30994         if(this.isDocument){
30995             this.setThumbBoxSize();
30996         }
30997         
30998         this.setThumbBoxPosition();
30999         
31000         this.baseScaleLevel();
31001         
31002         this.draw();
31003         
31004         this.resize();
31005         
31006         this.canvasLoaded = true;
31007         
31008         if(this.loadMask){
31009             this.maskEl.unmask();
31010         }
31011         
31012     },
31013     
31014     setCanvasPosition : function()
31015     {   
31016         if(!this.canvasEl){
31017             return;
31018         }
31019         
31020         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31021         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31022         
31023         this.previewEl.setLeft(pw);
31024         this.previewEl.setTop(ph);
31025         
31026     },
31027     
31028     onMouseDown : function(e)
31029     {   
31030         e.stopEvent();
31031         
31032         this.dragable = true;
31033         this.pinching = false;
31034         
31035         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31036             this.dragable = false;
31037             return;
31038         }
31039         
31040         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31041         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31042         
31043     },
31044     
31045     onMouseMove : function(e)
31046     {   
31047         e.stopEvent();
31048         
31049         if(!this.canvasLoaded){
31050             return;
31051         }
31052         
31053         if (!this.dragable){
31054             return;
31055         }
31056         
31057         var minX = Math.ceil(this.thumbEl.getLeft(true));
31058         var minY = Math.ceil(this.thumbEl.getTop(true));
31059         
31060         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31061         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31062         
31063         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31064         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31065         
31066         x = x - this.mouseX;
31067         y = y - this.mouseY;
31068         
31069         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31070         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31071         
31072         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31073         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31074         
31075         this.previewEl.setLeft(bgX);
31076         this.previewEl.setTop(bgY);
31077         
31078         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31079         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31080     },
31081     
31082     onMouseUp : function(e)
31083     {   
31084         e.stopEvent();
31085         
31086         this.dragable = false;
31087     },
31088     
31089     onMouseWheel : function(e)
31090     {   
31091         e.stopEvent();
31092         
31093         this.startScale = this.scale;
31094         
31095         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31096         
31097         if(!this.zoomable()){
31098             this.scale = this.startScale;
31099             return;
31100         }
31101         
31102         this.draw();
31103         
31104         return;
31105     },
31106     
31107     zoomable : function()
31108     {
31109         var minScale = this.thumbEl.getWidth() / this.minWidth;
31110         
31111         if(this.minWidth < this.minHeight){
31112             minScale = this.thumbEl.getHeight() / this.minHeight;
31113         }
31114         
31115         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31116         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31117         
31118         if(
31119                 this.isDocument &&
31120                 (this.rotate == 0 || this.rotate == 180) && 
31121                 (
31122                     width > this.imageEl.OriginWidth || 
31123                     height > this.imageEl.OriginHeight ||
31124                     (width < this.minWidth && height < this.minHeight)
31125                 )
31126         ){
31127             return false;
31128         }
31129         
31130         if(
31131                 this.isDocument &&
31132                 (this.rotate == 90 || this.rotate == 270) && 
31133                 (
31134                     width > this.imageEl.OriginWidth || 
31135                     height > this.imageEl.OriginHeight ||
31136                     (width < this.minHeight && height < this.minWidth)
31137                 )
31138         ){
31139             return false;
31140         }
31141         
31142         if(
31143                 !this.isDocument &&
31144                 (this.rotate == 0 || this.rotate == 180) && 
31145                 (
31146                     width < this.minWidth || 
31147                     width > this.imageEl.OriginWidth || 
31148                     height < this.minHeight || 
31149                     height > this.imageEl.OriginHeight
31150                 )
31151         ){
31152             return false;
31153         }
31154         
31155         if(
31156                 !this.isDocument &&
31157                 (this.rotate == 90 || this.rotate == 270) && 
31158                 (
31159                     width < this.minHeight || 
31160                     width > this.imageEl.OriginWidth || 
31161                     height < this.minWidth || 
31162                     height > this.imageEl.OriginHeight
31163                 )
31164         ){
31165             return false;
31166         }
31167         
31168         return true;
31169         
31170     },
31171     
31172     onRotateLeft : function(e)
31173     {   
31174         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31175             
31176             var minScale = this.thumbEl.getWidth() / this.minWidth;
31177             
31178             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31179             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31180             
31181             this.startScale = this.scale;
31182             
31183             while (this.getScaleLevel() < minScale){
31184             
31185                 this.scale = this.scale + 1;
31186                 
31187                 if(!this.zoomable()){
31188                     break;
31189                 }
31190                 
31191                 if(
31192                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31193                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31194                 ){
31195                     continue;
31196                 }
31197                 
31198                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31199
31200                 this.draw();
31201                 
31202                 return;
31203             }
31204             
31205             this.scale = this.startScale;
31206             
31207             this.onRotateFail();
31208             
31209             return false;
31210         }
31211         
31212         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31213
31214         if(this.isDocument){
31215             this.setThumbBoxSize();
31216             this.setThumbBoxPosition();
31217             this.setCanvasPosition();
31218         }
31219         
31220         this.draw();
31221         
31222         this.fireEvent('rotate', this, 'left');
31223         
31224     },
31225     
31226     onRotateRight : function(e)
31227     {
31228         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31229             
31230             var minScale = this.thumbEl.getWidth() / this.minWidth;
31231         
31232             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31233             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31234             
31235             this.startScale = this.scale;
31236             
31237             while (this.getScaleLevel() < minScale){
31238             
31239                 this.scale = this.scale + 1;
31240                 
31241                 if(!this.zoomable()){
31242                     break;
31243                 }
31244                 
31245                 if(
31246                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31247                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31248                 ){
31249                     continue;
31250                 }
31251                 
31252                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31253
31254                 this.draw();
31255                 
31256                 return;
31257             }
31258             
31259             this.scale = this.startScale;
31260             
31261             this.onRotateFail();
31262             
31263             return false;
31264         }
31265         
31266         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31267
31268         if(this.isDocument){
31269             this.setThumbBoxSize();
31270             this.setThumbBoxPosition();
31271             this.setCanvasPosition();
31272         }
31273         
31274         this.draw();
31275         
31276         this.fireEvent('rotate', this, 'right');
31277     },
31278     
31279     onRotateFail : function()
31280     {
31281         this.errorEl.show(true);
31282         
31283         var _this = this;
31284         
31285         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31286     },
31287     
31288     draw : function()
31289     {
31290         this.previewEl.dom.innerHTML = '';
31291         
31292         var canvasEl = document.createElement("canvas");
31293         
31294         var contextEl = canvasEl.getContext("2d");
31295         
31296         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31297         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31298         var center = this.imageEl.OriginWidth / 2;
31299         
31300         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31301             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31302             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31303             center = this.imageEl.OriginHeight / 2;
31304         }
31305         
31306         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31307         
31308         contextEl.translate(center, center);
31309         contextEl.rotate(this.rotate * Math.PI / 180);
31310
31311         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31312         
31313         this.canvasEl = document.createElement("canvas");
31314         
31315         this.contextEl = this.canvasEl.getContext("2d");
31316         
31317         switch (this.rotate) {
31318             case 0 :
31319                 
31320                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31321                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31322                 
31323                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31324                 
31325                 break;
31326             case 90 : 
31327                 
31328                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31329                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31330                 
31331                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31332                     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);
31333                     break;
31334                 }
31335                 
31336                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31337                 
31338                 break;
31339             case 180 :
31340                 
31341                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31342                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31343                 
31344                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31345                     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);
31346                     break;
31347                 }
31348                 
31349                 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);
31350                 
31351                 break;
31352             case 270 :
31353                 
31354                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31355                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31356         
31357                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31358                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31359                     break;
31360                 }
31361                 
31362                 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);
31363                 
31364                 break;
31365             default : 
31366                 break;
31367         }
31368         
31369         this.previewEl.appendChild(this.canvasEl);
31370         
31371         this.setCanvasPosition();
31372     },
31373     
31374     crop : function()
31375     {
31376         if(!this.canvasLoaded){
31377             return;
31378         }
31379         
31380         var imageCanvas = document.createElement("canvas");
31381         
31382         var imageContext = imageCanvas.getContext("2d");
31383         
31384         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31385         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31386         
31387         var center = imageCanvas.width / 2;
31388         
31389         imageContext.translate(center, center);
31390         
31391         imageContext.rotate(this.rotate * Math.PI / 180);
31392         
31393         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31394         
31395         var canvas = document.createElement("canvas");
31396         
31397         var context = canvas.getContext("2d");
31398                 
31399         canvas.width = this.minWidth;
31400         canvas.height = this.minHeight;
31401
31402         switch (this.rotate) {
31403             case 0 :
31404                 
31405                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31406                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31407                 
31408                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31409                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31410                 
31411                 var targetWidth = this.minWidth - 2 * x;
31412                 var targetHeight = this.minHeight - 2 * y;
31413                 
31414                 var scale = 1;
31415                 
31416                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31417                     scale = targetWidth / width;
31418                 }
31419                 
31420                 if(x > 0 && y == 0){
31421                     scale = targetHeight / height;
31422                 }
31423                 
31424                 if(x > 0 && y > 0){
31425                     scale = targetWidth / width;
31426                     
31427                     if(width < height){
31428                         scale = targetHeight / height;
31429                     }
31430                 }
31431                 
31432                 context.scale(scale, scale);
31433                 
31434                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31435                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31436
31437                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31438                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31439
31440                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31441                 
31442                 break;
31443             case 90 : 
31444                 
31445                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31446                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31447                 
31448                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31449                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31450                 
31451                 var targetWidth = this.minWidth - 2 * x;
31452                 var targetHeight = this.minHeight - 2 * y;
31453                 
31454                 var scale = 1;
31455                 
31456                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31457                     scale = targetWidth / width;
31458                 }
31459                 
31460                 if(x > 0 && y == 0){
31461                     scale = targetHeight / height;
31462                 }
31463                 
31464                 if(x > 0 && y > 0){
31465                     scale = targetWidth / width;
31466                     
31467                     if(width < height){
31468                         scale = targetHeight / height;
31469                     }
31470                 }
31471                 
31472                 context.scale(scale, scale);
31473                 
31474                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31475                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31476
31477                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31478                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31479                 
31480                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31481                 
31482                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31483                 
31484                 break;
31485             case 180 :
31486                 
31487                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31488                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31489                 
31490                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31491                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31492                 
31493                 var targetWidth = this.minWidth - 2 * x;
31494                 var targetHeight = this.minHeight - 2 * y;
31495                 
31496                 var scale = 1;
31497                 
31498                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31499                     scale = targetWidth / width;
31500                 }
31501                 
31502                 if(x > 0 && y == 0){
31503                     scale = targetHeight / height;
31504                 }
31505                 
31506                 if(x > 0 && y > 0){
31507                     scale = targetWidth / width;
31508                     
31509                     if(width < height){
31510                         scale = targetHeight / height;
31511                     }
31512                 }
31513                 
31514                 context.scale(scale, scale);
31515                 
31516                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31517                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31518
31519                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31520                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31521
31522                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31523                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31524                 
31525                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31526                 
31527                 break;
31528             case 270 :
31529                 
31530                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31531                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31532                 
31533                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31534                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31535                 
31536                 var targetWidth = this.minWidth - 2 * x;
31537                 var targetHeight = this.minHeight - 2 * y;
31538                 
31539                 var scale = 1;
31540                 
31541                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31542                     scale = targetWidth / width;
31543                 }
31544                 
31545                 if(x > 0 && y == 0){
31546                     scale = targetHeight / height;
31547                 }
31548                 
31549                 if(x > 0 && y > 0){
31550                     scale = targetWidth / width;
31551                     
31552                     if(width < height){
31553                         scale = targetHeight / height;
31554                     }
31555                 }
31556                 
31557                 context.scale(scale, scale);
31558                 
31559                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31560                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31561
31562                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31563                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31564                 
31565                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31566                 
31567                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31568                 
31569                 break;
31570             default : 
31571                 break;
31572         }
31573         
31574         this.cropData = canvas.toDataURL(this.cropType);
31575         
31576         if(this.fireEvent('crop', this, this.cropData) !== false){
31577             this.process(this.file, this.cropData);
31578         }
31579         
31580         return;
31581         
31582     },
31583     
31584     setThumbBoxSize : function()
31585     {
31586         var width, height;
31587         
31588         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31589             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31590             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31591             
31592             this.minWidth = width;
31593             this.minHeight = height;
31594             
31595             if(this.rotate == 90 || this.rotate == 270){
31596                 this.minWidth = height;
31597                 this.minHeight = width;
31598             }
31599         }
31600         
31601         height = 300;
31602         width = Math.ceil(this.minWidth * height / this.minHeight);
31603         
31604         if(this.minWidth > this.minHeight){
31605             width = 300;
31606             height = Math.ceil(this.minHeight * width / this.minWidth);
31607         }
31608         
31609         this.thumbEl.setStyle({
31610             width : width + 'px',
31611             height : height + 'px'
31612         });
31613
31614         return;
31615             
31616     },
31617     
31618     setThumbBoxPosition : function()
31619     {
31620         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31621         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31622         
31623         this.thumbEl.setLeft(x);
31624         this.thumbEl.setTop(y);
31625         
31626     },
31627     
31628     baseRotateLevel : function()
31629     {
31630         this.baseRotate = 1;
31631         
31632         if(
31633                 typeof(this.exif) != 'undefined' &&
31634                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31635                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31636         ){
31637             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31638         }
31639         
31640         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31641         
31642     },
31643     
31644     baseScaleLevel : function()
31645     {
31646         var width, height;
31647         
31648         if(this.isDocument){
31649             
31650             if(this.baseRotate == 6 || this.baseRotate == 8){
31651             
31652                 height = this.thumbEl.getHeight();
31653                 this.baseScale = height / this.imageEl.OriginWidth;
31654
31655                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31656                     width = this.thumbEl.getWidth();
31657                     this.baseScale = width / this.imageEl.OriginHeight;
31658                 }
31659
31660                 return;
31661             }
31662
31663             height = this.thumbEl.getHeight();
31664             this.baseScale = height / this.imageEl.OriginHeight;
31665
31666             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31667                 width = this.thumbEl.getWidth();
31668                 this.baseScale = width / this.imageEl.OriginWidth;
31669             }
31670
31671             return;
31672         }
31673         
31674         if(this.baseRotate == 6 || this.baseRotate == 8){
31675             
31676             width = this.thumbEl.getHeight();
31677             this.baseScale = width / this.imageEl.OriginHeight;
31678             
31679             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31680                 height = this.thumbEl.getWidth();
31681                 this.baseScale = height / this.imageEl.OriginHeight;
31682             }
31683             
31684             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31685                 height = this.thumbEl.getWidth();
31686                 this.baseScale = height / this.imageEl.OriginHeight;
31687                 
31688                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31689                     width = this.thumbEl.getHeight();
31690                     this.baseScale = width / this.imageEl.OriginWidth;
31691                 }
31692             }
31693             
31694             return;
31695         }
31696         
31697         width = this.thumbEl.getWidth();
31698         this.baseScale = width / this.imageEl.OriginWidth;
31699         
31700         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31701             height = this.thumbEl.getHeight();
31702             this.baseScale = height / this.imageEl.OriginHeight;
31703         }
31704         
31705         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31706             
31707             height = this.thumbEl.getHeight();
31708             this.baseScale = height / this.imageEl.OriginHeight;
31709             
31710             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31711                 width = this.thumbEl.getWidth();
31712                 this.baseScale = width / this.imageEl.OriginWidth;
31713             }
31714             
31715         }
31716         
31717         return;
31718     },
31719     
31720     getScaleLevel : function()
31721     {
31722         return this.baseScale * Math.pow(1.1, this.scale);
31723     },
31724     
31725     onTouchStart : function(e)
31726     {
31727         if(!this.canvasLoaded){
31728             this.beforeSelectFile(e);
31729             return;
31730         }
31731         
31732         var touches = e.browserEvent.touches;
31733         
31734         if(!touches){
31735             return;
31736         }
31737         
31738         if(touches.length == 1){
31739             this.onMouseDown(e);
31740             return;
31741         }
31742         
31743         if(touches.length != 2){
31744             return;
31745         }
31746         
31747         var coords = [];
31748         
31749         for(var i = 0, finger; finger = touches[i]; i++){
31750             coords.push(finger.pageX, finger.pageY);
31751         }
31752         
31753         var x = Math.pow(coords[0] - coords[2], 2);
31754         var y = Math.pow(coords[1] - coords[3], 2);
31755         
31756         this.startDistance = Math.sqrt(x + y);
31757         
31758         this.startScale = this.scale;
31759         
31760         this.pinching = true;
31761         this.dragable = false;
31762         
31763     },
31764     
31765     onTouchMove : function(e)
31766     {
31767         if(!this.pinching && !this.dragable){
31768             return;
31769         }
31770         
31771         var touches = e.browserEvent.touches;
31772         
31773         if(!touches){
31774             return;
31775         }
31776         
31777         if(this.dragable){
31778             this.onMouseMove(e);
31779             return;
31780         }
31781         
31782         var coords = [];
31783         
31784         for(var i = 0, finger; finger = touches[i]; i++){
31785             coords.push(finger.pageX, finger.pageY);
31786         }
31787         
31788         var x = Math.pow(coords[0] - coords[2], 2);
31789         var y = Math.pow(coords[1] - coords[3], 2);
31790         
31791         this.endDistance = Math.sqrt(x + y);
31792         
31793         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31794         
31795         if(!this.zoomable()){
31796             this.scale = this.startScale;
31797             return;
31798         }
31799         
31800         this.draw();
31801         
31802     },
31803     
31804     onTouchEnd : function(e)
31805     {
31806         this.pinching = false;
31807         this.dragable = false;
31808         
31809     },
31810     
31811     process : function(file, crop)
31812     {
31813         if(this.loadMask){
31814             this.maskEl.mask(this.loadingText);
31815         }
31816         
31817         this.xhr = new XMLHttpRequest();
31818         
31819         file.xhr = this.xhr;
31820
31821         this.xhr.open(this.method, this.url, true);
31822         
31823         var headers = {
31824             "Accept": "application/json",
31825             "Cache-Control": "no-cache",
31826             "X-Requested-With": "XMLHttpRequest"
31827         };
31828         
31829         for (var headerName in headers) {
31830             var headerValue = headers[headerName];
31831             if (headerValue) {
31832                 this.xhr.setRequestHeader(headerName, headerValue);
31833             }
31834         }
31835         
31836         var _this = this;
31837         
31838         this.xhr.onload = function()
31839         {
31840             _this.xhrOnLoad(_this.xhr);
31841         }
31842         
31843         this.xhr.onerror = function()
31844         {
31845             _this.xhrOnError(_this.xhr);
31846         }
31847         
31848         var formData = new FormData();
31849
31850         formData.append('returnHTML', 'NO');
31851         
31852         if(crop){
31853             formData.append('crop', crop);
31854         }
31855         
31856         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31857             formData.append(this.paramName, file, file.name);
31858         }
31859         
31860         if(typeof(file.filename) != 'undefined'){
31861             formData.append('filename', file.filename);
31862         }
31863         
31864         if(typeof(file.mimetype) != 'undefined'){
31865             formData.append('mimetype', file.mimetype);
31866         }
31867         
31868         if(this.fireEvent('arrange', this, formData) != false){
31869             this.xhr.send(formData);
31870         };
31871     },
31872     
31873     xhrOnLoad : function(xhr)
31874     {
31875         if(this.loadMask){
31876             this.maskEl.unmask();
31877         }
31878         
31879         if (xhr.readyState !== 4) {
31880             this.fireEvent('exception', this, xhr);
31881             return;
31882         }
31883
31884         var response = Roo.decode(xhr.responseText);
31885         
31886         if(!response.success){
31887             this.fireEvent('exception', this, xhr);
31888             return;
31889         }
31890         
31891         var response = Roo.decode(xhr.responseText);
31892         
31893         this.fireEvent('upload', this, response);
31894         
31895     },
31896     
31897     xhrOnError : function()
31898     {
31899         if(this.loadMask){
31900             this.maskEl.unmask();
31901         }
31902         
31903         Roo.log('xhr on error');
31904         
31905         var response = Roo.decode(xhr.responseText);
31906           
31907         Roo.log(response);
31908         
31909     },
31910     
31911     prepare : function(file)
31912     {   
31913         if(this.loadMask){
31914             this.maskEl.mask(this.loadingText);
31915         }
31916         
31917         this.file = false;
31918         this.exif = {};
31919         
31920         if(typeof(file) === 'string'){
31921             this.loadCanvas(file);
31922             return;
31923         }
31924         
31925         if(!file || !this.urlAPI){
31926             return;
31927         }
31928         
31929         this.file = file;
31930         this.cropType = file.type;
31931         
31932         var _this = this;
31933         
31934         if(this.fireEvent('prepare', this, this.file) != false){
31935             
31936             var reader = new FileReader();
31937             
31938             reader.onload = function (e) {
31939                 if (e.target.error) {
31940                     Roo.log(e.target.error);
31941                     return;
31942                 }
31943                 
31944                 var buffer = e.target.result,
31945                     dataView = new DataView(buffer),
31946                     offset = 2,
31947                     maxOffset = dataView.byteLength - 4,
31948                     markerBytes,
31949                     markerLength;
31950                 
31951                 if (dataView.getUint16(0) === 0xffd8) {
31952                     while (offset < maxOffset) {
31953                         markerBytes = dataView.getUint16(offset);
31954                         
31955                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31956                             markerLength = dataView.getUint16(offset + 2) + 2;
31957                             if (offset + markerLength > dataView.byteLength) {
31958                                 Roo.log('Invalid meta data: Invalid segment size.');
31959                                 break;
31960                             }
31961                             
31962                             if(markerBytes == 0xffe1){
31963                                 _this.parseExifData(
31964                                     dataView,
31965                                     offset,
31966                                     markerLength
31967                                 );
31968                             }
31969                             
31970                             offset += markerLength;
31971                             
31972                             continue;
31973                         }
31974                         
31975                         break;
31976                     }
31977                     
31978                 }
31979                 
31980                 var url = _this.urlAPI.createObjectURL(_this.file);
31981                 
31982                 _this.loadCanvas(url);
31983                 
31984                 return;
31985             }
31986             
31987             reader.readAsArrayBuffer(this.file);
31988             
31989         }
31990         
31991     },
31992     
31993     parseExifData : function(dataView, offset, length)
31994     {
31995         var tiffOffset = offset + 10,
31996             littleEndian,
31997             dirOffset;
31998     
31999         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32000             // No Exif data, might be XMP data instead
32001             return;
32002         }
32003         
32004         // Check for the ASCII code for "Exif" (0x45786966):
32005         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32006             // No Exif data, might be XMP data instead
32007             return;
32008         }
32009         if (tiffOffset + 8 > dataView.byteLength) {
32010             Roo.log('Invalid Exif data: Invalid segment size.');
32011             return;
32012         }
32013         // Check for the two null bytes:
32014         if (dataView.getUint16(offset + 8) !== 0x0000) {
32015             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32016             return;
32017         }
32018         // Check the byte alignment:
32019         switch (dataView.getUint16(tiffOffset)) {
32020         case 0x4949:
32021             littleEndian = true;
32022             break;
32023         case 0x4D4D:
32024             littleEndian = false;
32025             break;
32026         default:
32027             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32028             return;
32029         }
32030         // Check for the TIFF tag marker (0x002A):
32031         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32032             Roo.log('Invalid Exif data: Missing TIFF marker.');
32033             return;
32034         }
32035         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32036         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32037         
32038         this.parseExifTags(
32039             dataView,
32040             tiffOffset,
32041             tiffOffset + dirOffset,
32042             littleEndian
32043         );
32044     },
32045     
32046     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32047     {
32048         var tagsNumber,
32049             dirEndOffset,
32050             i;
32051         if (dirOffset + 6 > dataView.byteLength) {
32052             Roo.log('Invalid Exif data: Invalid directory offset.');
32053             return;
32054         }
32055         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32056         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32057         if (dirEndOffset + 4 > dataView.byteLength) {
32058             Roo.log('Invalid Exif data: Invalid directory size.');
32059             return;
32060         }
32061         for (i = 0; i < tagsNumber; i += 1) {
32062             this.parseExifTag(
32063                 dataView,
32064                 tiffOffset,
32065                 dirOffset + 2 + 12 * i, // tag offset
32066                 littleEndian
32067             );
32068         }
32069         // Return the offset to the next directory:
32070         return dataView.getUint32(dirEndOffset, littleEndian);
32071     },
32072     
32073     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32074     {
32075         var tag = dataView.getUint16(offset, littleEndian);
32076         
32077         this.exif[tag] = this.getExifValue(
32078             dataView,
32079             tiffOffset,
32080             offset,
32081             dataView.getUint16(offset + 2, littleEndian), // tag type
32082             dataView.getUint32(offset + 4, littleEndian), // tag length
32083             littleEndian
32084         );
32085     },
32086     
32087     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32088     {
32089         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32090             tagSize,
32091             dataOffset,
32092             values,
32093             i,
32094             str,
32095             c;
32096     
32097         if (!tagType) {
32098             Roo.log('Invalid Exif data: Invalid tag type.');
32099             return;
32100         }
32101         
32102         tagSize = tagType.size * length;
32103         // Determine if the value is contained in the dataOffset bytes,
32104         // or if the value at the dataOffset is a pointer to the actual data:
32105         dataOffset = tagSize > 4 ?
32106                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32107         if (dataOffset + tagSize > dataView.byteLength) {
32108             Roo.log('Invalid Exif data: Invalid data offset.');
32109             return;
32110         }
32111         if (length === 1) {
32112             return tagType.getValue(dataView, dataOffset, littleEndian);
32113         }
32114         values = [];
32115         for (i = 0; i < length; i += 1) {
32116             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32117         }
32118         
32119         if (tagType.ascii) {
32120             str = '';
32121             // Concatenate the chars:
32122             for (i = 0; i < values.length; i += 1) {
32123                 c = values[i];
32124                 // Ignore the terminating NULL byte(s):
32125                 if (c === '\u0000') {
32126                     break;
32127                 }
32128                 str += c;
32129             }
32130             return str;
32131         }
32132         return values;
32133     }
32134     
32135 });
32136
32137 Roo.apply(Roo.bootstrap.UploadCropbox, {
32138     tags : {
32139         'Orientation': 0x0112
32140     },
32141     
32142     Orientation: {
32143             1: 0, //'top-left',
32144 //            2: 'top-right',
32145             3: 180, //'bottom-right',
32146 //            4: 'bottom-left',
32147 //            5: 'left-top',
32148             6: 90, //'right-top',
32149 //            7: 'right-bottom',
32150             8: 270 //'left-bottom'
32151     },
32152     
32153     exifTagTypes : {
32154         // byte, 8-bit unsigned int:
32155         1: {
32156             getValue: function (dataView, dataOffset) {
32157                 return dataView.getUint8(dataOffset);
32158             },
32159             size: 1
32160         },
32161         // ascii, 8-bit byte:
32162         2: {
32163             getValue: function (dataView, dataOffset) {
32164                 return String.fromCharCode(dataView.getUint8(dataOffset));
32165             },
32166             size: 1,
32167             ascii: true
32168         },
32169         // short, 16 bit int:
32170         3: {
32171             getValue: function (dataView, dataOffset, littleEndian) {
32172                 return dataView.getUint16(dataOffset, littleEndian);
32173             },
32174             size: 2
32175         },
32176         // long, 32 bit int:
32177         4: {
32178             getValue: function (dataView, dataOffset, littleEndian) {
32179                 return dataView.getUint32(dataOffset, littleEndian);
32180             },
32181             size: 4
32182         },
32183         // rational = two long values, first is numerator, second is denominator:
32184         5: {
32185             getValue: function (dataView, dataOffset, littleEndian) {
32186                 return dataView.getUint32(dataOffset, littleEndian) /
32187                     dataView.getUint32(dataOffset + 4, littleEndian);
32188             },
32189             size: 8
32190         },
32191         // slong, 32 bit signed int:
32192         9: {
32193             getValue: function (dataView, dataOffset, littleEndian) {
32194                 return dataView.getInt32(dataOffset, littleEndian);
32195             },
32196             size: 4
32197         },
32198         // srational, two slongs, first is numerator, second is denominator:
32199         10: {
32200             getValue: function (dataView, dataOffset, littleEndian) {
32201                 return dataView.getInt32(dataOffset, littleEndian) /
32202                     dataView.getInt32(dataOffset + 4, littleEndian);
32203             },
32204             size: 8
32205         }
32206     },
32207     
32208     footer : {
32209         STANDARD : [
32210             {
32211                 tag : 'div',
32212                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32213                 action : 'rotate-left',
32214                 cn : [
32215                     {
32216                         tag : 'button',
32217                         cls : 'btn btn-default',
32218                         html : '<i class="fa fa-undo"></i>'
32219                     }
32220                 ]
32221             },
32222             {
32223                 tag : 'div',
32224                 cls : 'btn-group roo-upload-cropbox-picture',
32225                 action : 'picture',
32226                 cn : [
32227                     {
32228                         tag : 'button',
32229                         cls : 'btn btn-default',
32230                         html : '<i class="fa fa-picture-o"></i>'
32231                     }
32232                 ]
32233             },
32234             {
32235                 tag : 'div',
32236                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32237                 action : 'rotate-right',
32238                 cn : [
32239                     {
32240                         tag : 'button',
32241                         cls : 'btn btn-default',
32242                         html : '<i class="fa fa-repeat"></i>'
32243                     }
32244                 ]
32245             }
32246         ],
32247         DOCUMENT : [
32248             {
32249                 tag : 'div',
32250                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32251                 action : 'rotate-left',
32252                 cn : [
32253                     {
32254                         tag : 'button',
32255                         cls : 'btn btn-default',
32256                         html : '<i class="fa fa-undo"></i>'
32257                     }
32258                 ]
32259             },
32260             {
32261                 tag : 'div',
32262                 cls : 'btn-group roo-upload-cropbox-download',
32263                 action : 'download',
32264                 cn : [
32265                     {
32266                         tag : 'button',
32267                         cls : 'btn btn-default',
32268                         html : '<i class="fa fa-download"></i>'
32269                     }
32270                 ]
32271             },
32272             {
32273                 tag : 'div',
32274                 cls : 'btn-group roo-upload-cropbox-crop',
32275                 action : 'crop',
32276                 cn : [
32277                     {
32278                         tag : 'button',
32279                         cls : 'btn btn-default',
32280                         html : '<i class="fa fa-crop"></i>'
32281                     }
32282                 ]
32283             },
32284             {
32285                 tag : 'div',
32286                 cls : 'btn-group roo-upload-cropbox-trash',
32287                 action : 'trash',
32288                 cn : [
32289                     {
32290                         tag : 'button',
32291                         cls : 'btn btn-default',
32292                         html : '<i class="fa fa-trash"></i>'
32293                     }
32294                 ]
32295             },
32296             {
32297                 tag : 'div',
32298                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32299                 action : 'rotate-right',
32300                 cn : [
32301                     {
32302                         tag : 'button',
32303                         cls : 'btn btn-default',
32304                         html : '<i class="fa fa-repeat"></i>'
32305                     }
32306                 ]
32307             }
32308         ],
32309         ROTATOR : [
32310             {
32311                 tag : 'div',
32312                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32313                 action : 'rotate-left',
32314                 cn : [
32315                     {
32316                         tag : 'button',
32317                         cls : 'btn btn-default',
32318                         html : '<i class="fa fa-undo"></i>'
32319                     }
32320                 ]
32321             },
32322             {
32323                 tag : 'div',
32324                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32325                 action : 'rotate-right',
32326                 cn : [
32327                     {
32328                         tag : 'button',
32329                         cls : 'btn btn-default',
32330                         html : '<i class="fa fa-repeat"></i>'
32331                     }
32332                 ]
32333             }
32334         ]
32335     }
32336 });
32337
32338 /*
32339 * Licence: LGPL
32340 */
32341
32342 /**
32343  * @class Roo.bootstrap.DocumentManager
32344  * @extends Roo.bootstrap.Component
32345  * Bootstrap DocumentManager class
32346  * @cfg {String} paramName default 'imageUpload'
32347  * @cfg {String} toolTipName default 'filename'
32348  * @cfg {String} method default POST
32349  * @cfg {String} url action url
32350  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32351  * @cfg {Boolean} multiple multiple upload default true
32352  * @cfg {Number} thumbSize default 300
32353  * @cfg {String} fieldLabel
32354  * @cfg {Number} labelWidth default 4
32355  * @cfg {String} labelAlign (left|top) default left
32356  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32357 * @cfg {Number} labellg set the width of label (1-12)
32358  * @cfg {Number} labelmd set the width of label (1-12)
32359  * @cfg {Number} labelsm set the width of label (1-12)
32360  * @cfg {Number} labelxs set the width of label (1-12)
32361  * 
32362  * @constructor
32363  * Create a new DocumentManager
32364  * @param {Object} config The config object
32365  */
32366
32367 Roo.bootstrap.DocumentManager = function(config){
32368     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32369     
32370     this.files = [];
32371     this.delegates = [];
32372     
32373     this.addEvents({
32374         /**
32375          * @event initial
32376          * Fire when initial the DocumentManager
32377          * @param {Roo.bootstrap.DocumentManager} this
32378          */
32379         "initial" : true,
32380         /**
32381          * @event inspect
32382          * inspect selected file
32383          * @param {Roo.bootstrap.DocumentManager} this
32384          * @param {File} file
32385          */
32386         "inspect" : true,
32387         /**
32388          * @event exception
32389          * Fire when xhr load exception
32390          * @param {Roo.bootstrap.DocumentManager} this
32391          * @param {XMLHttpRequest} xhr
32392          */
32393         "exception" : true,
32394         /**
32395          * @event afterupload
32396          * Fire when xhr load exception
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          * @param {XMLHttpRequest} xhr
32399          */
32400         "afterupload" : true,
32401         /**
32402          * @event prepare
32403          * prepare the form data
32404          * @param {Roo.bootstrap.DocumentManager} this
32405          * @param {Object} formData
32406          */
32407         "prepare" : true,
32408         /**
32409          * @event remove
32410          * Fire when remove the file
32411          * @param {Roo.bootstrap.DocumentManager} this
32412          * @param {Object} file
32413          */
32414         "remove" : true,
32415         /**
32416          * @event refresh
32417          * Fire after refresh the file
32418          * @param {Roo.bootstrap.DocumentManager} this
32419          */
32420         "refresh" : true,
32421         /**
32422          * @event click
32423          * Fire after click the image
32424          * @param {Roo.bootstrap.DocumentManager} this
32425          * @param {Object} file
32426          */
32427         "click" : true,
32428         /**
32429          * @event edit
32430          * Fire when upload a image and editable set to true
32431          * @param {Roo.bootstrap.DocumentManager} this
32432          * @param {Object} file
32433          */
32434         "edit" : true,
32435         /**
32436          * @event beforeselectfile
32437          * Fire before select file
32438          * @param {Roo.bootstrap.DocumentManager} this
32439          */
32440         "beforeselectfile" : true,
32441         /**
32442          * @event process
32443          * Fire before process file
32444          * @param {Roo.bootstrap.DocumentManager} this
32445          * @param {Object} file
32446          */
32447         "process" : true,
32448         /**
32449          * @event previewrendered
32450          * Fire when preview rendered
32451          * @param {Roo.bootstrap.DocumentManager} this
32452          * @param {Object} file
32453          */
32454         "previewrendered" : true,
32455         /**
32456          */
32457         "previewResize" : true
32458         
32459     });
32460 };
32461
32462 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32463     
32464     boxes : 0,
32465     inputName : '',
32466     thumbSize : 300,
32467     multiple : true,
32468     files : false,
32469     method : 'POST',
32470     url : '',
32471     paramName : 'imageUpload',
32472     toolTipName : 'filename',
32473     fieldLabel : '',
32474     labelWidth : 4,
32475     labelAlign : 'left',
32476     editable : true,
32477     delegates : false,
32478     xhr : false, 
32479     
32480     labellg : 0,
32481     labelmd : 0,
32482     labelsm : 0,
32483     labelxs : 0,
32484     
32485     getAutoCreate : function()
32486     {   
32487         var managerWidget = {
32488             tag : 'div',
32489             cls : 'roo-document-manager',
32490             cn : [
32491                 {
32492                     tag : 'input',
32493                     cls : 'roo-document-manager-selector',
32494                     type : 'file'
32495                 },
32496                 {
32497                     tag : 'div',
32498                     cls : 'roo-document-manager-uploader',
32499                     cn : [
32500                         {
32501                             tag : 'div',
32502                             cls : 'roo-document-manager-upload-btn',
32503                             html : '<i class="fa fa-plus"></i>'
32504                         }
32505                     ]
32506                     
32507                 }
32508             ]
32509         };
32510         
32511         var content = [
32512             {
32513                 tag : 'div',
32514                 cls : 'column col-md-12',
32515                 cn : managerWidget
32516             }
32517         ];
32518         
32519         if(this.fieldLabel.length){
32520             
32521             content = [
32522                 {
32523                     tag : 'div',
32524                     cls : 'column col-md-12',
32525                     html : this.fieldLabel
32526                 },
32527                 {
32528                     tag : 'div',
32529                     cls : 'column col-md-12',
32530                     cn : managerWidget
32531                 }
32532             ];
32533
32534             if(this.labelAlign == 'left'){
32535                 content = [
32536                     {
32537                         tag : 'div',
32538                         cls : 'column',
32539                         html : this.fieldLabel
32540                     },
32541                     {
32542                         tag : 'div',
32543                         cls : 'column',
32544                         cn : managerWidget
32545                     }
32546                 ];
32547                 
32548                 if(this.labelWidth > 12){
32549                     content[0].style = "width: " + this.labelWidth + 'px';
32550                 }
32551
32552                 if(this.labelWidth < 13 && this.labelmd == 0){
32553                     this.labelmd = this.labelWidth;
32554                 }
32555
32556                 if(this.labellg > 0){
32557                     content[0].cls += ' col-lg-' + this.labellg;
32558                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32559                 }
32560
32561                 if(this.labelmd > 0){
32562                     content[0].cls += ' col-md-' + this.labelmd;
32563                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32564                 }
32565
32566                 if(this.labelsm > 0){
32567                     content[0].cls += ' col-sm-' + this.labelsm;
32568                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32569                 }
32570
32571                 if(this.labelxs > 0){
32572                     content[0].cls += ' col-xs-' + this.labelxs;
32573                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32574                 }
32575                 
32576             }
32577         }
32578         
32579         var cfg = {
32580             tag : 'div',
32581             cls : 'row clearfix',
32582             cn : content
32583         };
32584         
32585         return cfg;
32586         
32587     },
32588     
32589     initEvents : function()
32590     {
32591         this.managerEl = this.el.select('.roo-document-manager', true).first();
32592         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32593         
32594         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32595         this.selectorEl.hide();
32596         
32597         if(this.multiple){
32598             this.selectorEl.attr('multiple', 'multiple');
32599         }
32600         
32601         this.selectorEl.on('change', this.onFileSelected, this);
32602         
32603         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32604         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32605         
32606         this.uploader.on('click', this.onUploaderClick, this);
32607         
32608         this.renderProgressDialog();
32609         
32610         var _this = this;
32611         
32612         window.addEventListener("resize", function() { _this.refresh(); } );
32613         
32614         this.fireEvent('initial', this);
32615     },
32616     
32617     renderProgressDialog : function()
32618     {
32619         var _this = this;
32620         
32621         this.progressDialog = new Roo.bootstrap.Modal({
32622             cls : 'roo-document-manager-progress-dialog',
32623             allow_close : false,
32624             animate : false,
32625             title : '',
32626             buttons : [
32627                 {
32628                     name  :'cancel',
32629                     weight : 'danger',
32630                     html : 'Cancel'
32631                 }
32632             ], 
32633             listeners : { 
32634                 btnclick : function() {
32635                     _this.uploadCancel();
32636                     this.hide();
32637                 }
32638             }
32639         });
32640          
32641         this.progressDialog.render(Roo.get(document.body));
32642          
32643         this.progress = new Roo.bootstrap.Progress({
32644             cls : 'roo-document-manager-progress',
32645             active : true,
32646             striped : true
32647         });
32648         
32649         this.progress.render(this.progressDialog.getChildContainer());
32650         
32651         this.progressBar = new Roo.bootstrap.ProgressBar({
32652             cls : 'roo-document-manager-progress-bar',
32653             aria_valuenow : 0,
32654             aria_valuemin : 0,
32655             aria_valuemax : 12,
32656             panel : 'success'
32657         });
32658         
32659         this.progressBar.render(this.progress.getChildContainer());
32660     },
32661     
32662     onUploaderClick : function(e)
32663     {
32664         e.preventDefault();
32665      
32666         if(this.fireEvent('beforeselectfile', this) != false){
32667             this.selectorEl.dom.click();
32668         }
32669         
32670     },
32671     
32672     onFileSelected : function(e)
32673     {
32674         e.preventDefault();
32675         
32676         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32677             return;
32678         }
32679         
32680         Roo.each(this.selectorEl.dom.files, function(file){
32681             if(this.fireEvent('inspect', this, file) != false){
32682                 this.files.push(file);
32683             }
32684         }, this);
32685         
32686         this.queue();
32687         
32688     },
32689     
32690     queue : function()
32691     {
32692         this.selectorEl.dom.value = '';
32693         
32694         if(!this.files || !this.files.length){
32695             return;
32696         }
32697         
32698         if(this.boxes > 0 && this.files.length > this.boxes){
32699             this.files = this.files.slice(0, this.boxes);
32700         }
32701         
32702         this.uploader.show();
32703         
32704         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32705             this.uploader.hide();
32706         }
32707         
32708         var _this = this;
32709         
32710         var files = [];
32711         
32712         var docs = [];
32713         
32714         Roo.each(this.files, function(file){
32715             
32716             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32717                 var f = this.renderPreview(file);
32718                 files.push(f);
32719                 return;
32720             }
32721             
32722             if(file.type.indexOf('image') != -1){
32723                 this.delegates.push(
32724                     (function(){
32725                         _this.process(file);
32726                     }).createDelegate(this)
32727                 );
32728         
32729                 return;
32730             }
32731             
32732             docs.push(
32733                 (function(){
32734                     _this.process(file);
32735                 }).createDelegate(this)
32736             );
32737             
32738         }, this);
32739         
32740         this.files = files;
32741         
32742         this.delegates = this.delegates.concat(docs);
32743         
32744         if(!this.delegates.length){
32745             this.refresh();
32746             return;
32747         }
32748         
32749         this.progressBar.aria_valuemax = this.delegates.length;
32750         
32751         this.arrange();
32752         
32753         return;
32754     },
32755     
32756     arrange : function()
32757     {
32758         if(!this.delegates.length){
32759             this.progressDialog.hide();
32760             this.refresh();
32761             return;
32762         }
32763         
32764         var delegate = this.delegates.shift();
32765         
32766         this.progressDialog.show();
32767         
32768         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32769         
32770         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32771         
32772         delegate();
32773     },
32774     
32775     refresh : function()
32776     {
32777         this.uploader.show();
32778         
32779         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32780             this.uploader.hide();
32781         }
32782         
32783         Roo.isTouch ? this.closable(false) : this.closable(true);
32784         
32785         this.fireEvent('refresh', this);
32786     },
32787     
32788     onRemove : function(e, el, o)
32789     {
32790         e.preventDefault();
32791         
32792         this.fireEvent('remove', this, o);
32793         
32794     },
32795     
32796     remove : function(o)
32797     {
32798         var files = [];
32799         
32800         Roo.each(this.files, function(file){
32801             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32802                 files.push(file);
32803                 return;
32804             }
32805
32806             o.target.remove();
32807
32808         }, this);
32809         
32810         this.files = files;
32811         
32812         this.refresh();
32813     },
32814     
32815     clear : function()
32816     {
32817         Roo.each(this.files, function(file){
32818             if(!file.target){
32819                 return;
32820             }
32821             
32822             file.target.remove();
32823
32824         }, this);
32825         
32826         this.files = [];
32827         
32828         this.refresh();
32829     },
32830     
32831     onClick : function(e, el, o)
32832     {
32833         e.preventDefault();
32834         
32835         this.fireEvent('click', this, o);
32836         
32837     },
32838     
32839     closable : function(closable)
32840     {
32841         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32842             
32843             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32844             
32845             if(closable){
32846                 el.show();
32847                 return;
32848             }
32849             
32850             el.hide();
32851             
32852         }, this);
32853     },
32854     
32855     xhrOnLoad : function(xhr)
32856     {
32857         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32858             el.remove();
32859         }, this);
32860         
32861         if (xhr.readyState !== 4) {
32862             this.arrange();
32863             this.fireEvent('exception', this, xhr);
32864             return;
32865         }
32866
32867         var response = Roo.decode(xhr.responseText);
32868         
32869         if(!response.success){
32870             this.arrange();
32871             this.fireEvent('exception', this, xhr);
32872             return;
32873         }
32874         
32875         var file = this.renderPreview(response.data);
32876         
32877         this.files.push(file);
32878         
32879         this.arrange();
32880         
32881         this.fireEvent('afterupload', this, xhr);
32882         
32883     },
32884     
32885     xhrOnError : function(xhr)
32886     {
32887         Roo.log('xhr on error');
32888         
32889         var response = Roo.decode(xhr.responseText);
32890           
32891         Roo.log(response);
32892         
32893         this.arrange();
32894     },
32895     
32896     process : function(file)
32897     {
32898         if(this.fireEvent('process', this, file) !== false){
32899             if(this.editable && file.type.indexOf('image') != -1){
32900                 this.fireEvent('edit', this, file);
32901                 return;
32902             }
32903
32904             this.uploadStart(file, false);
32905
32906             return;
32907         }
32908         
32909     },
32910     
32911     uploadStart : function(file, crop)
32912     {
32913         this.xhr = new XMLHttpRequest();
32914         
32915         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32916             this.arrange();
32917             return;
32918         }
32919         
32920         file.xhr = this.xhr;
32921             
32922         this.managerEl.createChild({
32923             tag : 'div',
32924             cls : 'roo-document-manager-loading',
32925             cn : [
32926                 {
32927                     tag : 'div',
32928                     tooltip : file.name,
32929                     cls : 'roo-document-manager-thumb',
32930                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32931                 }
32932             ]
32933
32934         });
32935
32936         this.xhr.open(this.method, this.url, true);
32937         
32938         var headers = {
32939             "Accept": "application/json",
32940             "Cache-Control": "no-cache",
32941             "X-Requested-With": "XMLHttpRequest"
32942         };
32943         
32944         for (var headerName in headers) {
32945             var headerValue = headers[headerName];
32946             if (headerValue) {
32947                 this.xhr.setRequestHeader(headerName, headerValue);
32948             }
32949         }
32950         
32951         var _this = this;
32952         
32953         this.xhr.onload = function()
32954         {
32955             _this.xhrOnLoad(_this.xhr);
32956         }
32957         
32958         this.xhr.onerror = function()
32959         {
32960             _this.xhrOnError(_this.xhr);
32961         }
32962         
32963         var formData = new FormData();
32964
32965         formData.append('returnHTML', 'NO');
32966         
32967         if(crop){
32968             formData.append('crop', crop);
32969         }
32970         
32971         formData.append(this.paramName, file, file.name);
32972         
32973         var options = {
32974             file : file, 
32975             manually : false
32976         };
32977         
32978         if(this.fireEvent('prepare', this, formData, options) != false){
32979             
32980             if(options.manually){
32981                 return;
32982             }
32983             
32984             this.xhr.send(formData);
32985             return;
32986         };
32987         
32988         this.uploadCancel();
32989     },
32990     
32991     uploadCancel : function()
32992     {
32993         if (this.xhr) {
32994             this.xhr.abort();
32995         }
32996         
32997         this.delegates = [];
32998         
32999         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33000             el.remove();
33001         }, this);
33002         
33003         this.arrange();
33004     },
33005     
33006     renderPreview : function(file)
33007     {
33008         if(typeof(file.target) != 'undefined' && file.target){
33009             return file;
33010         }
33011         
33012         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33013         
33014         var previewEl = this.managerEl.createChild({
33015             tag : 'div',
33016             cls : 'roo-document-manager-preview',
33017             cn : [
33018                 {
33019                     tag : 'div',
33020                     tooltip : file[this.toolTipName],
33021                     cls : 'roo-document-manager-thumb',
33022                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33023                 },
33024                 {
33025                     tag : 'button',
33026                     cls : 'close',
33027                     html : '<i class="fa fa-times-circle"></i>'
33028                 }
33029             ]
33030         });
33031
33032         var close = previewEl.select('button.close', true).first();
33033
33034         close.on('click', this.onRemove, this, file);
33035
33036         file.target = previewEl;
33037
33038         var image = previewEl.select('img', true).first();
33039         
33040         var _this = this;
33041         
33042         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33043         
33044         image.on('click', this.onClick, this, file);
33045         
33046         this.fireEvent('previewrendered', this, file);
33047         
33048         return file;
33049         
33050     },
33051     
33052     onPreviewLoad : function(file, image)
33053     {
33054         if(typeof(file.target) == 'undefined' || !file.target){
33055             return;
33056         }
33057         
33058         var width = image.dom.naturalWidth || image.dom.width;
33059         var height = image.dom.naturalHeight || image.dom.height;
33060         
33061         if(!this.previewResize) {
33062             return;
33063         }
33064         
33065         if(width > height){
33066             file.target.addClass('wide');
33067             return;
33068         }
33069         
33070         file.target.addClass('tall');
33071         return;
33072         
33073     },
33074     
33075     uploadFromSource : function(file, crop)
33076     {
33077         this.xhr = new XMLHttpRequest();
33078         
33079         this.managerEl.createChild({
33080             tag : 'div',
33081             cls : 'roo-document-manager-loading',
33082             cn : [
33083                 {
33084                     tag : 'div',
33085                     tooltip : file.name,
33086                     cls : 'roo-document-manager-thumb',
33087                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33088                 }
33089             ]
33090
33091         });
33092
33093         this.xhr.open(this.method, this.url, true);
33094         
33095         var headers = {
33096             "Accept": "application/json",
33097             "Cache-Control": "no-cache",
33098             "X-Requested-With": "XMLHttpRequest"
33099         };
33100         
33101         for (var headerName in headers) {
33102             var headerValue = headers[headerName];
33103             if (headerValue) {
33104                 this.xhr.setRequestHeader(headerName, headerValue);
33105             }
33106         }
33107         
33108         var _this = this;
33109         
33110         this.xhr.onload = function()
33111         {
33112             _this.xhrOnLoad(_this.xhr);
33113         }
33114         
33115         this.xhr.onerror = function()
33116         {
33117             _this.xhrOnError(_this.xhr);
33118         }
33119         
33120         var formData = new FormData();
33121
33122         formData.append('returnHTML', 'NO');
33123         
33124         formData.append('crop', crop);
33125         
33126         if(typeof(file.filename) != 'undefined'){
33127             formData.append('filename', file.filename);
33128         }
33129         
33130         if(typeof(file.mimetype) != 'undefined'){
33131             formData.append('mimetype', file.mimetype);
33132         }
33133         
33134         Roo.log(formData);
33135         
33136         if(this.fireEvent('prepare', this, formData) != false){
33137             this.xhr.send(formData);
33138         };
33139     }
33140 });
33141
33142 /*
33143 * Licence: LGPL
33144 */
33145
33146 /**
33147  * @class Roo.bootstrap.DocumentViewer
33148  * @extends Roo.bootstrap.Component
33149  * Bootstrap DocumentViewer class
33150  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33151  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33152  * 
33153  * @constructor
33154  * Create a new DocumentViewer
33155  * @param {Object} config The config object
33156  */
33157
33158 Roo.bootstrap.DocumentViewer = function(config){
33159     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33160     
33161     this.addEvents({
33162         /**
33163          * @event initial
33164          * Fire after initEvent
33165          * @param {Roo.bootstrap.DocumentViewer} this
33166          */
33167         "initial" : true,
33168         /**
33169          * @event click
33170          * Fire after click
33171          * @param {Roo.bootstrap.DocumentViewer} this
33172          */
33173         "click" : true,
33174         /**
33175          * @event download
33176          * Fire after download button
33177          * @param {Roo.bootstrap.DocumentViewer} this
33178          */
33179         "download" : true,
33180         /**
33181          * @event trash
33182          * Fire after trash button
33183          * @param {Roo.bootstrap.DocumentViewer} this
33184          */
33185         "trash" : true
33186         
33187     });
33188 };
33189
33190 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33191     
33192     showDownload : true,
33193     
33194     showTrash : true,
33195     
33196     getAutoCreate : function()
33197     {
33198         var cfg = {
33199             tag : 'div',
33200             cls : 'roo-document-viewer',
33201             cn : [
33202                 {
33203                     tag : 'div',
33204                     cls : 'roo-document-viewer-body',
33205                     cn : [
33206                         {
33207                             tag : 'div',
33208                             cls : 'roo-document-viewer-thumb',
33209                             cn : [
33210                                 {
33211                                     tag : 'img',
33212                                     cls : 'roo-document-viewer-image'
33213                                 }
33214                             ]
33215                         }
33216                     ]
33217                 },
33218                 {
33219                     tag : 'div',
33220                     cls : 'roo-document-viewer-footer',
33221                     cn : {
33222                         tag : 'div',
33223                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33224                         cn : [
33225                             {
33226                                 tag : 'div',
33227                                 cls : 'btn-group roo-document-viewer-download',
33228                                 cn : [
33229                                     {
33230                                         tag : 'button',
33231                                         cls : 'btn btn-default',
33232                                         html : '<i class="fa fa-download"></i>'
33233                                     }
33234                                 ]
33235                             },
33236                             {
33237                                 tag : 'div',
33238                                 cls : 'btn-group roo-document-viewer-trash',
33239                                 cn : [
33240                                     {
33241                                         tag : 'button',
33242                                         cls : 'btn btn-default',
33243                                         html : '<i class="fa fa-trash"></i>'
33244                                     }
33245                                 ]
33246                             }
33247                         ]
33248                     }
33249                 }
33250             ]
33251         };
33252         
33253         return cfg;
33254     },
33255     
33256     initEvents : function()
33257     {
33258         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33259         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33260         
33261         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33262         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33263         
33264         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33265         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33266         
33267         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33268         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33269         
33270         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33271         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33272         
33273         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33274         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33275         
33276         this.bodyEl.on('click', this.onClick, this);
33277         this.downloadBtn.on('click', this.onDownload, this);
33278         this.trashBtn.on('click', this.onTrash, this);
33279         
33280         this.downloadBtn.hide();
33281         this.trashBtn.hide();
33282         
33283         if(this.showDownload){
33284             this.downloadBtn.show();
33285         }
33286         
33287         if(this.showTrash){
33288             this.trashBtn.show();
33289         }
33290         
33291         if(!this.showDownload && !this.showTrash) {
33292             this.footerEl.hide();
33293         }
33294         
33295     },
33296     
33297     initial : function()
33298     {
33299         this.fireEvent('initial', this);
33300         
33301     },
33302     
33303     onClick : function(e)
33304     {
33305         e.preventDefault();
33306         
33307         this.fireEvent('click', this);
33308     },
33309     
33310     onDownload : function(e)
33311     {
33312         e.preventDefault();
33313         
33314         this.fireEvent('download', this);
33315     },
33316     
33317     onTrash : function(e)
33318     {
33319         e.preventDefault();
33320         
33321         this.fireEvent('trash', this);
33322     }
33323     
33324 });
33325 /*
33326  * - LGPL
33327  *
33328  * nav progress bar
33329  * 
33330  */
33331
33332 /**
33333  * @class Roo.bootstrap.NavProgressBar
33334  * @extends Roo.bootstrap.Component
33335  * Bootstrap NavProgressBar class
33336  * 
33337  * @constructor
33338  * Create a new nav progress bar
33339  * @param {Object} config The config object
33340  */
33341
33342 Roo.bootstrap.NavProgressBar = function(config){
33343     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33344
33345     this.bullets = this.bullets || [];
33346    
33347 //    Roo.bootstrap.NavProgressBar.register(this);
33348      this.addEvents({
33349         /**
33350              * @event changed
33351              * Fires when the active item changes
33352              * @param {Roo.bootstrap.NavProgressBar} this
33353              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33354              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33355          */
33356         'changed': true
33357      });
33358     
33359 };
33360
33361 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33362     
33363     bullets : [],
33364     barItems : [],
33365     
33366     getAutoCreate : function()
33367     {
33368         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33369         
33370         cfg = {
33371             tag : 'div',
33372             cls : 'roo-navigation-bar-group',
33373             cn : [
33374                 {
33375                     tag : 'div',
33376                     cls : 'roo-navigation-top-bar'
33377                 },
33378                 {
33379                     tag : 'div',
33380                     cls : 'roo-navigation-bullets-bar',
33381                     cn : [
33382                         {
33383                             tag : 'ul',
33384                             cls : 'roo-navigation-bar'
33385                         }
33386                     ]
33387                 },
33388                 
33389                 {
33390                     tag : 'div',
33391                     cls : 'roo-navigation-bottom-bar'
33392                 }
33393             ]
33394             
33395         };
33396         
33397         return cfg;
33398         
33399     },
33400     
33401     initEvents: function() 
33402     {
33403         
33404     },
33405     
33406     onRender : function(ct, position) 
33407     {
33408         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33409         
33410         if(this.bullets.length){
33411             Roo.each(this.bullets, function(b){
33412                this.addItem(b);
33413             }, this);
33414         }
33415         
33416         this.format();
33417         
33418     },
33419     
33420     addItem : function(cfg)
33421     {
33422         var item = new Roo.bootstrap.NavProgressItem(cfg);
33423         
33424         item.parentId = this.id;
33425         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33426         
33427         if(cfg.html){
33428             var top = new Roo.bootstrap.Element({
33429                 tag : 'div',
33430                 cls : 'roo-navigation-bar-text'
33431             });
33432             
33433             var bottom = new Roo.bootstrap.Element({
33434                 tag : 'div',
33435                 cls : 'roo-navigation-bar-text'
33436             });
33437             
33438             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33439             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33440             
33441             var topText = new Roo.bootstrap.Element({
33442                 tag : 'span',
33443                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33444             });
33445             
33446             var bottomText = new Roo.bootstrap.Element({
33447                 tag : 'span',
33448                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33449             });
33450             
33451             topText.onRender(top.el, null);
33452             bottomText.onRender(bottom.el, null);
33453             
33454             item.topEl = top;
33455             item.bottomEl = bottom;
33456         }
33457         
33458         this.barItems.push(item);
33459         
33460         return item;
33461     },
33462     
33463     getActive : function()
33464     {
33465         var active = false;
33466         
33467         Roo.each(this.barItems, function(v){
33468             
33469             if (!v.isActive()) {
33470                 return;
33471             }
33472             
33473             active = v;
33474             return false;
33475             
33476         });
33477         
33478         return active;
33479     },
33480     
33481     setActiveItem : function(item)
33482     {
33483         var prev = false;
33484         
33485         Roo.each(this.barItems, function(v){
33486             if (v.rid == item.rid) {
33487                 return ;
33488             }
33489             
33490             if (v.isActive()) {
33491                 v.setActive(false);
33492                 prev = v;
33493             }
33494         });
33495
33496         item.setActive(true);
33497         
33498         this.fireEvent('changed', this, item, prev);
33499     },
33500     
33501     getBarItem: function(rid)
33502     {
33503         var ret = false;
33504         
33505         Roo.each(this.barItems, function(e) {
33506             if (e.rid != rid) {
33507                 return;
33508             }
33509             
33510             ret =  e;
33511             return false;
33512         });
33513         
33514         return ret;
33515     },
33516     
33517     indexOfItem : function(item)
33518     {
33519         var index = false;
33520         
33521         Roo.each(this.barItems, function(v, i){
33522             
33523             if (v.rid != item.rid) {
33524                 return;
33525             }
33526             
33527             index = i;
33528             return false
33529         });
33530         
33531         return index;
33532     },
33533     
33534     setActiveNext : function()
33535     {
33536         var i = this.indexOfItem(this.getActive());
33537         
33538         if (i > this.barItems.length) {
33539             return;
33540         }
33541         
33542         this.setActiveItem(this.barItems[i+1]);
33543     },
33544     
33545     setActivePrev : function()
33546     {
33547         var i = this.indexOfItem(this.getActive());
33548         
33549         if (i  < 1) {
33550             return;
33551         }
33552         
33553         this.setActiveItem(this.barItems[i-1]);
33554     },
33555     
33556     format : function()
33557     {
33558         if(!this.barItems.length){
33559             return;
33560         }
33561      
33562         var width = 100 / this.barItems.length;
33563         
33564         Roo.each(this.barItems, function(i){
33565             i.el.setStyle('width', width + '%');
33566             i.topEl.el.setStyle('width', width + '%');
33567             i.bottomEl.el.setStyle('width', width + '%');
33568         }, this);
33569         
33570     }
33571     
33572 });
33573 /*
33574  * - LGPL
33575  *
33576  * Nav Progress Item
33577  * 
33578  */
33579
33580 /**
33581  * @class Roo.bootstrap.NavProgressItem
33582  * @extends Roo.bootstrap.Component
33583  * Bootstrap NavProgressItem class
33584  * @cfg {String} rid the reference id
33585  * @cfg {Boolean} active (true|false) Is item active default false
33586  * @cfg {Boolean} disabled (true|false) Is item active default false
33587  * @cfg {String} html
33588  * @cfg {String} position (top|bottom) text position default bottom
33589  * @cfg {String} icon show icon instead of number
33590  * 
33591  * @constructor
33592  * Create a new NavProgressItem
33593  * @param {Object} config The config object
33594  */
33595 Roo.bootstrap.NavProgressItem = function(config){
33596     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33597     this.addEvents({
33598         // raw events
33599         /**
33600          * @event click
33601          * The raw click event for the entire grid.
33602          * @param {Roo.bootstrap.NavProgressItem} this
33603          * @param {Roo.EventObject} e
33604          */
33605         "click" : true
33606     });
33607    
33608 };
33609
33610 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33611     
33612     rid : '',
33613     active : false,
33614     disabled : false,
33615     html : '',
33616     position : 'bottom',
33617     icon : false,
33618     
33619     getAutoCreate : function()
33620     {
33621         var iconCls = 'roo-navigation-bar-item-icon';
33622         
33623         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33624         
33625         var cfg = {
33626             tag: 'li',
33627             cls: 'roo-navigation-bar-item',
33628             cn : [
33629                 {
33630                     tag : 'i',
33631                     cls : iconCls
33632                 }
33633             ]
33634         };
33635         
33636         if(this.active){
33637             cfg.cls += ' active';
33638         }
33639         if(this.disabled){
33640             cfg.cls += ' disabled';
33641         }
33642         
33643         return cfg;
33644     },
33645     
33646     disable : function()
33647     {
33648         this.setDisabled(true);
33649     },
33650     
33651     enable : function()
33652     {
33653         this.setDisabled(false);
33654     },
33655     
33656     initEvents: function() 
33657     {
33658         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33659         
33660         this.iconEl.on('click', this.onClick, this);
33661     },
33662     
33663     onClick : function(e)
33664     {
33665         e.preventDefault();
33666         
33667         if(this.disabled){
33668             return;
33669         }
33670         
33671         if(this.fireEvent('click', this, e) === false){
33672             return;
33673         };
33674         
33675         this.parent().setActiveItem(this);
33676     },
33677     
33678     isActive: function () 
33679     {
33680         return this.active;
33681     },
33682     
33683     setActive : function(state)
33684     {
33685         if(this.active == state){
33686             return;
33687         }
33688         
33689         this.active = state;
33690         
33691         if (state) {
33692             this.el.addClass('active');
33693             return;
33694         }
33695         
33696         this.el.removeClass('active');
33697         
33698         return;
33699     },
33700     
33701     setDisabled : function(state)
33702     {
33703         if(this.disabled == state){
33704             return;
33705         }
33706         
33707         this.disabled = state;
33708         
33709         if (state) {
33710             this.el.addClass('disabled');
33711             return;
33712         }
33713         
33714         this.el.removeClass('disabled');
33715     },
33716     
33717     tooltipEl : function()
33718     {
33719         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33720     }
33721 });
33722  
33723
33724  /*
33725  * - LGPL
33726  *
33727  * FieldLabel
33728  * 
33729  */
33730
33731 /**
33732  * @class Roo.bootstrap.FieldLabel
33733  * @extends Roo.bootstrap.Component
33734  * Bootstrap FieldLabel class
33735  * @cfg {String} html contents of the element
33736  * @cfg {String} tag tag of the element default label
33737  * @cfg {String} cls class of the element
33738  * @cfg {String} target label target 
33739  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33740  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33741  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33742  * @cfg {String} iconTooltip default "This field is required"
33743  * @cfg {String} indicatorpos (left|right) default left
33744  * 
33745  * @constructor
33746  * Create a new FieldLabel
33747  * @param {Object} config The config object
33748  */
33749
33750 Roo.bootstrap.FieldLabel = function(config){
33751     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33752     
33753     this.addEvents({
33754             /**
33755              * @event invalid
33756              * Fires after the field has been marked as invalid.
33757              * @param {Roo.form.FieldLabel} this
33758              * @param {String} msg The validation message
33759              */
33760             invalid : true,
33761             /**
33762              * @event valid
33763              * Fires after the field has been validated with no errors.
33764              * @param {Roo.form.FieldLabel} this
33765              */
33766             valid : true
33767         });
33768 };
33769
33770 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33771     
33772     tag: 'label',
33773     cls: '',
33774     html: '',
33775     target: '',
33776     allowBlank : true,
33777     invalidClass : 'has-warning',
33778     validClass : 'has-success',
33779     iconTooltip : 'This field is required',
33780     indicatorpos : 'left',
33781     
33782     getAutoCreate : function(){
33783         
33784         var cls = "";
33785         if (!this.allowBlank) {
33786             cls  = "visible";
33787         }
33788         
33789         var cfg = {
33790             tag : this.tag,
33791             cls : 'roo-bootstrap-field-label ' + this.cls,
33792             for : this.target,
33793             cn : [
33794                 {
33795                     tag : 'i',
33796                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33797                     tooltip : this.iconTooltip
33798                 },
33799                 {
33800                     tag : 'span',
33801                     html : this.html
33802                 }
33803             ] 
33804         };
33805         
33806         if(this.indicatorpos == 'right'){
33807             var cfg = {
33808                 tag : this.tag,
33809                 cls : 'roo-bootstrap-field-label ' + this.cls,
33810                 for : this.target,
33811                 cn : [
33812                     {
33813                         tag : 'span',
33814                         html : this.html
33815                     },
33816                     {
33817                         tag : 'i',
33818                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33819                         tooltip : this.iconTooltip
33820                     }
33821                 ] 
33822             };
33823         }
33824         
33825         return cfg;
33826     },
33827     
33828     initEvents: function() 
33829     {
33830         Roo.bootstrap.Element.superclass.initEvents.call(this);
33831         
33832         this.indicator = this.indicatorEl();
33833         
33834         if(this.indicator){
33835             this.indicator.removeClass('visible');
33836             this.indicator.addClass('invisible');
33837         }
33838         
33839         Roo.bootstrap.FieldLabel.register(this);
33840     },
33841     
33842     indicatorEl : function()
33843     {
33844         var indicator = this.el.select('i.roo-required-indicator',true).first();
33845         
33846         if(!indicator){
33847             return false;
33848         }
33849         
33850         return indicator;
33851         
33852     },
33853     
33854     /**
33855      * Mark this field as valid
33856      */
33857     markValid : function()
33858     {
33859         if(this.indicator){
33860             this.indicator.removeClass('visible');
33861             this.indicator.addClass('invisible');
33862         }
33863         if (Roo.bootstrap.version == 3) {
33864             this.el.removeClass(this.invalidClass);
33865             this.el.addClass(this.validClass);
33866         } else {
33867             this.el.removeClass('is-invalid');
33868             this.el.addClass('is-valid');
33869         }
33870         
33871         
33872         this.fireEvent('valid', this);
33873     },
33874     
33875     /**
33876      * Mark this field as invalid
33877      * @param {String} msg The validation message
33878      */
33879     markInvalid : function(msg)
33880     {
33881         if(this.indicator){
33882             this.indicator.removeClass('invisible');
33883             this.indicator.addClass('visible');
33884         }
33885           if (Roo.bootstrap.version == 3) {
33886             this.el.removeClass(this.validClass);
33887             this.el.addClass(this.invalidClass);
33888         } else {
33889             this.el.removeClass('is-valid');
33890             this.el.addClass('is-invalid');
33891         }
33892         
33893         
33894         this.fireEvent('invalid', this, msg);
33895     }
33896     
33897    
33898 });
33899
33900 Roo.apply(Roo.bootstrap.FieldLabel, {
33901     
33902     groups: {},
33903     
33904      /**
33905     * register a FieldLabel Group
33906     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33907     */
33908     register : function(label)
33909     {
33910         if(this.groups.hasOwnProperty(label.target)){
33911             return;
33912         }
33913      
33914         this.groups[label.target] = label;
33915         
33916     },
33917     /**
33918     * fetch a FieldLabel Group based on the target
33919     * @param {string} target
33920     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33921     */
33922     get: function(target) {
33923         if (typeof(this.groups[target]) == 'undefined') {
33924             return false;
33925         }
33926         
33927         return this.groups[target] ;
33928     }
33929 });
33930
33931  
33932
33933  /*
33934  * - LGPL
33935  *
33936  * page DateSplitField.
33937  * 
33938  */
33939
33940
33941 /**
33942  * @class Roo.bootstrap.DateSplitField
33943  * @extends Roo.bootstrap.Component
33944  * Bootstrap DateSplitField class
33945  * @cfg {string} fieldLabel - the label associated
33946  * @cfg {Number} labelWidth set the width of label (0-12)
33947  * @cfg {String} labelAlign (top|left)
33948  * @cfg {Boolean} dayAllowBlank (true|false) default false
33949  * @cfg {Boolean} monthAllowBlank (true|false) default false
33950  * @cfg {Boolean} yearAllowBlank (true|false) default false
33951  * @cfg {string} dayPlaceholder 
33952  * @cfg {string} monthPlaceholder
33953  * @cfg {string} yearPlaceholder
33954  * @cfg {string} dayFormat default 'd'
33955  * @cfg {string} monthFormat default 'm'
33956  * @cfg {string} yearFormat default 'Y'
33957  * @cfg {Number} labellg set the width of label (1-12)
33958  * @cfg {Number} labelmd set the width of label (1-12)
33959  * @cfg {Number} labelsm set the width of label (1-12)
33960  * @cfg {Number} labelxs set the width of label (1-12)
33961
33962  *     
33963  * @constructor
33964  * Create a new DateSplitField
33965  * @param {Object} config The config object
33966  */
33967
33968 Roo.bootstrap.DateSplitField = function(config){
33969     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33970     
33971     this.addEvents({
33972         // raw events
33973          /**
33974          * @event years
33975          * getting the data of years
33976          * @param {Roo.bootstrap.DateSplitField} this
33977          * @param {Object} years
33978          */
33979         "years" : true,
33980         /**
33981          * @event days
33982          * getting the data of days
33983          * @param {Roo.bootstrap.DateSplitField} this
33984          * @param {Object} days
33985          */
33986         "days" : true,
33987         /**
33988          * @event invalid
33989          * Fires after the field has been marked as invalid.
33990          * @param {Roo.form.Field} this
33991          * @param {String} msg The validation message
33992          */
33993         invalid : true,
33994        /**
33995          * @event valid
33996          * Fires after the field has been validated with no errors.
33997          * @param {Roo.form.Field} this
33998          */
33999         valid : true
34000     });
34001 };
34002
34003 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34004     
34005     fieldLabel : '',
34006     labelAlign : 'top',
34007     labelWidth : 3,
34008     dayAllowBlank : false,
34009     monthAllowBlank : false,
34010     yearAllowBlank : false,
34011     dayPlaceholder : '',
34012     monthPlaceholder : '',
34013     yearPlaceholder : '',
34014     dayFormat : 'd',
34015     monthFormat : 'm',
34016     yearFormat : 'Y',
34017     isFormField : true,
34018     labellg : 0,
34019     labelmd : 0,
34020     labelsm : 0,
34021     labelxs : 0,
34022     
34023     getAutoCreate : function()
34024     {
34025         var cfg = {
34026             tag : 'div',
34027             cls : 'row roo-date-split-field-group',
34028             cn : [
34029                 {
34030                     tag : 'input',
34031                     type : 'hidden',
34032                     cls : 'form-hidden-field roo-date-split-field-group-value',
34033                     name : this.name
34034                 }
34035             ]
34036         };
34037         
34038         var labelCls = 'col-md-12';
34039         var contentCls = 'col-md-4';
34040         
34041         if(this.fieldLabel){
34042             
34043             var label = {
34044                 tag : 'div',
34045                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34046                 cn : [
34047                     {
34048                         tag : 'label',
34049                         html : this.fieldLabel
34050                     }
34051                 ]
34052             };
34053             
34054             if(this.labelAlign == 'left'){
34055             
34056                 if(this.labelWidth > 12){
34057                     label.style = "width: " + this.labelWidth + 'px';
34058                 }
34059
34060                 if(this.labelWidth < 13 && this.labelmd == 0){
34061                     this.labelmd = this.labelWidth;
34062                 }
34063
34064                 if(this.labellg > 0){
34065                     labelCls = ' col-lg-' + this.labellg;
34066                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34067                 }
34068
34069                 if(this.labelmd > 0){
34070                     labelCls = ' col-md-' + this.labelmd;
34071                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34072                 }
34073
34074                 if(this.labelsm > 0){
34075                     labelCls = ' col-sm-' + this.labelsm;
34076                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34077                 }
34078
34079                 if(this.labelxs > 0){
34080                     labelCls = ' col-xs-' + this.labelxs;
34081                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34082                 }
34083             }
34084             
34085             label.cls += ' ' + labelCls;
34086             
34087             cfg.cn.push(label);
34088         }
34089         
34090         Roo.each(['day', 'month', 'year'], function(t){
34091             cfg.cn.push({
34092                 tag : 'div',
34093                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34094             });
34095         }, this);
34096         
34097         return cfg;
34098     },
34099     
34100     inputEl: function ()
34101     {
34102         return this.el.select('.roo-date-split-field-group-value', true).first();
34103     },
34104     
34105     onRender : function(ct, position) 
34106     {
34107         var _this = this;
34108         
34109         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34110         
34111         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34112         
34113         this.dayField = new Roo.bootstrap.ComboBox({
34114             allowBlank : this.dayAllowBlank,
34115             alwaysQuery : true,
34116             displayField : 'value',
34117             editable : false,
34118             fieldLabel : '',
34119             forceSelection : true,
34120             mode : 'local',
34121             placeholder : this.dayPlaceholder,
34122             selectOnFocus : true,
34123             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34124             triggerAction : 'all',
34125             typeAhead : true,
34126             valueField : 'value',
34127             store : new Roo.data.SimpleStore({
34128                 data : (function() {    
34129                     var days = [];
34130                     _this.fireEvent('days', _this, days);
34131                     return days;
34132                 })(),
34133                 fields : [ 'value' ]
34134             }),
34135             listeners : {
34136                 select : function (_self, record, index)
34137                 {
34138                     _this.setValue(_this.getValue());
34139                 }
34140             }
34141         });
34142
34143         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34144         
34145         this.monthField = new Roo.bootstrap.MonthField({
34146             after : '<i class=\"fa fa-calendar\"></i>',
34147             allowBlank : this.monthAllowBlank,
34148             placeholder : this.monthPlaceholder,
34149             readOnly : true,
34150             listeners : {
34151                 render : function (_self)
34152                 {
34153                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34154                         e.preventDefault();
34155                         _self.focus();
34156                     });
34157                 },
34158                 select : function (_self, oldvalue, newvalue)
34159                 {
34160                     _this.setValue(_this.getValue());
34161                 }
34162             }
34163         });
34164         
34165         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34166         
34167         this.yearField = new Roo.bootstrap.ComboBox({
34168             allowBlank : this.yearAllowBlank,
34169             alwaysQuery : true,
34170             displayField : 'value',
34171             editable : false,
34172             fieldLabel : '',
34173             forceSelection : true,
34174             mode : 'local',
34175             placeholder : this.yearPlaceholder,
34176             selectOnFocus : true,
34177             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34178             triggerAction : 'all',
34179             typeAhead : true,
34180             valueField : 'value',
34181             store : new Roo.data.SimpleStore({
34182                 data : (function() {
34183                     var years = [];
34184                     _this.fireEvent('years', _this, years);
34185                     return years;
34186                 })(),
34187                 fields : [ 'value' ]
34188             }),
34189             listeners : {
34190                 select : function (_self, record, index)
34191                 {
34192                     _this.setValue(_this.getValue());
34193                 }
34194             }
34195         });
34196
34197         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34198     },
34199     
34200     setValue : function(v, format)
34201     {
34202         this.inputEl.dom.value = v;
34203         
34204         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34205         
34206         var d = Date.parseDate(v, f);
34207         
34208         if(!d){
34209             this.validate();
34210             return;
34211         }
34212         
34213         this.setDay(d.format(this.dayFormat));
34214         this.setMonth(d.format(this.monthFormat));
34215         this.setYear(d.format(this.yearFormat));
34216         
34217         this.validate();
34218         
34219         return;
34220     },
34221     
34222     setDay : function(v)
34223     {
34224         this.dayField.setValue(v);
34225         this.inputEl.dom.value = this.getValue();
34226         this.validate();
34227         return;
34228     },
34229     
34230     setMonth : function(v)
34231     {
34232         this.monthField.setValue(v, true);
34233         this.inputEl.dom.value = this.getValue();
34234         this.validate();
34235         return;
34236     },
34237     
34238     setYear : function(v)
34239     {
34240         this.yearField.setValue(v);
34241         this.inputEl.dom.value = this.getValue();
34242         this.validate();
34243         return;
34244     },
34245     
34246     getDay : function()
34247     {
34248         return this.dayField.getValue();
34249     },
34250     
34251     getMonth : function()
34252     {
34253         return this.monthField.getValue();
34254     },
34255     
34256     getYear : function()
34257     {
34258         return this.yearField.getValue();
34259     },
34260     
34261     getValue : function()
34262     {
34263         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34264         
34265         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34266         
34267         return date;
34268     },
34269     
34270     reset : function()
34271     {
34272         this.setDay('');
34273         this.setMonth('');
34274         this.setYear('');
34275         this.inputEl.dom.value = '';
34276         this.validate();
34277         return;
34278     },
34279     
34280     validate : function()
34281     {
34282         var d = this.dayField.validate();
34283         var m = this.monthField.validate();
34284         var y = this.yearField.validate();
34285         
34286         var valid = true;
34287         
34288         if(
34289                 (!this.dayAllowBlank && !d) ||
34290                 (!this.monthAllowBlank && !m) ||
34291                 (!this.yearAllowBlank && !y)
34292         ){
34293             valid = false;
34294         }
34295         
34296         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34297             return valid;
34298         }
34299         
34300         if(valid){
34301             this.markValid();
34302             return valid;
34303         }
34304         
34305         this.markInvalid();
34306         
34307         return valid;
34308     },
34309     
34310     markValid : function()
34311     {
34312         
34313         var label = this.el.select('label', true).first();
34314         var icon = this.el.select('i.fa-star', true).first();
34315
34316         if(label && icon){
34317             icon.remove();
34318         }
34319         
34320         this.fireEvent('valid', this);
34321     },
34322     
34323      /**
34324      * Mark this field as invalid
34325      * @param {String} msg The validation message
34326      */
34327     markInvalid : function(msg)
34328     {
34329         
34330         var label = this.el.select('label', true).first();
34331         var icon = this.el.select('i.fa-star', true).first();
34332
34333         if(label && !icon){
34334             this.el.select('.roo-date-split-field-label', true).createChild({
34335                 tag : 'i',
34336                 cls : 'text-danger fa fa-lg fa-star',
34337                 tooltip : 'This field is required',
34338                 style : 'margin-right:5px;'
34339             }, label, true);
34340         }
34341         
34342         this.fireEvent('invalid', this, msg);
34343     },
34344     
34345     clearInvalid : function()
34346     {
34347         var label = this.el.select('label', true).first();
34348         var icon = this.el.select('i.fa-star', true).first();
34349
34350         if(label && icon){
34351             icon.remove();
34352         }
34353         
34354         this.fireEvent('valid', this);
34355     },
34356     
34357     getName: function()
34358     {
34359         return this.name;
34360     }
34361     
34362 });
34363
34364  /**
34365  *
34366  * This is based on 
34367  * http://masonry.desandro.com
34368  *
34369  * The idea is to render all the bricks based on vertical width...
34370  *
34371  * The original code extends 'outlayer' - we might need to use that....
34372  * 
34373  */
34374
34375
34376 /**
34377  * @class Roo.bootstrap.LayoutMasonry
34378  * @extends Roo.bootstrap.Component
34379  * Bootstrap Layout Masonry class
34380  * 
34381  * @constructor
34382  * Create a new Element
34383  * @param {Object} config The config object
34384  */
34385
34386 Roo.bootstrap.LayoutMasonry = function(config){
34387     
34388     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34389     
34390     this.bricks = [];
34391     
34392     Roo.bootstrap.LayoutMasonry.register(this);
34393     
34394     this.addEvents({
34395         // raw events
34396         /**
34397          * @event layout
34398          * Fire after layout the items
34399          * @param {Roo.bootstrap.LayoutMasonry} this
34400          * @param {Roo.EventObject} e
34401          */
34402         "layout" : true
34403     });
34404     
34405 };
34406
34407 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34408     
34409     /**
34410      * @cfg {Boolean} isLayoutInstant = no animation?
34411      */   
34412     isLayoutInstant : false, // needed?
34413    
34414     /**
34415      * @cfg {Number} boxWidth  width of the columns
34416      */   
34417     boxWidth : 450,
34418     
34419       /**
34420      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34421      */   
34422     boxHeight : 0,
34423     
34424     /**
34425      * @cfg {Number} padWidth padding below box..
34426      */   
34427     padWidth : 10, 
34428     
34429     /**
34430      * @cfg {Number} gutter gutter width..
34431      */   
34432     gutter : 10,
34433     
34434      /**
34435      * @cfg {Number} maxCols maximum number of columns
34436      */   
34437     
34438     maxCols: 0,
34439     
34440     /**
34441      * @cfg {Boolean} isAutoInitial defalut true
34442      */   
34443     isAutoInitial : true, 
34444     
34445     containerWidth: 0,
34446     
34447     /**
34448      * @cfg {Boolean} isHorizontal defalut false
34449      */   
34450     isHorizontal : false, 
34451
34452     currentSize : null,
34453     
34454     tag: 'div',
34455     
34456     cls: '',
34457     
34458     bricks: null, //CompositeElement
34459     
34460     cols : 1,
34461     
34462     _isLayoutInited : false,
34463     
34464 //    isAlternative : false, // only use for vertical layout...
34465     
34466     /**
34467      * @cfg {Number} alternativePadWidth padding below box..
34468      */   
34469     alternativePadWidth : 50,
34470     
34471     selectedBrick : [],
34472     
34473     getAutoCreate : function(){
34474         
34475         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34476         
34477         var cfg = {
34478             tag: this.tag,
34479             cls: 'blog-masonary-wrapper ' + this.cls,
34480             cn : {
34481                 cls : 'mas-boxes masonary'
34482             }
34483         };
34484         
34485         return cfg;
34486     },
34487     
34488     getChildContainer: function( )
34489     {
34490         if (this.boxesEl) {
34491             return this.boxesEl;
34492         }
34493         
34494         this.boxesEl = this.el.select('.mas-boxes').first();
34495         
34496         return this.boxesEl;
34497     },
34498     
34499     
34500     initEvents : function()
34501     {
34502         var _this = this;
34503         
34504         if(this.isAutoInitial){
34505             Roo.log('hook children rendered');
34506             this.on('childrenrendered', function() {
34507                 Roo.log('children rendered');
34508                 _this.initial();
34509             } ,this);
34510         }
34511     },
34512     
34513     initial : function()
34514     {
34515         this.selectedBrick = [];
34516         
34517         this.currentSize = this.el.getBox(true);
34518         
34519         Roo.EventManager.onWindowResize(this.resize, this); 
34520
34521         if(!this.isAutoInitial){
34522             this.layout();
34523             return;
34524         }
34525         
34526         this.layout();
34527         
34528         return;
34529         //this.layout.defer(500,this);
34530         
34531     },
34532     
34533     resize : function()
34534     {
34535         var cs = this.el.getBox(true);
34536         
34537         if (
34538                 this.currentSize.width == cs.width && 
34539                 this.currentSize.x == cs.x && 
34540                 this.currentSize.height == cs.height && 
34541                 this.currentSize.y == cs.y 
34542         ) {
34543             Roo.log("no change in with or X or Y");
34544             return;
34545         }
34546         
34547         this.currentSize = cs;
34548         
34549         this.layout();
34550         
34551     },
34552     
34553     layout : function()
34554     {   
34555         this._resetLayout();
34556         
34557         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34558         
34559         this.layoutItems( isInstant );
34560       
34561         this._isLayoutInited = true;
34562         
34563         this.fireEvent('layout', this);
34564         
34565     },
34566     
34567     _resetLayout : function()
34568     {
34569         if(this.isHorizontal){
34570             this.horizontalMeasureColumns();
34571             return;
34572         }
34573         
34574         this.verticalMeasureColumns();
34575         
34576     },
34577     
34578     verticalMeasureColumns : function()
34579     {
34580         this.getContainerWidth();
34581         
34582 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34583 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34584 //            return;
34585 //        }
34586         
34587         var boxWidth = this.boxWidth + this.padWidth;
34588         
34589         if(this.containerWidth < this.boxWidth){
34590             boxWidth = this.containerWidth
34591         }
34592         
34593         var containerWidth = this.containerWidth;
34594         
34595         var cols = Math.floor(containerWidth / boxWidth);
34596         
34597         this.cols = Math.max( cols, 1 );
34598         
34599         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34600         
34601         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34602         
34603         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34604         
34605         this.colWidth = boxWidth + avail - this.padWidth;
34606         
34607         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34608         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34609     },
34610     
34611     horizontalMeasureColumns : function()
34612     {
34613         this.getContainerWidth();
34614         
34615         var boxWidth = this.boxWidth;
34616         
34617         if(this.containerWidth < boxWidth){
34618             boxWidth = this.containerWidth;
34619         }
34620         
34621         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34622         
34623         this.el.setHeight(boxWidth);
34624         
34625     },
34626     
34627     getContainerWidth : function()
34628     {
34629         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34630     },
34631     
34632     layoutItems : function( isInstant )
34633     {
34634         Roo.log(this.bricks);
34635         
34636         var items = Roo.apply([], this.bricks);
34637         
34638         if(this.isHorizontal){
34639             this._horizontalLayoutItems( items , isInstant );
34640             return;
34641         }
34642         
34643 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34644 //            this._verticalAlternativeLayoutItems( items , isInstant );
34645 //            return;
34646 //        }
34647         
34648         this._verticalLayoutItems( items , isInstant );
34649         
34650     },
34651     
34652     _verticalLayoutItems : function ( items , isInstant)
34653     {
34654         if ( !items || !items.length ) {
34655             return;
34656         }
34657         
34658         var standard = [
34659             ['xs', 'xs', 'xs', 'tall'],
34660             ['xs', 'xs', 'tall'],
34661             ['xs', 'xs', 'sm'],
34662             ['xs', 'xs', 'xs'],
34663             ['xs', 'tall'],
34664             ['xs', 'sm'],
34665             ['xs', 'xs'],
34666             ['xs'],
34667             
34668             ['sm', 'xs', 'xs'],
34669             ['sm', 'xs'],
34670             ['sm'],
34671             
34672             ['tall', 'xs', 'xs', 'xs'],
34673             ['tall', 'xs', 'xs'],
34674             ['tall', 'xs'],
34675             ['tall']
34676             
34677         ];
34678         
34679         var queue = [];
34680         
34681         var boxes = [];
34682         
34683         var box = [];
34684         
34685         Roo.each(items, function(item, k){
34686             
34687             switch (item.size) {
34688                 // these layouts take up a full box,
34689                 case 'md' :
34690                 case 'md-left' :
34691                 case 'md-right' :
34692                 case 'wide' :
34693                     
34694                     if(box.length){
34695                         boxes.push(box);
34696                         box = [];
34697                     }
34698                     
34699                     boxes.push([item]);
34700                     
34701                     break;
34702                     
34703                 case 'xs' :
34704                 case 'sm' :
34705                 case 'tall' :
34706                     
34707                     box.push(item);
34708                     
34709                     break;
34710                 default :
34711                     break;
34712                     
34713             }
34714             
34715         }, this);
34716         
34717         if(box.length){
34718             boxes.push(box);
34719             box = [];
34720         }
34721         
34722         var filterPattern = function(box, length)
34723         {
34724             if(!box.length){
34725                 return;
34726             }
34727             
34728             var match = false;
34729             
34730             var pattern = box.slice(0, length);
34731             
34732             var format = [];
34733             
34734             Roo.each(pattern, function(i){
34735                 format.push(i.size);
34736             }, this);
34737             
34738             Roo.each(standard, function(s){
34739                 
34740                 if(String(s) != String(format)){
34741                     return;
34742                 }
34743                 
34744                 match = true;
34745                 return false;
34746                 
34747             }, this);
34748             
34749             if(!match && length == 1){
34750                 return;
34751             }
34752             
34753             if(!match){
34754                 filterPattern(box, length - 1);
34755                 return;
34756             }
34757                 
34758             queue.push(pattern);
34759
34760             box = box.slice(length, box.length);
34761
34762             filterPattern(box, 4);
34763
34764             return;
34765             
34766         }
34767         
34768         Roo.each(boxes, function(box, k){
34769             
34770             if(!box.length){
34771                 return;
34772             }
34773             
34774             if(box.length == 1){
34775                 queue.push(box);
34776                 return;
34777             }
34778             
34779             filterPattern(box, 4);
34780             
34781         }, this);
34782         
34783         this._processVerticalLayoutQueue( queue, isInstant );
34784         
34785     },
34786     
34787 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34788 //    {
34789 //        if ( !items || !items.length ) {
34790 //            return;
34791 //        }
34792 //
34793 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34794 //        
34795 //    },
34796     
34797     _horizontalLayoutItems : function ( items , isInstant)
34798     {
34799         if ( !items || !items.length || items.length < 3) {
34800             return;
34801         }
34802         
34803         items.reverse();
34804         
34805         var eItems = items.slice(0, 3);
34806         
34807         items = items.slice(3, items.length);
34808         
34809         var standard = [
34810             ['xs', 'xs', 'xs', 'wide'],
34811             ['xs', 'xs', 'wide'],
34812             ['xs', 'xs', 'sm'],
34813             ['xs', 'xs', 'xs'],
34814             ['xs', 'wide'],
34815             ['xs', 'sm'],
34816             ['xs', 'xs'],
34817             ['xs'],
34818             
34819             ['sm', 'xs', 'xs'],
34820             ['sm', 'xs'],
34821             ['sm'],
34822             
34823             ['wide', 'xs', 'xs', 'xs'],
34824             ['wide', 'xs', 'xs'],
34825             ['wide', 'xs'],
34826             ['wide'],
34827             
34828             ['wide-thin']
34829         ];
34830         
34831         var queue = [];
34832         
34833         var boxes = [];
34834         
34835         var box = [];
34836         
34837         Roo.each(items, function(item, k){
34838             
34839             switch (item.size) {
34840                 case 'md' :
34841                 case 'md-left' :
34842                 case 'md-right' :
34843                 case 'tall' :
34844                     
34845                     if(box.length){
34846                         boxes.push(box);
34847                         box = [];
34848                     }
34849                     
34850                     boxes.push([item]);
34851                     
34852                     break;
34853                     
34854                 case 'xs' :
34855                 case 'sm' :
34856                 case 'wide' :
34857                 case 'wide-thin' :
34858                     
34859                     box.push(item);
34860                     
34861                     break;
34862                 default :
34863                     break;
34864                     
34865             }
34866             
34867         }, this);
34868         
34869         if(box.length){
34870             boxes.push(box);
34871             box = [];
34872         }
34873         
34874         var filterPattern = function(box, length)
34875         {
34876             if(!box.length){
34877                 return;
34878             }
34879             
34880             var match = false;
34881             
34882             var pattern = box.slice(0, length);
34883             
34884             var format = [];
34885             
34886             Roo.each(pattern, function(i){
34887                 format.push(i.size);
34888             }, this);
34889             
34890             Roo.each(standard, function(s){
34891                 
34892                 if(String(s) != String(format)){
34893                     return;
34894                 }
34895                 
34896                 match = true;
34897                 return false;
34898                 
34899             }, this);
34900             
34901             if(!match && length == 1){
34902                 return;
34903             }
34904             
34905             if(!match){
34906                 filterPattern(box, length - 1);
34907                 return;
34908             }
34909                 
34910             queue.push(pattern);
34911
34912             box = box.slice(length, box.length);
34913
34914             filterPattern(box, 4);
34915
34916             return;
34917             
34918         }
34919         
34920         Roo.each(boxes, function(box, k){
34921             
34922             if(!box.length){
34923                 return;
34924             }
34925             
34926             if(box.length == 1){
34927                 queue.push(box);
34928                 return;
34929             }
34930             
34931             filterPattern(box, 4);
34932             
34933         }, this);
34934         
34935         
34936         var prune = [];
34937         
34938         var pos = this.el.getBox(true);
34939         
34940         var minX = pos.x;
34941         
34942         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34943         
34944         var hit_end = false;
34945         
34946         Roo.each(queue, function(box){
34947             
34948             if(hit_end){
34949                 
34950                 Roo.each(box, function(b){
34951                 
34952                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34953                     b.el.hide();
34954
34955                 }, this);
34956
34957                 return;
34958             }
34959             
34960             var mx = 0;
34961             
34962             Roo.each(box, function(b){
34963                 
34964                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34965                 b.el.show();
34966
34967                 mx = Math.max(mx, b.x);
34968                 
34969             }, this);
34970             
34971             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34972             
34973             if(maxX < minX){
34974                 
34975                 Roo.each(box, function(b){
34976                 
34977                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34978                     b.el.hide();
34979                     
34980                 }, this);
34981                 
34982                 hit_end = true;
34983                 
34984                 return;
34985             }
34986             
34987             prune.push(box);
34988             
34989         }, this);
34990         
34991         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34992     },
34993     
34994     /** Sets position of item in DOM
34995     * @param {Element} item
34996     * @param {Number} x - horizontal position
34997     * @param {Number} y - vertical position
34998     * @param {Boolean} isInstant - disables transitions
34999     */
35000     _processVerticalLayoutQueue : function( queue, isInstant )
35001     {
35002         var pos = this.el.getBox(true);
35003         var x = pos.x;
35004         var y = pos.y;
35005         var maxY = [];
35006         
35007         for (var i = 0; i < this.cols; i++){
35008             maxY[i] = pos.y;
35009         }
35010         
35011         Roo.each(queue, function(box, k){
35012             
35013             var col = k % this.cols;
35014             
35015             Roo.each(box, function(b,kk){
35016                 
35017                 b.el.position('absolute');
35018                 
35019                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35020                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35021                 
35022                 if(b.size == 'md-left' || b.size == 'md-right'){
35023                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35024                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35025                 }
35026                 
35027                 b.el.setWidth(width);
35028                 b.el.setHeight(height);
35029                 // iframe?
35030                 b.el.select('iframe',true).setSize(width,height);
35031                 
35032             }, this);
35033             
35034             for (var i = 0; i < this.cols; i++){
35035                 
35036                 if(maxY[i] < maxY[col]){
35037                     col = i;
35038                     continue;
35039                 }
35040                 
35041                 col = Math.min(col, i);
35042                 
35043             }
35044             
35045             x = pos.x + col * (this.colWidth + this.padWidth);
35046             
35047             y = maxY[col];
35048             
35049             var positions = [];
35050             
35051             switch (box.length){
35052                 case 1 :
35053                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35054                     break;
35055                 case 2 :
35056                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35057                     break;
35058                 case 3 :
35059                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35060                     break;
35061                 case 4 :
35062                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35063                     break;
35064                 default :
35065                     break;
35066             }
35067             
35068             Roo.each(box, function(b,kk){
35069                 
35070                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35071                 
35072                 var sz = b.el.getSize();
35073                 
35074                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35075                 
35076             }, this);
35077             
35078         }, this);
35079         
35080         var mY = 0;
35081         
35082         for (var i = 0; i < this.cols; i++){
35083             mY = Math.max(mY, maxY[i]);
35084         }
35085         
35086         this.el.setHeight(mY - pos.y);
35087         
35088     },
35089     
35090 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35091 //    {
35092 //        var pos = this.el.getBox(true);
35093 //        var x = pos.x;
35094 //        var y = pos.y;
35095 //        var maxX = pos.right;
35096 //        
35097 //        var maxHeight = 0;
35098 //        
35099 //        Roo.each(items, function(item, k){
35100 //            
35101 //            var c = k % 2;
35102 //            
35103 //            item.el.position('absolute');
35104 //                
35105 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35106 //
35107 //            item.el.setWidth(width);
35108 //
35109 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35110 //
35111 //            item.el.setHeight(height);
35112 //            
35113 //            if(c == 0){
35114 //                item.el.setXY([x, y], isInstant ? false : true);
35115 //            } else {
35116 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35117 //            }
35118 //            
35119 //            y = y + height + this.alternativePadWidth;
35120 //            
35121 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35122 //            
35123 //        }, this);
35124 //        
35125 //        this.el.setHeight(maxHeight);
35126 //        
35127 //    },
35128     
35129     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35130     {
35131         var pos = this.el.getBox(true);
35132         
35133         var minX = pos.x;
35134         var minY = pos.y;
35135         
35136         var maxX = pos.right;
35137         
35138         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35139         
35140         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35141         
35142         Roo.each(queue, function(box, k){
35143             
35144             Roo.each(box, function(b, kk){
35145                 
35146                 b.el.position('absolute');
35147                 
35148                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35149                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35150                 
35151                 if(b.size == 'md-left' || b.size == 'md-right'){
35152                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35153                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35154                 }
35155                 
35156                 b.el.setWidth(width);
35157                 b.el.setHeight(height);
35158                 
35159             }, this);
35160             
35161             if(!box.length){
35162                 return;
35163             }
35164             
35165             var positions = [];
35166             
35167             switch (box.length){
35168                 case 1 :
35169                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35170                     break;
35171                 case 2 :
35172                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35173                     break;
35174                 case 3 :
35175                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35176                     break;
35177                 case 4 :
35178                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35179                     break;
35180                 default :
35181                     break;
35182             }
35183             
35184             Roo.each(box, function(b,kk){
35185                 
35186                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35187                 
35188                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35189                 
35190             }, this);
35191             
35192         }, this);
35193         
35194     },
35195     
35196     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35197     {
35198         Roo.each(eItems, function(b,k){
35199             
35200             b.size = (k == 0) ? 'sm' : 'xs';
35201             b.x = (k == 0) ? 2 : 1;
35202             b.y = (k == 0) ? 2 : 1;
35203             
35204             b.el.position('absolute');
35205             
35206             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35207                 
35208             b.el.setWidth(width);
35209             
35210             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35211             
35212             b.el.setHeight(height);
35213             
35214         }, this);
35215
35216         var positions = [];
35217         
35218         positions.push({
35219             x : maxX - this.unitWidth * 2 - this.gutter,
35220             y : minY
35221         });
35222         
35223         positions.push({
35224             x : maxX - this.unitWidth,
35225             y : minY + (this.unitWidth + this.gutter) * 2
35226         });
35227         
35228         positions.push({
35229             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35230             y : minY
35231         });
35232         
35233         Roo.each(eItems, function(b,k){
35234             
35235             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35236
35237         }, this);
35238         
35239     },
35240     
35241     getVerticalOneBoxColPositions : function(x, y, box)
35242     {
35243         var pos = [];
35244         
35245         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35246         
35247         if(box[0].size == 'md-left'){
35248             rand = 0;
35249         }
35250         
35251         if(box[0].size == 'md-right'){
35252             rand = 1;
35253         }
35254         
35255         pos.push({
35256             x : x + (this.unitWidth + this.gutter) * rand,
35257             y : y
35258         });
35259         
35260         return pos;
35261     },
35262     
35263     getVerticalTwoBoxColPositions : function(x, y, box)
35264     {
35265         var pos = [];
35266         
35267         if(box[0].size == 'xs'){
35268             
35269             pos.push({
35270                 x : x,
35271                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35272             });
35273
35274             pos.push({
35275                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35276                 y : y
35277             });
35278             
35279             return pos;
35280             
35281         }
35282         
35283         pos.push({
35284             x : x,
35285             y : y
35286         });
35287
35288         pos.push({
35289             x : x + (this.unitWidth + this.gutter) * 2,
35290             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35291         });
35292         
35293         return pos;
35294         
35295     },
35296     
35297     getVerticalThreeBoxColPositions : function(x, y, box)
35298     {
35299         var pos = [];
35300         
35301         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35302             
35303             pos.push({
35304                 x : x,
35305                 y : y
35306             });
35307
35308             pos.push({
35309                 x : x + (this.unitWidth + this.gutter) * 1,
35310                 y : y
35311             });
35312             
35313             pos.push({
35314                 x : x + (this.unitWidth + this.gutter) * 2,
35315                 y : y
35316             });
35317             
35318             return pos;
35319             
35320         }
35321         
35322         if(box[0].size == 'xs' && box[1].size == 'xs'){
35323             
35324             pos.push({
35325                 x : x,
35326                 y : y
35327             });
35328
35329             pos.push({
35330                 x : x,
35331                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35332             });
35333             
35334             pos.push({
35335                 x : x + (this.unitWidth + this.gutter) * 1,
35336                 y : y
35337             });
35338             
35339             return pos;
35340             
35341         }
35342         
35343         pos.push({
35344             x : x,
35345             y : y
35346         });
35347
35348         pos.push({
35349             x : x + (this.unitWidth + this.gutter) * 2,
35350             y : y
35351         });
35352
35353         pos.push({
35354             x : x + (this.unitWidth + this.gutter) * 2,
35355             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35356         });
35357             
35358         return pos;
35359         
35360     },
35361     
35362     getVerticalFourBoxColPositions : function(x, y, box)
35363     {
35364         var pos = [];
35365         
35366         if(box[0].size == 'xs'){
35367             
35368             pos.push({
35369                 x : x,
35370                 y : y
35371             });
35372
35373             pos.push({
35374                 x : x,
35375                 y : y + (this.unitHeight + this.gutter) * 1
35376             });
35377             
35378             pos.push({
35379                 x : x,
35380                 y : y + (this.unitHeight + this.gutter) * 2
35381             });
35382             
35383             pos.push({
35384                 x : x + (this.unitWidth + this.gutter) * 1,
35385                 y : y
35386             });
35387             
35388             return pos;
35389             
35390         }
35391         
35392         pos.push({
35393             x : x,
35394             y : y
35395         });
35396
35397         pos.push({
35398             x : x + (this.unitWidth + this.gutter) * 2,
35399             y : y
35400         });
35401
35402         pos.push({
35403             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35404             y : y + (this.unitHeight + this.gutter) * 1
35405         });
35406
35407         pos.push({
35408             x : x + (this.unitWidth + this.gutter) * 2,
35409             y : y + (this.unitWidth + this.gutter) * 2
35410         });
35411
35412         return pos;
35413         
35414     },
35415     
35416     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35417     {
35418         var pos = [];
35419         
35420         if(box[0].size == 'md-left'){
35421             pos.push({
35422                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35423                 y : minY
35424             });
35425             
35426             return pos;
35427         }
35428         
35429         if(box[0].size == 'md-right'){
35430             pos.push({
35431                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35432                 y : minY + (this.unitWidth + this.gutter) * 1
35433             });
35434             
35435             return pos;
35436         }
35437         
35438         var rand = Math.floor(Math.random() * (4 - box[0].y));
35439         
35440         pos.push({
35441             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35442             y : minY + (this.unitWidth + this.gutter) * rand
35443         });
35444         
35445         return pos;
35446         
35447     },
35448     
35449     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35450     {
35451         var pos = [];
35452         
35453         if(box[0].size == 'xs'){
35454             
35455             pos.push({
35456                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35457                 y : minY
35458             });
35459
35460             pos.push({
35461                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35462                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35463             });
35464             
35465             return pos;
35466             
35467         }
35468         
35469         pos.push({
35470             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35471             y : minY
35472         });
35473
35474         pos.push({
35475             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35476             y : minY + (this.unitWidth + this.gutter) * 2
35477         });
35478         
35479         return pos;
35480         
35481     },
35482     
35483     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35484     {
35485         var pos = [];
35486         
35487         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35488             
35489             pos.push({
35490                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35491                 y : minY
35492             });
35493
35494             pos.push({
35495                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35496                 y : minY + (this.unitWidth + this.gutter) * 1
35497             });
35498             
35499             pos.push({
35500                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35501                 y : minY + (this.unitWidth + this.gutter) * 2
35502             });
35503             
35504             return pos;
35505             
35506         }
35507         
35508         if(box[0].size == 'xs' && box[1].size == 'xs'){
35509             
35510             pos.push({
35511                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35512                 y : minY
35513             });
35514
35515             pos.push({
35516                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35517                 y : minY
35518             });
35519             
35520             pos.push({
35521                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35522                 y : minY + (this.unitWidth + this.gutter) * 1
35523             });
35524             
35525             return pos;
35526             
35527         }
35528         
35529         pos.push({
35530             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35531             y : minY
35532         });
35533
35534         pos.push({
35535             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35536             y : minY + (this.unitWidth + this.gutter) * 2
35537         });
35538
35539         pos.push({
35540             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35541             y : minY + (this.unitWidth + this.gutter) * 2
35542         });
35543             
35544         return pos;
35545         
35546     },
35547     
35548     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35549     {
35550         var pos = [];
35551         
35552         if(box[0].size == 'xs'){
35553             
35554             pos.push({
35555                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35556                 y : minY
35557             });
35558
35559             pos.push({
35560                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35561                 y : minY
35562             });
35563             
35564             pos.push({
35565                 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),
35566                 y : minY
35567             });
35568             
35569             pos.push({
35570                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35571                 y : minY + (this.unitWidth + this.gutter) * 1
35572             });
35573             
35574             return pos;
35575             
35576         }
35577         
35578         pos.push({
35579             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35580             y : minY
35581         });
35582         
35583         pos.push({
35584             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35585             y : minY + (this.unitWidth + this.gutter) * 2
35586         });
35587         
35588         pos.push({
35589             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35590             y : minY + (this.unitWidth + this.gutter) * 2
35591         });
35592         
35593         pos.push({
35594             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),
35595             y : minY + (this.unitWidth + this.gutter) * 2
35596         });
35597
35598         return pos;
35599         
35600     },
35601     
35602     /**
35603     * remove a Masonry Brick
35604     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35605     */
35606     removeBrick : function(brick_id)
35607     {
35608         if (!brick_id) {
35609             return;
35610         }
35611         
35612         for (var i = 0; i<this.bricks.length; i++) {
35613             if (this.bricks[i].id == brick_id) {
35614                 this.bricks.splice(i,1);
35615                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35616                 this.initial();
35617             }
35618         }
35619     },
35620     
35621     /**
35622     * adds a Masonry Brick
35623     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35624     */
35625     addBrick : function(cfg)
35626     {
35627         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35628         //this.register(cn);
35629         cn.parentId = this.id;
35630         cn.render(this.el);
35631         return cn;
35632     },
35633     
35634     /**
35635     * register a Masonry Brick
35636     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35637     */
35638     
35639     register : function(brick)
35640     {
35641         this.bricks.push(brick);
35642         brick.masonryId = this.id;
35643     },
35644     
35645     /**
35646     * clear all the Masonry Brick
35647     */
35648     clearAll : function()
35649     {
35650         this.bricks = [];
35651         //this.getChildContainer().dom.innerHTML = "";
35652         this.el.dom.innerHTML = '';
35653     },
35654     
35655     getSelected : function()
35656     {
35657         if (!this.selectedBrick) {
35658             return false;
35659         }
35660         
35661         return this.selectedBrick;
35662     }
35663 });
35664
35665 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35666     
35667     groups: {},
35668      /**
35669     * register a Masonry Layout
35670     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35671     */
35672     
35673     register : function(layout)
35674     {
35675         this.groups[layout.id] = layout;
35676     },
35677     /**
35678     * fetch a  Masonry Layout based on the masonry layout ID
35679     * @param {string} the masonry layout to add
35680     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35681     */
35682     
35683     get: function(layout_id) {
35684         if (typeof(this.groups[layout_id]) == 'undefined') {
35685             return false;
35686         }
35687         return this.groups[layout_id] ;
35688     }
35689     
35690     
35691     
35692 });
35693
35694  
35695
35696  /**
35697  *
35698  * This is based on 
35699  * http://masonry.desandro.com
35700  *
35701  * The idea is to render all the bricks based on vertical width...
35702  *
35703  * The original code extends 'outlayer' - we might need to use that....
35704  * 
35705  */
35706
35707
35708 /**
35709  * @class Roo.bootstrap.LayoutMasonryAuto
35710  * @extends Roo.bootstrap.Component
35711  * Bootstrap Layout Masonry class
35712  * 
35713  * @constructor
35714  * Create a new Element
35715  * @param {Object} config The config object
35716  */
35717
35718 Roo.bootstrap.LayoutMasonryAuto = function(config){
35719     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35720 };
35721
35722 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35723     
35724       /**
35725      * @cfg {Boolean} isFitWidth  - resize the width..
35726      */   
35727     isFitWidth : false,  // options..
35728     /**
35729      * @cfg {Boolean} isOriginLeft = left align?
35730      */   
35731     isOriginLeft : true,
35732     /**
35733      * @cfg {Boolean} isOriginTop = top align?
35734      */   
35735     isOriginTop : false,
35736     /**
35737      * @cfg {Boolean} isLayoutInstant = no animation?
35738      */   
35739     isLayoutInstant : false, // needed?
35740     /**
35741      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35742      */   
35743     isResizingContainer : true,
35744     /**
35745      * @cfg {Number} columnWidth  width of the columns 
35746      */   
35747     
35748     columnWidth : 0,
35749     
35750     /**
35751      * @cfg {Number} maxCols maximum number of columns
35752      */   
35753     
35754     maxCols: 0,
35755     /**
35756      * @cfg {Number} padHeight padding below box..
35757      */   
35758     
35759     padHeight : 10, 
35760     
35761     /**
35762      * @cfg {Boolean} isAutoInitial defalut true
35763      */   
35764     
35765     isAutoInitial : true, 
35766     
35767     // private?
35768     gutter : 0,
35769     
35770     containerWidth: 0,
35771     initialColumnWidth : 0,
35772     currentSize : null,
35773     
35774     colYs : null, // array.
35775     maxY : 0,
35776     padWidth: 10,
35777     
35778     
35779     tag: 'div',
35780     cls: '',
35781     bricks: null, //CompositeElement
35782     cols : 0, // array?
35783     // element : null, // wrapped now this.el
35784     _isLayoutInited : null, 
35785     
35786     
35787     getAutoCreate : function(){
35788         
35789         var cfg = {
35790             tag: this.tag,
35791             cls: 'blog-masonary-wrapper ' + this.cls,
35792             cn : {
35793                 cls : 'mas-boxes masonary'
35794             }
35795         };
35796         
35797         return cfg;
35798     },
35799     
35800     getChildContainer: function( )
35801     {
35802         if (this.boxesEl) {
35803             return this.boxesEl;
35804         }
35805         
35806         this.boxesEl = this.el.select('.mas-boxes').first();
35807         
35808         return this.boxesEl;
35809     },
35810     
35811     
35812     initEvents : function()
35813     {
35814         var _this = this;
35815         
35816         if(this.isAutoInitial){
35817             Roo.log('hook children rendered');
35818             this.on('childrenrendered', function() {
35819                 Roo.log('children rendered');
35820                 _this.initial();
35821             } ,this);
35822         }
35823         
35824     },
35825     
35826     initial : function()
35827     {
35828         this.reloadItems();
35829
35830         this.currentSize = this.el.getBox(true);
35831
35832         /// was window resize... - let's see if this works..
35833         Roo.EventManager.onWindowResize(this.resize, this); 
35834
35835         if(!this.isAutoInitial){
35836             this.layout();
35837             return;
35838         }
35839         
35840         this.layout.defer(500,this);
35841     },
35842     
35843     reloadItems: function()
35844     {
35845         this.bricks = this.el.select('.masonry-brick', true);
35846         
35847         this.bricks.each(function(b) {
35848             //Roo.log(b.getSize());
35849             if (!b.attr('originalwidth')) {
35850                 b.attr('originalwidth',  b.getSize().width);
35851             }
35852             
35853         });
35854         
35855         Roo.log(this.bricks.elements.length);
35856     },
35857     
35858     resize : function()
35859     {
35860         Roo.log('resize');
35861         var cs = this.el.getBox(true);
35862         
35863         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35864             Roo.log("no change in with or X");
35865             return;
35866         }
35867         this.currentSize = cs;
35868         this.layout();
35869     },
35870     
35871     layout : function()
35872     {
35873          Roo.log('layout');
35874         this._resetLayout();
35875         //this._manageStamps();
35876       
35877         // don't animate first layout
35878         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35879         this.layoutItems( isInstant );
35880       
35881         // flag for initalized
35882         this._isLayoutInited = true;
35883     },
35884     
35885     layoutItems : function( isInstant )
35886     {
35887         //var items = this._getItemsForLayout( this.items );
35888         // original code supports filtering layout items.. we just ignore it..
35889         
35890         this._layoutItems( this.bricks , isInstant );
35891       
35892         this._postLayout();
35893     },
35894     _layoutItems : function ( items , isInstant)
35895     {
35896        //this.fireEvent( 'layout', this, items );
35897     
35898
35899         if ( !items || !items.elements.length ) {
35900           // no items, emit event with empty array
35901             return;
35902         }
35903
35904         var queue = [];
35905         items.each(function(item) {
35906             Roo.log("layout item");
35907             Roo.log(item);
35908             // get x/y object from method
35909             var position = this._getItemLayoutPosition( item );
35910             // enqueue
35911             position.item = item;
35912             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35913             queue.push( position );
35914         }, this);
35915       
35916         this._processLayoutQueue( queue );
35917     },
35918     /** Sets position of item in DOM
35919     * @param {Element} item
35920     * @param {Number} x - horizontal position
35921     * @param {Number} y - vertical position
35922     * @param {Boolean} isInstant - disables transitions
35923     */
35924     _processLayoutQueue : function( queue )
35925     {
35926         for ( var i=0, len = queue.length; i < len; i++ ) {
35927             var obj = queue[i];
35928             obj.item.position('absolute');
35929             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35930         }
35931     },
35932       
35933     
35934     /**
35935     * Any logic you want to do after each layout,
35936     * i.e. size the container
35937     */
35938     _postLayout : function()
35939     {
35940         this.resizeContainer();
35941     },
35942     
35943     resizeContainer : function()
35944     {
35945         if ( !this.isResizingContainer ) {
35946             return;
35947         }
35948         var size = this._getContainerSize();
35949         if ( size ) {
35950             this.el.setSize(size.width,size.height);
35951             this.boxesEl.setSize(size.width,size.height);
35952         }
35953     },
35954     
35955     
35956     
35957     _resetLayout : function()
35958     {
35959         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35960         this.colWidth = this.el.getWidth();
35961         //this.gutter = this.el.getWidth(); 
35962         
35963         this.measureColumns();
35964
35965         // reset column Y
35966         var i = this.cols;
35967         this.colYs = [];
35968         while (i--) {
35969             this.colYs.push( 0 );
35970         }
35971     
35972         this.maxY = 0;
35973     },
35974
35975     measureColumns : function()
35976     {
35977         this.getContainerWidth();
35978       // if columnWidth is 0, default to outerWidth of first item
35979         if ( !this.columnWidth ) {
35980             var firstItem = this.bricks.first();
35981             Roo.log(firstItem);
35982             this.columnWidth  = this.containerWidth;
35983             if (firstItem && firstItem.attr('originalwidth') ) {
35984                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35985             }
35986             // columnWidth fall back to item of first element
35987             Roo.log("set column width?");
35988                         this.initialColumnWidth = this.columnWidth  ;
35989
35990             // if first elem has no width, default to size of container
35991             
35992         }
35993         
35994         
35995         if (this.initialColumnWidth) {
35996             this.columnWidth = this.initialColumnWidth;
35997         }
35998         
35999         
36000             
36001         // column width is fixed at the top - however if container width get's smaller we should
36002         // reduce it...
36003         
36004         // this bit calcs how man columns..
36005             
36006         var columnWidth = this.columnWidth += this.gutter;
36007       
36008         // calculate columns
36009         var containerWidth = this.containerWidth + this.gutter;
36010         
36011         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36012         // fix rounding errors, typically with gutters
36013         var excess = columnWidth - containerWidth % columnWidth;
36014         
36015         
36016         // if overshoot is less than a pixel, round up, otherwise floor it
36017         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36018         cols = Math[ mathMethod ]( cols );
36019         this.cols = Math.max( cols, 1 );
36020         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36021         
36022          // padding positioning..
36023         var totalColWidth = this.cols * this.columnWidth;
36024         var padavail = this.containerWidth - totalColWidth;
36025         // so for 2 columns - we need 3 'pads'
36026         
36027         var padNeeded = (1+this.cols) * this.padWidth;
36028         
36029         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36030         
36031         this.columnWidth += padExtra
36032         //this.padWidth = Math.floor(padavail /  ( this.cols));
36033         
36034         // adjust colum width so that padding is fixed??
36035         
36036         // we have 3 columns ... total = width * 3
36037         // we have X left over... that should be used by 
36038         
36039         //if (this.expandC) {
36040             
36041         //}
36042         
36043         
36044         
36045     },
36046     
36047     getContainerWidth : function()
36048     {
36049        /* // container is parent if fit width
36050         var container = this.isFitWidth ? this.element.parentNode : this.element;
36051         // check that this.size and size are there
36052         // IE8 triggers resize on body size change, so they might not be
36053         
36054         var size = getSize( container );  //FIXME
36055         this.containerWidth = size && size.innerWidth; //FIXME
36056         */
36057          
36058         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36059         
36060     },
36061     
36062     _getItemLayoutPosition : function( item )  // what is item?
36063     {
36064         // we resize the item to our columnWidth..
36065       
36066         item.setWidth(this.columnWidth);
36067         item.autoBoxAdjust  = false;
36068         
36069         var sz = item.getSize();
36070  
36071         // how many columns does this brick span
36072         var remainder = this.containerWidth % this.columnWidth;
36073         
36074         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36075         // round if off by 1 pixel, otherwise use ceil
36076         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36077         colSpan = Math.min( colSpan, this.cols );
36078         
36079         // normally this should be '1' as we dont' currently allow multi width columns..
36080         
36081         var colGroup = this._getColGroup( colSpan );
36082         // get the minimum Y value from the columns
36083         var minimumY = Math.min.apply( Math, colGroup );
36084         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36085         
36086         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36087          
36088         // position the brick
36089         var position = {
36090             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36091             y: this.currentSize.y + minimumY + this.padHeight
36092         };
36093         
36094         Roo.log(position);
36095         // apply setHeight to necessary columns
36096         var setHeight = minimumY + sz.height + this.padHeight;
36097         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36098         
36099         var setSpan = this.cols + 1 - colGroup.length;
36100         for ( var i = 0; i < setSpan; i++ ) {
36101           this.colYs[ shortColIndex + i ] = setHeight ;
36102         }
36103       
36104         return position;
36105     },
36106     
36107     /**
36108      * @param {Number} colSpan - number of columns the element spans
36109      * @returns {Array} colGroup
36110      */
36111     _getColGroup : function( colSpan )
36112     {
36113         if ( colSpan < 2 ) {
36114           // if brick spans only one column, use all the column Ys
36115           return this.colYs;
36116         }
36117       
36118         var colGroup = [];
36119         // how many different places could this brick fit horizontally
36120         var groupCount = this.cols + 1 - colSpan;
36121         // for each group potential horizontal position
36122         for ( var i = 0; i < groupCount; i++ ) {
36123           // make an array of colY values for that one group
36124           var groupColYs = this.colYs.slice( i, i + colSpan );
36125           // and get the max value of the array
36126           colGroup[i] = Math.max.apply( Math, groupColYs );
36127         }
36128         return colGroup;
36129     },
36130     /*
36131     _manageStamp : function( stamp )
36132     {
36133         var stampSize =  stamp.getSize();
36134         var offset = stamp.getBox();
36135         // get the columns that this stamp affects
36136         var firstX = this.isOriginLeft ? offset.x : offset.right;
36137         var lastX = firstX + stampSize.width;
36138         var firstCol = Math.floor( firstX / this.columnWidth );
36139         firstCol = Math.max( 0, firstCol );
36140         
36141         var lastCol = Math.floor( lastX / this.columnWidth );
36142         // lastCol should not go over if multiple of columnWidth #425
36143         lastCol -= lastX % this.columnWidth ? 0 : 1;
36144         lastCol = Math.min( this.cols - 1, lastCol );
36145         
36146         // set colYs to bottom of the stamp
36147         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36148             stampSize.height;
36149             
36150         for ( var i = firstCol; i <= lastCol; i++ ) {
36151           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36152         }
36153     },
36154     */
36155     
36156     _getContainerSize : function()
36157     {
36158         this.maxY = Math.max.apply( Math, this.colYs );
36159         var size = {
36160             height: this.maxY
36161         };
36162       
36163         if ( this.isFitWidth ) {
36164             size.width = this._getContainerFitWidth();
36165         }
36166       
36167         return size;
36168     },
36169     
36170     _getContainerFitWidth : function()
36171     {
36172         var unusedCols = 0;
36173         // count unused columns
36174         var i = this.cols;
36175         while ( --i ) {
36176           if ( this.colYs[i] !== 0 ) {
36177             break;
36178           }
36179           unusedCols++;
36180         }
36181         // fit container to columns that have been used
36182         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36183     },
36184     
36185     needsResizeLayout : function()
36186     {
36187         var previousWidth = this.containerWidth;
36188         this.getContainerWidth();
36189         return previousWidth !== this.containerWidth;
36190     }
36191  
36192 });
36193
36194  
36195
36196  /*
36197  * - LGPL
36198  *
36199  * element
36200  * 
36201  */
36202
36203 /**
36204  * @class Roo.bootstrap.MasonryBrick
36205  * @extends Roo.bootstrap.Component
36206  * Bootstrap MasonryBrick class
36207  * 
36208  * @constructor
36209  * Create a new MasonryBrick
36210  * @param {Object} config The config object
36211  */
36212
36213 Roo.bootstrap.MasonryBrick = function(config){
36214     
36215     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36216     
36217     Roo.bootstrap.MasonryBrick.register(this);
36218     
36219     this.addEvents({
36220         // raw events
36221         /**
36222          * @event click
36223          * When a MasonryBrick is clcik
36224          * @param {Roo.bootstrap.MasonryBrick} this
36225          * @param {Roo.EventObject} e
36226          */
36227         "click" : true
36228     });
36229 };
36230
36231 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36232     
36233     /**
36234      * @cfg {String} title
36235      */   
36236     title : '',
36237     /**
36238      * @cfg {String} html
36239      */   
36240     html : '',
36241     /**
36242      * @cfg {String} bgimage
36243      */   
36244     bgimage : '',
36245     /**
36246      * @cfg {String} videourl
36247      */   
36248     videourl : '',
36249     /**
36250      * @cfg {String} cls
36251      */   
36252     cls : '',
36253     /**
36254      * @cfg {String} href
36255      */   
36256     href : '',
36257     /**
36258      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36259      */   
36260     size : 'xs',
36261     
36262     /**
36263      * @cfg {String} placetitle (center|bottom)
36264      */   
36265     placetitle : '',
36266     
36267     /**
36268      * @cfg {Boolean} isFitContainer defalut true
36269      */   
36270     isFitContainer : true, 
36271     
36272     /**
36273      * @cfg {Boolean} preventDefault defalut false
36274      */   
36275     preventDefault : false, 
36276     
36277     /**
36278      * @cfg {Boolean} inverse defalut false
36279      */   
36280     maskInverse : false, 
36281     
36282     getAutoCreate : function()
36283     {
36284         if(!this.isFitContainer){
36285             return this.getSplitAutoCreate();
36286         }
36287         
36288         var cls = 'masonry-brick masonry-brick-full';
36289         
36290         if(this.href.length){
36291             cls += ' masonry-brick-link';
36292         }
36293         
36294         if(this.bgimage.length){
36295             cls += ' masonry-brick-image';
36296         }
36297         
36298         if(this.maskInverse){
36299             cls += ' mask-inverse';
36300         }
36301         
36302         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36303             cls += ' enable-mask';
36304         }
36305         
36306         if(this.size){
36307             cls += ' masonry-' + this.size + '-brick';
36308         }
36309         
36310         if(this.placetitle.length){
36311             
36312             switch (this.placetitle) {
36313                 case 'center' :
36314                     cls += ' masonry-center-title';
36315                     break;
36316                 case 'bottom' :
36317                     cls += ' masonry-bottom-title';
36318                     break;
36319                 default:
36320                     break;
36321             }
36322             
36323         } else {
36324             if(!this.html.length && !this.bgimage.length){
36325                 cls += ' masonry-center-title';
36326             }
36327
36328             if(!this.html.length && this.bgimage.length){
36329                 cls += ' masonry-bottom-title';
36330             }
36331         }
36332         
36333         if(this.cls){
36334             cls += ' ' + this.cls;
36335         }
36336         
36337         var cfg = {
36338             tag: (this.href.length) ? 'a' : 'div',
36339             cls: cls,
36340             cn: [
36341                 {
36342                     tag: 'div',
36343                     cls: 'masonry-brick-mask'
36344                 },
36345                 {
36346                     tag: 'div',
36347                     cls: 'masonry-brick-paragraph',
36348                     cn: []
36349                 }
36350             ]
36351         };
36352         
36353         if(this.href.length){
36354             cfg.href = this.href;
36355         }
36356         
36357         var cn = cfg.cn[1].cn;
36358         
36359         if(this.title.length){
36360             cn.push({
36361                 tag: 'h4',
36362                 cls: 'masonry-brick-title',
36363                 html: this.title
36364             });
36365         }
36366         
36367         if(this.html.length){
36368             cn.push({
36369                 tag: 'p',
36370                 cls: 'masonry-brick-text',
36371                 html: this.html
36372             });
36373         }
36374         
36375         if (!this.title.length && !this.html.length) {
36376             cfg.cn[1].cls += ' hide';
36377         }
36378         
36379         if(this.bgimage.length){
36380             cfg.cn.push({
36381                 tag: 'img',
36382                 cls: 'masonry-brick-image-view',
36383                 src: this.bgimage
36384             });
36385         }
36386         
36387         if(this.videourl.length){
36388             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36389             // youtube support only?
36390             cfg.cn.push({
36391                 tag: 'iframe',
36392                 cls: 'masonry-brick-image-view',
36393                 src: vurl,
36394                 frameborder : 0,
36395                 allowfullscreen : true
36396             });
36397         }
36398         
36399         return cfg;
36400         
36401     },
36402     
36403     getSplitAutoCreate : function()
36404     {
36405         var cls = 'masonry-brick masonry-brick-split';
36406         
36407         if(this.href.length){
36408             cls += ' masonry-brick-link';
36409         }
36410         
36411         if(this.bgimage.length){
36412             cls += ' masonry-brick-image';
36413         }
36414         
36415         if(this.size){
36416             cls += ' masonry-' + this.size + '-brick';
36417         }
36418         
36419         switch (this.placetitle) {
36420             case 'center' :
36421                 cls += ' masonry-center-title';
36422                 break;
36423             case 'bottom' :
36424                 cls += ' masonry-bottom-title';
36425                 break;
36426             default:
36427                 if(!this.bgimage.length){
36428                     cls += ' masonry-center-title';
36429                 }
36430
36431                 if(this.bgimage.length){
36432                     cls += ' masonry-bottom-title';
36433                 }
36434                 break;
36435         }
36436         
36437         if(this.cls){
36438             cls += ' ' + this.cls;
36439         }
36440         
36441         var cfg = {
36442             tag: (this.href.length) ? 'a' : 'div',
36443             cls: cls,
36444             cn: [
36445                 {
36446                     tag: 'div',
36447                     cls: 'masonry-brick-split-head',
36448                     cn: [
36449                         {
36450                             tag: 'div',
36451                             cls: 'masonry-brick-paragraph',
36452                             cn: []
36453                         }
36454                     ]
36455                 },
36456                 {
36457                     tag: 'div',
36458                     cls: 'masonry-brick-split-body',
36459                     cn: []
36460                 }
36461             ]
36462         };
36463         
36464         if(this.href.length){
36465             cfg.href = this.href;
36466         }
36467         
36468         if(this.title.length){
36469             cfg.cn[0].cn[0].cn.push({
36470                 tag: 'h4',
36471                 cls: 'masonry-brick-title',
36472                 html: this.title
36473             });
36474         }
36475         
36476         if(this.html.length){
36477             cfg.cn[1].cn.push({
36478                 tag: 'p',
36479                 cls: 'masonry-brick-text',
36480                 html: this.html
36481             });
36482         }
36483
36484         if(this.bgimage.length){
36485             cfg.cn[0].cn.push({
36486                 tag: 'img',
36487                 cls: 'masonry-brick-image-view',
36488                 src: this.bgimage
36489             });
36490         }
36491         
36492         if(this.videourl.length){
36493             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36494             // youtube support only?
36495             cfg.cn[0].cn.cn.push({
36496                 tag: 'iframe',
36497                 cls: 'masonry-brick-image-view',
36498                 src: vurl,
36499                 frameborder : 0,
36500                 allowfullscreen : true
36501             });
36502         }
36503         
36504         return cfg;
36505     },
36506     
36507     initEvents: function() 
36508     {
36509         switch (this.size) {
36510             case 'xs' :
36511                 this.x = 1;
36512                 this.y = 1;
36513                 break;
36514             case 'sm' :
36515                 this.x = 2;
36516                 this.y = 2;
36517                 break;
36518             case 'md' :
36519             case 'md-left' :
36520             case 'md-right' :
36521                 this.x = 3;
36522                 this.y = 3;
36523                 break;
36524             case 'tall' :
36525                 this.x = 2;
36526                 this.y = 3;
36527                 break;
36528             case 'wide' :
36529                 this.x = 3;
36530                 this.y = 2;
36531                 break;
36532             case 'wide-thin' :
36533                 this.x = 3;
36534                 this.y = 1;
36535                 break;
36536                         
36537             default :
36538                 break;
36539         }
36540         
36541         if(Roo.isTouch){
36542             this.el.on('touchstart', this.onTouchStart, this);
36543             this.el.on('touchmove', this.onTouchMove, this);
36544             this.el.on('touchend', this.onTouchEnd, this);
36545             this.el.on('contextmenu', this.onContextMenu, this);
36546         } else {
36547             this.el.on('mouseenter'  ,this.enter, this);
36548             this.el.on('mouseleave', this.leave, this);
36549             this.el.on('click', this.onClick, this);
36550         }
36551         
36552         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36553             this.parent().bricks.push(this);   
36554         }
36555         
36556     },
36557     
36558     onClick: function(e, el)
36559     {
36560         var time = this.endTimer - this.startTimer;
36561         // Roo.log(e.preventDefault());
36562         if(Roo.isTouch){
36563             if(time > 1000){
36564                 e.preventDefault();
36565                 return;
36566             }
36567         }
36568         
36569         if(!this.preventDefault){
36570             return;
36571         }
36572         
36573         e.preventDefault();
36574         
36575         if (this.activeClass != '') {
36576             this.selectBrick();
36577         }
36578         
36579         this.fireEvent('click', this, e);
36580     },
36581     
36582     enter: function(e, el)
36583     {
36584         e.preventDefault();
36585         
36586         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36587             return;
36588         }
36589         
36590         if(this.bgimage.length && this.html.length){
36591             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36592         }
36593     },
36594     
36595     leave: function(e, el)
36596     {
36597         e.preventDefault();
36598         
36599         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36600             return;
36601         }
36602         
36603         if(this.bgimage.length && this.html.length){
36604             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36605         }
36606     },
36607     
36608     onTouchStart: function(e, el)
36609     {
36610 //        e.preventDefault();
36611         
36612         this.touchmoved = false;
36613         
36614         if(!this.isFitContainer){
36615             return;
36616         }
36617         
36618         if(!this.bgimage.length || !this.html.length){
36619             return;
36620         }
36621         
36622         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36623         
36624         this.timer = new Date().getTime();
36625         
36626     },
36627     
36628     onTouchMove: function(e, el)
36629     {
36630         this.touchmoved = true;
36631     },
36632     
36633     onContextMenu : function(e,el)
36634     {
36635         e.preventDefault();
36636         e.stopPropagation();
36637         return false;
36638     },
36639     
36640     onTouchEnd: function(e, el)
36641     {
36642 //        e.preventDefault();
36643         
36644         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36645         
36646             this.leave(e,el);
36647             
36648             return;
36649         }
36650         
36651         if(!this.bgimage.length || !this.html.length){
36652             
36653             if(this.href.length){
36654                 window.location.href = this.href;
36655             }
36656             
36657             return;
36658         }
36659         
36660         if(!this.isFitContainer){
36661             return;
36662         }
36663         
36664         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36665         
36666         window.location.href = this.href;
36667     },
36668     
36669     //selection on single brick only
36670     selectBrick : function() {
36671         
36672         if (!this.parentId) {
36673             return;
36674         }
36675         
36676         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36677         var index = m.selectedBrick.indexOf(this.id);
36678         
36679         if ( index > -1) {
36680             m.selectedBrick.splice(index,1);
36681             this.el.removeClass(this.activeClass);
36682             return;
36683         }
36684         
36685         for(var i = 0; i < m.selectedBrick.length; i++) {
36686             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36687             b.el.removeClass(b.activeClass);
36688         }
36689         
36690         m.selectedBrick = [];
36691         
36692         m.selectedBrick.push(this.id);
36693         this.el.addClass(this.activeClass);
36694         return;
36695     },
36696     
36697     isSelected : function(){
36698         return this.el.hasClass(this.activeClass);
36699         
36700     }
36701 });
36702
36703 Roo.apply(Roo.bootstrap.MasonryBrick, {
36704     
36705     //groups: {},
36706     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36707      /**
36708     * register a Masonry Brick
36709     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36710     */
36711     
36712     register : function(brick)
36713     {
36714         //this.groups[brick.id] = brick;
36715         this.groups.add(brick.id, brick);
36716     },
36717     /**
36718     * fetch a  masonry brick based on the masonry brick ID
36719     * @param {string} the masonry brick to add
36720     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36721     */
36722     
36723     get: function(brick_id) 
36724     {
36725         // if (typeof(this.groups[brick_id]) == 'undefined') {
36726         //     return false;
36727         // }
36728         // return this.groups[brick_id] ;
36729         
36730         if(this.groups.key(brick_id)) {
36731             return this.groups.key(brick_id);
36732         }
36733         
36734         return false;
36735     }
36736     
36737     
36738     
36739 });
36740
36741  /*
36742  * - LGPL
36743  *
36744  * element
36745  * 
36746  */
36747
36748 /**
36749  * @class Roo.bootstrap.Brick
36750  * @extends Roo.bootstrap.Component
36751  * Bootstrap Brick class
36752  * 
36753  * @constructor
36754  * Create a new Brick
36755  * @param {Object} config The config object
36756  */
36757
36758 Roo.bootstrap.Brick = function(config){
36759     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36760     
36761     this.addEvents({
36762         // raw events
36763         /**
36764          * @event click
36765          * When a Brick is click
36766          * @param {Roo.bootstrap.Brick} this
36767          * @param {Roo.EventObject} e
36768          */
36769         "click" : true
36770     });
36771 };
36772
36773 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36774     
36775     /**
36776      * @cfg {String} title
36777      */   
36778     title : '',
36779     /**
36780      * @cfg {String} html
36781      */   
36782     html : '',
36783     /**
36784      * @cfg {String} bgimage
36785      */   
36786     bgimage : '',
36787     /**
36788      * @cfg {String} cls
36789      */   
36790     cls : '',
36791     /**
36792      * @cfg {String} href
36793      */   
36794     href : '',
36795     /**
36796      * @cfg {String} video
36797      */   
36798     video : '',
36799     /**
36800      * @cfg {Boolean} square
36801      */   
36802     square : true,
36803     
36804     getAutoCreate : function()
36805     {
36806         var cls = 'roo-brick';
36807         
36808         if(this.href.length){
36809             cls += ' roo-brick-link';
36810         }
36811         
36812         if(this.bgimage.length){
36813             cls += ' roo-brick-image';
36814         }
36815         
36816         if(!this.html.length && !this.bgimage.length){
36817             cls += ' roo-brick-center-title';
36818         }
36819         
36820         if(!this.html.length && this.bgimage.length){
36821             cls += ' roo-brick-bottom-title';
36822         }
36823         
36824         if(this.cls){
36825             cls += ' ' + this.cls;
36826         }
36827         
36828         var cfg = {
36829             tag: (this.href.length) ? 'a' : 'div',
36830             cls: cls,
36831             cn: [
36832                 {
36833                     tag: 'div',
36834                     cls: 'roo-brick-paragraph',
36835                     cn: []
36836                 }
36837             ]
36838         };
36839         
36840         if(this.href.length){
36841             cfg.href = this.href;
36842         }
36843         
36844         var cn = cfg.cn[0].cn;
36845         
36846         if(this.title.length){
36847             cn.push({
36848                 tag: 'h4',
36849                 cls: 'roo-brick-title',
36850                 html: this.title
36851             });
36852         }
36853         
36854         if(this.html.length){
36855             cn.push({
36856                 tag: 'p',
36857                 cls: 'roo-brick-text',
36858                 html: this.html
36859             });
36860         } else {
36861             cn.cls += ' hide';
36862         }
36863         
36864         if(this.bgimage.length){
36865             cfg.cn.push({
36866                 tag: 'img',
36867                 cls: 'roo-brick-image-view',
36868                 src: this.bgimage
36869             });
36870         }
36871         
36872         return cfg;
36873     },
36874     
36875     initEvents: function() 
36876     {
36877         if(this.title.length || this.html.length){
36878             this.el.on('mouseenter'  ,this.enter, this);
36879             this.el.on('mouseleave', this.leave, this);
36880         }
36881         
36882         Roo.EventManager.onWindowResize(this.resize, this); 
36883         
36884         if(this.bgimage.length){
36885             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36886             this.imageEl.on('load', this.onImageLoad, this);
36887             return;
36888         }
36889         
36890         this.resize();
36891     },
36892     
36893     onImageLoad : function()
36894     {
36895         this.resize();
36896     },
36897     
36898     resize : function()
36899     {
36900         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36901         
36902         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36903         
36904         if(this.bgimage.length){
36905             var image = this.el.select('.roo-brick-image-view', true).first();
36906             
36907             image.setWidth(paragraph.getWidth());
36908             
36909             if(this.square){
36910                 image.setHeight(paragraph.getWidth());
36911             }
36912             
36913             this.el.setHeight(image.getHeight());
36914             paragraph.setHeight(image.getHeight());
36915             
36916         }
36917         
36918     },
36919     
36920     enter: function(e, el)
36921     {
36922         e.preventDefault();
36923         
36924         if(this.bgimage.length){
36925             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36926             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36927         }
36928     },
36929     
36930     leave: function(e, el)
36931     {
36932         e.preventDefault();
36933         
36934         if(this.bgimage.length){
36935             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36936             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36937         }
36938     }
36939     
36940 });
36941
36942  
36943
36944  /*
36945  * - LGPL
36946  *
36947  * Number field 
36948  */
36949
36950 /**
36951  * @class Roo.bootstrap.NumberField
36952  * @extends Roo.bootstrap.Input
36953  * Bootstrap NumberField class
36954  * 
36955  * 
36956  * 
36957  * 
36958  * @constructor
36959  * Create a new NumberField
36960  * @param {Object} config The config object
36961  */
36962
36963 Roo.bootstrap.NumberField = function(config){
36964     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36965 };
36966
36967 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36968     
36969     /**
36970      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36971      */
36972     allowDecimals : true,
36973     /**
36974      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36975      */
36976     decimalSeparator : ".",
36977     /**
36978      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36979      */
36980     decimalPrecision : 2,
36981     /**
36982      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36983      */
36984     allowNegative : true,
36985     
36986     /**
36987      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36988      */
36989     allowZero: true,
36990     /**
36991      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36992      */
36993     minValue : Number.NEGATIVE_INFINITY,
36994     /**
36995      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36996      */
36997     maxValue : Number.MAX_VALUE,
36998     /**
36999      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37000      */
37001     minText : "The minimum value for this field is {0}",
37002     /**
37003      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37004      */
37005     maxText : "The maximum value for this field is {0}",
37006     /**
37007      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37008      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37009      */
37010     nanText : "{0} is not a valid number",
37011     /**
37012      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37013      */
37014     thousandsDelimiter : false,
37015     /**
37016      * @cfg {String} valueAlign alignment of value
37017      */
37018     valueAlign : "left",
37019
37020     getAutoCreate : function()
37021     {
37022         var hiddenInput = {
37023             tag: 'input',
37024             type: 'hidden',
37025             id: Roo.id(),
37026             cls: 'hidden-number-input'
37027         };
37028         
37029         if (this.name) {
37030             hiddenInput.name = this.name;
37031         }
37032         
37033         this.name = '';
37034         
37035         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37036         
37037         this.name = hiddenInput.name;
37038         
37039         if(cfg.cn.length > 0) {
37040             cfg.cn.push(hiddenInput);
37041         }
37042         
37043         return cfg;
37044     },
37045
37046     // private
37047     initEvents : function()
37048     {   
37049         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37050         
37051         var allowed = "0123456789";
37052         
37053         if(this.allowDecimals){
37054             allowed += this.decimalSeparator;
37055         }
37056         
37057         if(this.allowNegative){
37058             allowed += "-";
37059         }
37060         
37061         if(this.thousandsDelimiter) {
37062             allowed += ",";
37063         }
37064         
37065         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37066         
37067         var keyPress = function(e){
37068             
37069             var k = e.getKey();
37070             
37071             var c = e.getCharCode();
37072             
37073             if(
37074                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37075                     allowed.indexOf(String.fromCharCode(c)) === -1
37076             ){
37077                 e.stopEvent();
37078                 return;
37079             }
37080             
37081             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37082                 return;
37083             }
37084             
37085             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37086                 e.stopEvent();
37087             }
37088         };
37089         
37090         this.el.on("keypress", keyPress, this);
37091     },
37092     
37093     validateValue : function(value)
37094     {
37095         
37096         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37097             return false;
37098         }
37099         
37100         var num = this.parseValue(value);
37101         
37102         if(isNaN(num)){
37103             this.markInvalid(String.format(this.nanText, value));
37104             return false;
37105         }
37106         
37107         if(num < this.minValue){
37108             this.markInvalid(String.format(this.minText, this.minValue));
37109             return false;
37110         }
37111         
37112         if(num > this.maxValue){
37113             this.markInvalid(String.format(this.maxText, this.maxValue));
37114             return false;
37115         }
37116         
37117         return true;
37118     },
37119
37120     getValue : function()
37121     {
37122         var v = this.hiddenEl().getValue();
37123         
37124         return this.fixPrecision(this.parseValue(v));
37125     },
37126
37127     parseValue : function(value)
37128     {
37129         if(this.thousandsDelimiter) {
37130             value += "";
37131             r = new RegExp(",", "g");
37132             value = value.replace(r, "");
37133         }
37134         
37135         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37136         return isNaN(value) ? '' : value;
37137     },
37138
37139     fixPrecision : function(value)
37140     {
37141         if(this.thousandsDelimiter) {
37142             value += "";
37143             r = new RegExp(",", "g");
37144             value = value.replace(r, "");
37145         }
37146         
37147         var nan = isNaN(value);
37148         
37149         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37150             return nan ? '' : value;
37151         }
37152         return parseFloat(value).toFixed(this.decimalPrecision);
37153     },
37154
37155     setValue : function(v)
37156     {
37157         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37158         
37159         this.value = v;
37160         
37161         if(this.rendered){
37162             
37163             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37164             
37165             this.inputEl().dom.value = (v == '') ? '' :
37166                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37167             
37168             if(!this.allowZero && v === '0') {
37169                 this.hiddenEl().dom.value = '';
37170                 this.inputEl().dom.value = '';
37171             }
37172             
37173             this.validate();
37174         }
37175     },
37176
37177     decimalPrecisionFcn : function(v)
37178     {
37179         return Math.floor(v);
37180     },
37181
37182     beforeBlur : function()
37183     {
37184         var v = this.parseValue(this.getRawValue());
37185         
37186         if(v || v === 0 || v === ''){
37187             this.setValue(v);
37188         }
37189     },
37190     
37191     hiddenEl : function()
37192     {
37193         return this.el.select('input.hidden-number-input',true).first();
37194     }
37195     
37196 });
37197
37198  
37199
37200 /*
37201 * Licence: LGPL
37202 */
37203
37204 /**
37205  * @class Roo.bootstrap.DocumentSlider
37206  * @extends Roo.bootstrap.Component
37207  * Bootstrap DocumentSlider class
37208  * 
37209  * @constructor
37210  * Create a new DocumentViewer
37211  * @param {Object} config The config object
37212  */
37213
37214 Roo.bootstrap.DocumentSlider = function(config){
37215     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37216     
37217     this.files = [];
37218     
37219     this.addEvents({
37220         /**
37221          * @event initial
37222          * Fire after initEvent
37223          * @param {Roo.bootstrap.DocumentSlider} this
37224          */
37225         "initial" : true,
37226         /**
37227          * @event update
37228          * Fire after update
37229          * @param {Roo.bootstrap.DocumentSlider} this
37230          */
37231         "update" : true,
37232         /**
37233          * @event click
37234          * Fire after click
37235          * @param {Roo.bootstrap.DocumentSlider} this
37236          */
37237         "click" : true
37238     });
37239 };
37240
37241 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37242     
37243     files : false,
37244     
37245     indicator : 0,
37246     
37247     getAutoCreate : function()
37248     {
37249         var cfg = {
37250             tag : 'div',
37251             cls : 'roo-document-slider',
37252             cn : [
37253                 {
37254                     tag : 'div',
37255                     cls : 'roo-document-slider-header',
37256                     cn : [
37257                         {
37258                             tag : 'div',
37259                             cls : 'roo-document-slider-header-title'
37260                         }
37261                     ]
37262                 },
37263                 {
37264                     tag : 'div',
37265                     cls : 'roo-document-slider-body',
37266                     cn : [
37267                         {
37268                             tag : 'div',
37269                             cls : 'roo-document-slider-prev',
37270                             cn : [
37271                                 {
37272                                     tag : 'i',
37273                                     cls : 'fa fa-chevron-left'
37274                                 }
37275                             ]
37276                         },
37277                         {
37278                             tag : 'div',
37279                             cls : 'roo-document-slider-thumb',
37280                             cn : [
37281                                 {
37282                                     tag : 'img',
37283                                     cls : 'roo-document-slider-image'
37284                                 }
37285                             ]
37286                         },
37287                         {
37288                             tag : 'div',
37289                             cls : 'roo-document-slider-next',
37290                             cn : [
37291                                 {
37292                                     tag : 'i',
37293                                     cls : 'fa fa-chevron-right'
37294                                 }
37295                             ]
37296                         }
37297                     ]
37298                 }
37299             ]
37300         };
37301         
37302         return cfg;
37303     },
37304     
37305     initEvents : function()
37306     {
37307         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37308         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37309         
37310         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37311         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37312         
37313         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37314         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37315         
37316         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37317         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37318         
37319         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37320         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37321         
37322         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37323         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37324         
37325         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37326         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37327         
37328         this.thumbEl.on('click', this.onClick, this);
37329         
37330         this.prevIndicator.on('click', this.prev, this);
37331         
37332         this.nextIndicator.on('click', this.next, this);
37333         
37334     },
37335     
37336     initial : function()
37337     {
37338         if(this.files.length){
37339             this.indicator = 1;
37340             this.update()
37341         }
37342         
37343         this.fireEvent('initial', this);
37344     },
37345     
37346     update : function()
37347     {
37348         this.imageEl.attr('src', this.files[this.indicator - 1]);
37349         
37350         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37351         
37352         this.prevIndicator.show();
37353         
37354         if(this.indicator == 1){
37355             this.prevIndicator.hide();
37356         }
37357         
37358         this.nextIndicator.show();
37359         
37360         if(this.indicator == this.files.length){
37361             this.nextIndicator.hide();
37362         }
37363         
37364         this.thumbEl.scrollTo('top');
37365         
37366         this.fireEvent('update', this);
37367     },
37368     
37369     onClick : function(e)
37370     {
37371         e.preventDefault();
37372         
37373         this.fireEvent('click', this);
37374     },
37375     
37376     prev : function(e)
37377     {
37378         e.preventDefault();
37379         
37380         this.indicator = Math.max(1, this.indicator - 1);
37381         
37382         this.update();
37383     },
37384     
37385     next : function(e)
37386     {
37387         e.preventDefault();
37388         
37389         this.indicator = Math.min(this.files.length, this.indicator + 1);
37390         
37391         this.update();
37392     }
37393 });
37394 /*
37395  * - LGPL
37396  *
37397  * RadioSet
37398  *
37399  *
37400  */
37401
37402 /**
37403  * @class Roo.bootstrap.RadioSet
37404  * @extends Roo.bootstrap.Input
37405  * Bootstrap RadioSet class
37406  * @cfg {String} indicatorpos (left|right) default left
37407  * @cfg {Boolean} inline (true|false) inline the element (default true)
37408  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37409  * @constructor
37410  * Create a new RadioSet
37411  * @param {Object} config The config object
37412  */
37413
37414 Roo.bootstrap.RadioSet = function(config){
37415     
37416     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37417     
37418     this.radioes = [];
37419     
37420     Roo.bootstrap.RadioSet.register(this);
37421     
37422     this.addEvents({
37423         /**
37424         * @event check
37425         * Fires when the element is checked or unchecked.
37426         * @param {Roo.bootstrap.RadioSet} this This radio
37427         * @param {Roo.bootstrap.Radio} item The checked item
37428         */
37429        check : true,
37430        /**
37431         * @event click
37432         * Fires when the element is click.
37433         * @param {Roo.bootstrap.RadioSet} this This radio set
37434         * @param {Roo.bootstrap.Radio} item The checked item
37435         * @param {Roo.EventObject} e The event object
37436         */
37437        click : true
37438     });
37439     
37440 };
37441
37442 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37443
37444     radioes : false,
37445     
37446     inline : true,
37447     
37448     weight : '',
37449     
37450     indicatorpos : 'left',
37451     
37452     getAutoCreate : function()
37453     {
37454         var label = {
37455             tag : 'label',
37456             cls : 'roo-radio-set-label',
37457             cn : [
37458                 {
37459                     tag : 'span',
37460                     html : this.fieldLabel
37461                 }
37462             ]
37463         };
37464         if (Roo.bootstrap.version == 3) {
37465             
37466             
37467             if(this.indicatorpos == 'left'){
37468                 label.cn.unshift({
37469                     tag : 'i',
37470                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37471                     tooltip : 'This field is required'
37472                 });
37473             } else {
37474                 label.cn.push({
37475                     tag : 'i',
37476                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37477                     tooltip : 'This field is required'
37478                 });
37479             }
37480         }
37481         var items = {
37482             tag : 'div',
37483             cls : 'roo-radio-set-items'
37484         };
37485         
37486         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37487         
37488         if (align === 'left' && this.fieldLabel.length) {
37489             
37490             items = {
37491                 cls : "roo-radio-set-right", 
37492                 cn: [
37493                     items
37494                 ]
37495             };
37496             
37497             if(this.labelWidth > 12){
37498                 label.style = "width: " + this.labelWidth + 'px';
37499             }
37500             
37501             if(this.labelWidth < 13 && this.labelmd == 0){
37502                 this.labelmd = this.labelWidth;
37503             }
37504             
37505             if(this.labellg > 0){
37506                 label.cls += ' col-lg-' + this.labellg;
37507                 items.cls += ' col-lg-' + (12 - this.labellg);
37508             }
37509             
37510             if(this.labelmd > 0){
37511                 label.cls += ' col-md-' + this.labelmd;
37512                 items.cls += ' col-md-' + (12 - this.labelmd);
37513             }
37514             
37515             if(this.labelsm > 0){
37516                 label.cls += ' col-sm-' + this.labelsm;
37517                 items.cls += ' col-sm-' + (12 - this.labelsm);
37518             }
37519             
37520             if(this.labelxs > 0){
37521                 label.cls += ' col-xs-' + this.labelxs;
37522                 items.cls += ' col-xs-' + (12 - this.labelxs);
37523             }
37524         }
37525         
37526         var cfg = {
37527             tag : 'div',
37528             cls : 'roo-radio-set',
37529             cn : [
37530                 {
37531                     tag : 'input',
37532                     cls : 'roo-radio-set-input',
37533                     type : 'hidden',
37534                     name : this.name,
37535                     value : this.value ? this.value :  ''
37536                 },
37537                 label,
37538                 items
37539             ]
37540         };
37541         
37542         if(this.weight.length){
37543             cfg.cls += ' roo-radio-' + this.weight;
37544         }
37545         
37546         if(this.inline) {
37547             cfg.cls += ' roo-radio-set-inline';
37548         }
37549         
37550         var settings=this;
37551         ['xs','sm','md','lg'].map(function(size){
37552             if (settings[size]) {
37553                 cfg.cls += ' col-' + size + '-' + settings[size];
37554             }
37555         });
37556         
37557         return cfg;
37558         
37559     },
37560
37561     initEvents : function()
37562     {
37563         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37564         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37565         
37566         if(!this.fieldLabel.length){
37567             this.labelEl.hide();
37568         }
37569         
37570         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37571         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37572         
37573         this.indicator = this.indicatorEl();
37574         
37575         if(this.indicator){
37576             this.indicator.addClass('invisible');
37577         }
37578         
37579         this.originalValue = this.getValue();
37580         
37581     },
37582     
37583     inputEl: function ()
37584     {
37585         return this.el.select('.roo-radio-set-input', true).first();
37586     },
37587     
37588     getChildContainer : function()
37589     {
37590         return this.itemsEl;
37591     },
37592     
37593     register : function(item)
37594     {
37595         this.radioes.push(item);
37596         
37597     },
37598     
37599     validate : function()
37600     {   
37601         if(this.getVisibilityEl().hasClass('hidden')){
37602             return true;
37603         }
37604         
37605         var valid = false;
37606         
37607         Roo.each(this.radioes, function(i){
37608             if(!i.checked){
37609                 return;
37610             }
37611             
37612             valid = true;
37613             return false;
37614         });
37615         
37616         if(this.allowBlank) {
37617             return true;
37618         }
37619         
37620         if(this.disabled || valid){
37621             this.markValid();
37622             return true;
37623         }
37624         
37625         this.markInvalid();
37626         return false;
37627         
37628     },
37629     
37630     markValid : function()
37631     {
37632         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37633             this.indicatorEl().removeClass('visible');
37634             this.indicatorEl().addClass('invisible');
37635         }
37636         
37637         
37638         if (Roo.bootstrap.version == 3) {
37639             this.el.removeClass([this.invalidClass, this.validClass]);
37640             this.el.addClass(this.validClass);
37641         } else {
37642             this.el.removeClass(['is-invalid','is-valid']);
37643             this.el.addClass(['is-valid']);
37644         }
37645         this.fireEvent('valid', this);
37646     },
37647     
37648     markInvalid : function(msg)
37649     {
37650         if(this.allowBlank || this.disabled){
37651             return;
37652         }
37653         
37654         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37655             this.indicatorEl().removeClass('invisible');
37656             this.indicatorEl().addClass('visible');
37657         }
37658         if (Roo.bootstrap.version == 3) {
37659             this.el.removeClass([this.invalidClass, this.validClass]);
37660             this.el.addClass(this.invalidClass);
37661         } else {
37662             this.el.removeClass(['is-invalid','is-valid']);
37663             this.el.addClass(['is-invalid']);
37664         }
37665         
37666         this.fireEvent('invalid', this, msg);
37667         
37668     },
37669     
37670     setValue : function(v, suppressEvent)
37671     {   
37672         if(this.value === v){
37673             return;
37674         }
37675         
37676         this.value = v;
37677         
37678         if(this.rendered){
37679             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37680         }
37681         
37682         Roo.each(this.radioes, function(i){
37683             i.checked = false;
37684             i.el.removeClass('checked');
37685         });
37686         
37687         Roo.each(this.radioes, function(i){
37688             
37689             if(i.value === v || i.value.toString() === v.toString()){
37690                 i.checked = true;
37691                 i.el.addClass('checked');
37692                 
37693                 if(suppressEvent !== true){
37694                     this.fireEvent('check', this, i);
37695                 }
37696                 
37697                 return false;
37698             }
37699             
37700         }, this);
37701         
37702         this.validate();
37703     },
37704     
37705     clearInvalid : function(){
37706         
37707         if(!this.el || this.preventMark){
37708             return;
37709         }
37710         
37711         this.el.removeClass([this.invalidClass]);
37712         
37713         this.fireEvent('valid', this);
37714     }
37715     
37716 });
37717
37718 Roo.apply(Roo.bootstrap.RadioSet, {
37719     
37720     groups: {},
37721     
37722     register : function(set)
37723     {
37724         this.groups[set.name] = set;
37725     },
37726     
37727     get: function(name) 
37728     {
37729         if (typeof(this.groups[name]) == 'undefined') {
37730             return false;
37731         }
37732         
37733         return this.groups[name] ;
37734     }
37735     
37736 });
37737 /*
37738  * Based on:
37739  * Ext JS Library 1.1.1
37740  * Copyright(c) 2006-2007, Ext JS, LLC.
37741  *
37742  * Originally Released Under LGPL - original licence link has changed is not relivant.
37743  *
37744  * Fork - LGPL
37745  * <script type="text/javascript">
37746  */
37747
37748
37749 /**
37750  * @class Roo.bootstrap.SplitBar
37751  * @extends Roo.util.Observable
37752  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37753  * <br><br>
37754  * Usage:
37755  * <pre><code>
37756 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37757                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37758 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37759 split.minSize = 100;
37760 split.maxSize = 600;
37761 split.animate = true;
37762 split.on('moved', splitterMoved);
37763 </code></pre>
37764  * @constructor
37765  * Create a new SplitBar
37766  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37767  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37768  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37769  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37770                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37771                         position of the SplitBar).
37772  */
37773 Roo.bootstrap.SplitBar = function(cfg){
37774     
37775     /** @private */
37776     
37777     //{
37778     //  dragElement : elm
37779     //  resizingElement: el,
37780         // optional..
37781     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37782     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37783         // existingProxy ???
37784     //}
37785     
37786     this.el = Roo.get(cfg.dragElement, true);
37787     this.el.dom.unselectable = "on";
37788     /** @private */
37789     this.resizingEl = Roo.get(cfg.resizingElement, true);
37790
37791     /**
37792      * @private
37793      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37794      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37795      * @type Number
37796      */
37797     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37798     
37799     /**
37800      * The minimum size of the resizing element. (Defaults to 0)
37801      * @type Number
37802      */
37803     this.minSize = 0;
37804     
37805     /**
37806      * The maximum size of the resizing element. (Defaults to 2000)
37807      * @type Number
37808      */
37809     this.maxSize = 2000;
37810     
37811     /**
37812      * Whether to animate the transition to the new size
37813      * @type Boolean
37814      */
37815     this.animate = false;
37816     
37817     /**
37818      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37819      * @type Boolean
37820      */
37821     this.useShim = false;
37822     
37823     /** @private */
37824     this.shim = null;
37825     
37826     if(!cfg.existingProxy){
37827         /** @private */
37828         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37829     }else{
37830         this.proxy = Roo.get(cfg.existingProxy).dom;
37831     }
37832     /** @private */
37833     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37834     
37835     /** @private */
37836     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37837     
37838     /** @private */
37839     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37840     
37841     /** @private */
37842     this.dragSpecs = {};
37843     
37844     /**
37845      * @private The adapter to use to positon and resize elements
37846      */
37847     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37848     this.adapter.init(this);
37849     
37850     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37851         /** @private */
37852         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37853         this.el.addClass("roo-splitbar-h");
37854     }else{
37855         /** @private */
37856         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37857         this.el.addClass("roo-splitbar-v");
37858     }
37859     
37860     this.addEvents({
37861         /**
37862          * @event resize
37863          * Fires when the splitter is moved (alias for {@link #event-moved})
37864          * @param {Roo.bootstrap.SplitBar} this
37865          * @param {Number} newSize the new width or height
37866          */
37867         "resize" : true,
37868         /**
37869          * @event moved
37870          * Fires when the splitter is moved
37871          * @param {Roo.bootstrap.SplitBar} this
37872          * @param {Number} newSize the new width or height
37873          */
37874         "moved" : true,
37875         /**
37876          * @event beforeresize
37877          * Fires before the splitter is dragged
37878          * @param {Roo.bootstrap.SplitBar} this
37879          */
37880         "beforeresize" : true,
37881
37882         "beforeapply" : true
37883     });
37884
37885     Roo.util.Observable.call(this);
37886 };
37887
37888 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37889     onStartProxyDrag : function(x, y){
37890         this.fireEvent("beforeresize", this);
37891         if(!this.overlay){
37892             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37893             o.unselectable();
37894             o.enableDisplayMode("block");
37895             // all splitbars share the same overlay
37896             Roo.bootstrap.SplitBar.prototype.overlay = o;
37897         }
37898         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37899         this.overlay.show();
37900         Roo.get(this.proxy).setDisplayed("block");
37901         var size = this.adapter.getElementSize(this);
37902         this.activeMinSize = this.getMinimumSize();;
37903         this.activeMaxSize = this.getMaximumSize();;
37904         var c1 = size - this.activeMinSize;
37905         var c2 = Math.max(this.activeMaxSize - size, 0);
37906         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37907             this.dd.resetConstraints();
37908             this.dd.setXConstraint(
37909                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37910                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37911             );
37912             this.dd.setYConstraint(0, 0);
37913         }else{
37914             this.dd.resetConstraints();
37915             this.dd.setXConstraint(0, 0);
37916             this.dd.setYConstraint(
37917                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37918                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37919             );
37920          }
37921         this.dragSpecs.startSize = size;
37922         this.dragSpecs.startPoint = [x, y];
37923         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37924     },
37925     
37926     /** 
37927      * @private Called after the drag operation by the DDProxy
37928      */
37929     onEndProxyDrag : function(e){
37930         Roo.get(this.proxy).setDisplayed(false);
37931         var endPoint = Roo.lib.Event.getXY(e);
37932         if(this.overlay){
37933             this.overlay.hide();
37934         }
37935         var newSize;
37936         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37937             newSize = this.dragSpecs.startSize + 
37938                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37939                     endPoint[0] - this.dragSpecs.startPoint[0] :
37940                     this.dragSpecs.startPoint[0] - endPoint[0]
37941                 );
37942         }else{
37943             newSize = this.dragSpecs.startSize + 
37944                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37945                     endPoint[1] - this.dragSpecs.startPoint[1] :
37946                     this.dragSpecs.startPoint[1] - endPoint[1]
37947                 );
37948         }
37949         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37950         if(newSize != this.dragSpecs.startSize){
37951             if(this.fireEvent('beforeapply', this, newSize) !== false){
37952                 this.adapter.setElementSize(this, newSize);
37953                 this.fireEvent("moved", this, newSize);
37954                 this.fireEvent("resize", this, newSize);
37955             }
37956         }
37957     },
37958     
37959     /**
37960      * Get the adapter this SplitBar uses
37961      * @return The adapter object
37962      */
37963     getAdapter : function(){
37964         return this.adapter;
37965     },
37966     
37967     /**
37968      * Set the adapter this SplitBar uses
37969      * @param {Object} adapter A SplitBar adapter object
37970      */
37971     setAdapter : function(adapter){
37972         this.adapter = adapter;
37973         this.adapter.init(this);
37974     },
37975     
37976     /**
37977      * Gets the minimum size for the resizing element
37978      * @return {Number} The minimum size
37979      */
37980     getMinimumSize : function(){
37981         return this.minSize;
37982     },
37983     
37984     /**
37985      * Sets the minimum size for the resizing element
37986      * @param {Number} minSize The minimum size
37987      */
37988     setMinimumSize : function(minSize){
37989         this.minSize = minSize;
37990     },
37991     
37992     /**
37993      * Gets the maximum size for the resizing element
37994      * @return {Number} The maximum size
37995      */
37996     getMaximumSize : function(){
37997         return this.maxSize;
37998     },
37999     
38000     /**
38001      * Sets the maximum size for the resizing element
38002      * @param {Number} maxSize The maximum size
38003      */
38004     setMaximumSize : function(maxSize){
38005         this.maxSize = maxSize;
38006     },
38007     
38008     /**
38009      * Sets the initialize size for the resizing element
38010      * @param {Number} size The initial size
38011      */
38012     setCurrentSize : function(size){
38013         var oldAnimate = this.animate;
38014         this.animate = false;
38015         this.adapter.setElementSize(this, size);
38016         this.animate = oldAnimate;
38017     },
38018     
38019     /**
38020      * Destroy this splitbar. 
38021      * @param {Boolean} removeEl True to remove the element
38022      */
38023     destroy : function(removeEl){
38024         if(this.shim){
38025             this.shim.remove();
38026         }
38027         this.dd.unreg();
38028         this.proxy.parentNode.removeChild(this.proxy);
38029         if(removeEl){
38030             this.el.remove();
38031         }
38032     }
38033 });
38034
38035 /**
38036  * @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.
38037  */
38038 Roo.bootstrap.SplitBar.createProxy = function(dir){
38039     var proxy = new Roo.Element(document.createElement("div"));
38040     proxy.unselectable();
38041     var cls = 'roo-splitbar-proxy';
38042     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38043     document.body.appendChild(proxy.dom);
38044     return proxy.dom;
38045 };
38046
38047 /** 
38048  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38049  * Default Adapter. It assumes the splitter and resizing element are not positioned
38050  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38051  */
38052 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38053 };
38054
38055 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38056     // do nothing for now
38057     init : function(s){
38058     
38059     },
38060     /**
38061      * Called before drag operations to get the current size of the resizing element. 
38062      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38063      */
38064      getElementSize : function(s){
38065         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38066             return s.resizingEl.getWidth();
38067         }else{
38068             return s.resizingEl.getHeight();
38069         }
38070     },
38071     
38072     /**
38073      * Called after drag operations to set the size of the resizing element.
38074      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38075      * @param {Number} newSize The new size to set
38076      * @param {Function} onComplete A function to be invoked when resizing is complete
38077      */
38078     setElementSize : function(s, newSize, onComplete){
38079         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38080             if(!s.animate){
38081                 s.resizingEl.setWidth(newSize);
38082                 if(onComplete){
38083                     onComplete(s, newSize);
38084                 }
38085             }else{
38086                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38087             }
38088         }else{
38089             
38090             if(!s.animate){
38091                 s.resizingEl.setHeight(newSize);
38092                 if(onComplete){
38093                     onComplete(s, newSize);
38094                 }
38095             }else{
38096                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38097             }
38098         }
38099     }
38100 };
38101
38102 /** 
38103  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38104  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38105  * Adapter that  moves the splitter element to align with the resized sizing element. 
38106  * Used with an absolute positioned SplitBar.
38107  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38108  * document.body, make sure you assign an id to the body element.
38109  */
38110 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38111     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38112     this.container = Roo.get(container);
38113 };
38114
38115 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38116     init : function(s){
38117         this.basic.init(s);
38118     },
38119     
38120     getElementSize : function(s){
38121         return this.basic.getElementSize(s);
38122     },
38123     
38124     setElementSize : function(s, newSize, onComplete){
38125         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38126     },
38127     
38128     moveSplitter : function(s){
38129         var yes = Roo.bootstrap.SplitBar;
38130         switch(s.placement){
38131             case yes.LEFT:
38132                 s.el.setX(s.resizingEl.getRight());
38133                 break;
38134             case yes.RIGHT:
38135                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38136                 break;
38137             case yes.TOP:
38138                 s.el.setY(s.resizingEl.getBottom());
38139                 break;
38140             case yes.BOTTOM:
38141                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38142                 break;
38143         }
38144     }
38145 };
38146
38147 /**
38148  * Orientation constant - Create a vertical SplitBar
38149  * @static
38150  * @type Number
38151  */
38152 Roo.bootstrap.SplitBar.VERTICAL = 1;
38153
38154 /**
38155  * Orientation constant - Create a horizontal SplitBar
38156  * @static
38157  * @type Number
38158  */
38159 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38160
38161 /**
38162  * Placement constant - The resizing element is to the left of the splitter element
38163  * @static
38164  * @type Number
38165  */
38166 Roo.bootstrap.SplitBar.LEFT = 1;
38167
38168 /**
38169  * Placement constant - The resizing element is to the right of the splitter element
38170  * @static
38171  * @type Number
38172  */
38173 Roo.bootstrap.SplitBar.RIGHT = 2;
38174
38175 /**
38176  * Placement constant - The resizing element is positioned above the splitter element
38177  * @static
38178  * @type Number
38179  */
38180 Roo.bootstrap.SplitBar.TOP = 3;
38181
38182 /**
38183  * Placement constant - The resizing element is positioned under splitter element
38184  * @static
38185  * @type Number
38186  */
38187 Roo.bootstrap.SplitBar.BOTTOM = 4;
38188 Roo.namespace("Roo.bootstrap.layout");/*
38189  * Based on:
38190  * Ext JS Library 1.1.1
38191  * Copyright(c) 2006-2007, Ext JS, LLC.
38192  *
38193  * Originally Released Under LGPL - original licence link has changed is not relivant.
38194  *
38195  * Fork - LGPL
38196  * <script type="text/javascript">
38197  */
38198
38199 /**
38200  * @class Roo.bootstrap.layout.Manager
38201  * @extends Roo.bootstrap.Component
38202  * Base class for layout managers.
38203  */
38204 Roo.bootstrap.layout.Manager = function(config)
38205 {
38206     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38207
38208
38209
38210
38211
38212     /** false to disable window resize monitoring @type Boolean */
38213     this.monitorWindowResize = true;
38214     this.regions = {};
38215     this.addEvents({
38216         /**
38217          * @event layout
38218          * Fires when a layout is performed.
38219          * @param {Roo.LayoutManager} this
38220          */
38221         "layout" : true,
38222         /**
38223          * @event regionresized
38224          * Fires when the user resizes a region.
38225          * @param {Roo.LayoutRegion} region The resized region
38226          * @param {Number} newSize The new size (width for east/west, height for north/south)
38227          */
38228         "regionresized" : true,
38229         /**
38230          * @event regioncollapsed
38231          * Fires when a region is collapsed.
38232          * @param {Roo.LayoutRegion} region The collapsed region
38233          */
38234         "regioncollapsed" : true,
38235         /**
38236          * @event regionexpanded
38237          * Fires when a region is expanded.
38238          * @param {Roo.LayoutRegion} region The expanded region
38239          */
38240         "regionexpanded" : true
38241     });
38242     this.updating = false;
38243
38244     if (config.el) {
38245         this.el = Roo.get(config.el);
38246         this.initEvents();
38247     }
38248
38249 };
38250
38251 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38252
38253
38254     regions : null,
38255
38256     monitorWindowResize : true,
38257
38258
38259     updating : false,
38260
38261
38262     onRender : function(ct, position)
38263     {
38264         if(!this.el){
38265             this.el = Roo.get(ct);
38266             this.initEvents();
38267         }
38268         //this.fireEvent('render',this);
38269     },
38270
38271
38272     initEvents: function()
38273     {
38274
38275
38276         // ie scrollbar fix
38277         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38278             document.body.scroll = "no";
38279         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38280             this.el.position('relative');
38281         }
38282         this.id = this.el.id;
38283         this.el.addClass("roo-layout-container");
38284         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38285         if(this.el.dom != document.body ) {
38286             this.el.on('resize', this.layout,this);
38287             this.el.on('show', this.layout,this);
38288         }
38289
38290     },
38291
38292     /**
38293      * Returns true if this layout is currently being updated
38294      * @return {Boolean}
38295      */
38296     isUpdating : function(){
38297         return this.updating;
38298     },
38299
38300     /**
38301      * Suspend the LayoutManager from doing auto-layouts while
38302      * making multiple add or remove calls
38303      */
38304     beginUpdate : function(){
38305         this.updating = true;
38306     },
38307
38308     /**
38309      * Restore auto-layouts and optionally disable the manager from performing a layout
38310      * @param {Boolean} noLayout true to disable a layout update
38311      */
38312     endUpdate : function(noLayout){
38313         this.updating = false;
38314         if(!noLayout){
38315             this.layout();
38316         }
38317     },
38318
38319     layout: function(){
38320         // abstract...
38321     },
38322
38323     onRegionResized : function(region, newSize){
38324         this.fireEvent("regionresized", region, newSize);
38325         this.layout();
38326     },
38327
38328     onRegionCollapsed : function(region){
38329         this.fireEvent("regioncollapsed", region);
38330     },
38331
38332     onRegionExpanded : function(region){
38333         this.fireEvent("regionexpanded", region);
38334     },
38335
38336     /**
38337      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38338      * performs box-model adjustments.
38339      * @return {Object} The size as an object {width: (the width), height: (the height)}
38340      */
38341     getViewSize : function()
38342     {
38343         var size;
38344         if(this.el.dom != document.body){
38345             size = this.el.getSize();
38346         }else{
38347             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38348         }
38349         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38350         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38351         return size;
38352     },
38353
38354     /**
38355      * Returns the Element this layout is bound to.
38356      * @return {Roo.Element}
38357      */
38358     getEl : function(){
38359         return this.el;
38360     },
38361
38362     /**
38363      * Returns the specified region.
38364      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38365      * @return {Roo.LayoutRegion}
38366      */
38367     getRegion : function(target){
38368         return this.regions[target.toLowerCase()];
38369     },
38370
38371     onWindowResize : function(){
38372         if(this.monitorWindowResize){
38373             this.layout();
38374         }
38375     }
38376 });
38377 /*
38378  * Based on:
38379  * Ext JS Library 1.1.1
38380  * Copyright(c) 2006-2007, Ext JS, LLC.
38381  *
38382  * Originally Released Under LGPL - original licence link has changed is not relivant.
38383  *
38384  * Fork - LGPL
38385  * <script type="text/javascript">
38386  */
38387 /**
38388  * @class Roo.bootstrap.layout.Border
38389  * @extends Roo.bootstrap.layout.Manager
38390  * @builder-top
38391  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38392  * please see: examples/bootstrap/nested.html<br><br>
38393  
38394 <b>The container the layout is rendered into can be either the body element or any other element.
38395 If it is not the body element, the container needs to either be an absolute positioned element,
38396 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38397 the container size if it is not the body element.</b>
38398
38399 * @constructor
38400 * Create a new Border
38401 * @param {Object} config Configuration options
38402  */
38403 Roo.bootstrap.layout.Border = function(config){
38404     config = config || {};
38405     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38406     
38407     
38408     
38409     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38410         if(config[region]){
38411             config[region].region = region;
38412             this.addRegion(config[region]);
38413         }
38414     },this);
38415     
38416 };
38417
38418 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38419
38420 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38421     
38422     parent : false, // this might point to a 'nest' or a ???
38423     
38424     /**
38425      * Creates and adds a new region if it doesn't already exist.
38426      * @param {String} target The target region key (north, south, east, west or center).
38427      * @param {Object} config The regions config object
38428      * @return {BorderLayoutRegion} The new region
38429      */
38430     addRegion : function(config)
38431     {
38432         if(!this.regions[config.region]){
38433             var r = this.factory(config);
38434             this.bindRegion(r);
38435         }
38436         return this.regions[config.region];
38437     },
38438
38439     // private (kinda)
38440     bindRegion : function(r){
38441         this.regions[r.config.region] = r;
38442         
38443         r.on("visibilitychange",    this.layout, this);
38444         r.on("paneladded",          this.layout, this);
38445         r.on("panelremoved",        this.layout, this);
38446         r.on("invalidated",         this.layout, this);
38447         r.on("resized",             this.onRegionResized, this);
38448         r.on("collapsed",           this.onRegionCollapsed, this);
38449         r.on("expanded",            this.onRegionExpanded, this);
38450     },
38451
38452     /**
38453      * Performs a layout update.
38454      */
38455     layout : function()
38456     {
38457         if(this.updating) {
38458             return;
38459         }
38460         
38461         // render all the rebions if they have not been done alreayd?
38462         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38463             if(this.regions[region] && !this.regions[region].bodyEl){
38464                 this.regions[region].onRender(this.el)
38465             }
38466         },this);
38467         
38468         var size = this.getViewSize();
38469         var w = size.width;
38470         var h = size.height;
38471         var centerW = w;
38472         var centerH = h;
38473         var centerY = 0;
38474         var centerX = 0;
38475         //var x = 0, y = 0;
38476
38477         var rs = this.regions;
38478         var north = rs["north"];
38479         var south = rs["south"]; 
38480         var west = rs["west"];
38481         var east = rs["east"];
38482         var center = rs["center"];
38483         //if(this.hideOnLayout){ // not supported anymore
38484             //c.el.setStyle("display", "none");
38485         //}
38486         if(north && north.isVisible()){
38487             var b = north.getBox();
38488             var m = north.getMargins();
38489             b.width = w - (m.left+m.right);
38490             b.x = m.left;
38491             b.y = m.top;
38492             centerY = b.height + b.y + m.bottom;
38493             centerH -= centerY;
38494             north.updateBox(this.safeBox(b));
38495         }
38496         if(south && south.isVisible()){
38497             var b = south.getBox();
38498             var m = south.getMargins();
38499             b.width = w - (m.left+m.right);
38500             b.x = m.left;
38501             var totalHeight = (b.height + m.top + m.bottom);
38502             b.y = h - totalHeight + m.top;
38503             centerH -= totalHeight;
38504             south.updateBox(this.safeBox(b));
38505         }
38506         if(west && west.isVisible()){
38507             var b = west.getBox();
38508             var m = west.getMargins();
38509             b.height = centerH - (m.top+m.bottom);
38510             b.x = m.left;
38511             b.y = centerY + m.top;
38512             var totalWidth = (b.width + m.left + m.right);
38513             centerX += totalWidth;
38514             centerW -= totalWidth;
38515             west.updateBox(this.safeBox(b));
38516         }
38517         if(east && east.isVisible()){
38518             var b = east.getBox();
38519             var m = east.getMargins();
38520             b.height = centerH - (m.top+m.bottom);
38521             var totalWidth = (b.width + m.left + m.right);
38522             b.x = w - totalWidth + m.left;
38523             b.y = centerY + m.top;
38524             centerW -= totalWidth;
38525             east.updateBox(this.safeBox(b));
38526         }
38527         if(center){
38528             var m = center.getMargins();
38529             var centerBox = {
38530                 x: centerX + m.left,
38531                 y: centerY + m.top,
38532                 width: centerW - (m.left+m.right),
38533                 height: centerH - (m.top+m.bottom)
38534             };
38535             //if(this.hideOnLayout){
38536                 //center.el.setStyle("display", "block");
38537             //}
38538             center.updateBox(this.safeBox(centerBox));
38539         }
38540         this.el.repaint();
38541         this.fireEvent("layout", this);
38542     },
38543
38544     // private
38545     safeBox : function(box){
38546         box.width = Math.max(0, box.width);
38547         box.height = Math.max(0, box.height);
38548         return box;
38549     },
38550
38551     /**
38552      * Adds a ContentPanel (or subclass) to this layout.
38553      * @param {String} target The target region key (north, south, east, west or center).
38554      * @param {Roo.ContentPanel} panel The panel to add
38555      * @return {Roo.ContentPanel} The added panel
38556      */
38557     add : function(target, panel){
38558          
38559         target = target.toLowerCase();
38560         return this.regions[target].add(panel);
38561     },
38562
38563     /**
38564      * Remove a ContentPanel (or subclass) to this layout.
38565      * @param {String} target The target region key (north, south, east, west or center).
38566      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38567      * @return {Roo.ContentPanel} The removed panel
38568      */
38569     remove : function(target, panel){
38570         target = target.toLowerCase();
38571         return this.regions[target].remove(panel);
38572     },
38573
38574     /**
38575      * Searches all regions for a panel with the specified id
38576      * @param {String} panelId
38577      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38578      */
38579     findPanel : function(panelId){
38580         var rs = this.regions;
38581         for(var target in rs){
38582             if(typeof rs[target] != "function"){
38583                 var p = rs[target].getPanel(panelId);
38584                 if(p){
38585                     return p;
38586                 }
38587             }
38588         }
38589         return null;
38590     },
38591
38592     /**
38593      * Searches all regions for a panel with the specified id and activates (shows) it.
38594      * @param {String/ContentPanel} panelId The panels id or the panel itself
38595      * @return {Roo.ContentPanel} The shown panel or null
38596      */
38597     showPanel : function(panelId) {
38598       var rs = this.regions;
38599       for(var target in rs){
38600          var r = rs[target];
38601          if(typeof r != "function"){
38602             if(r.hasPanel(panelId)){
38603                return r.showPanel(panelId);
38604             }
38605          }
38606       }
38607       return null;
38608    },
38609
38610    /**
38611      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38612      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38613      */
38614    /*
38615     restoreState : function(provider){
38616         if(!provider){
38617             provider = Roo.state.Manager;
38618         }
38619         var sm = new Roo.LayoutStateManager();
38620         sm.init(this, provider);
38621     },
38622 */
38623  
38624  
38625     /**
38626      * Adds a xtype elements to the layout.
38627      * <pre><code>
38628
38629 layout.addxtype({
38630        xtype : 'ContentPanel',
38631        region: 'west',
38632        items: [ .... ]
38633    }
38634 );
38635
38636 layout.addxtype({
38637         xtype : 'NestedLayoutPanel',
38638         region: 'west',
38639         layout: {
38640            center: { },
38641            west: { }   
38642         },
38643         items : [ ... list of content panels or nested layout panels.. ]
38644    }
38645 );
38646 </code></pre>
38647      * @param {Object} cfg Xtype definition of item to add.
38648      */
38649     addxtype : function(cfg)
38650     {
38651         // basically accepts a pannel...
38652         // can accept a layout region..!?!?
38653         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38654         
38655         
38656         // theory?  children can only be panels??
38657         
38658         //if (!cfg.xtype.match(/Panel$/)) {
38659         //    return false;
38660         //}
38661         var ret = false;
38662         
38663         if (typeof(cfg.region) == 'undefined') {
38664             Roo.log("Failed to add Panel, region was not set");
38665             Roo.log(cfg);
38666             return false;
38667         }
38668         var region = cfg.region;
38669         delete cfg.region;
38670         
38671           
38672         var xitems = [];
38673         if (cfg.items) {
38674             xitems = cfg.items;
38675             delete cfg.items;
38676         }
38677         var nb = false;
38678         
38679         if ( region == 'center') {
38680             Roo.log("Center: " + cfg.title);
38681         }
38682         
38683         
38684         switch(cfg.xtype) 
38685         {
38686             case 'Content':  // ContentPanel (el, cfg)
38687             case 'Scroll':  // ContentPanel (el, cfg)
38688             case 'View': 
38689                 cfg.autoCreate = cfg.autoCreate || true;
38690                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38691                 //} else {
38692                 //    var el = this.el.createChild();
38693                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38694                 //}
38695                 
38696                 this.add(region, ret);
38697                 break;
38698             
38699             /*
38700             case 'TreePanel': // our new panel!
38701                 cfg.el = this.el.createChild();
38702                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38703                 this.add(region, ret);
38704                 break;
38705             */
38706             
38707             case 'Nest': 
38708                 // create a new Layout (which is  a Border Layout...
38709                 
38710                 var clayout = cfg.layout;
38711                 clayout.el  = this.el.createChild();
38712                 clayout.items   = clayout.items  || [];
38713                 
38714                 delete cfg.layout;
38715                 
38716                 // replace this exitems with the clayout ones..
38717                 xitems = clayout.items;
38718                  
38719                 // force background off if it's in center...
38720                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38721                     cfg.background = false;
38722                 }
38723                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38724                 
38725                 
38726                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38727                 //console.log('adding nested layout panel '  + cfg.toSource());
38728                 this.add(region, ret);
38729                 nb = {}; /// find first...
38730                 break;
38731             
38732             case 'Grid':
38733                 
38734                 // needs grid and region
38735                 
38736                 //var el = this.getRegion(region).el.createChild();
38737                 /*
38738                  *var el = this.el.createChild();
38739                 // create the grid first...
38740                 cfg.grid.container = el;
38741                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38742                 */
38743                 
38744                 if (region == 'center' && this.active ) {
38745                     cfg.background = false;
38746                 }
38747                 
38748                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38749                 
38750                 this.add(region, ret);
38751                 /*
38752                 if (cfg.background) {
38753                     // render grid on panel activation (if panel background)
38754                     ret.on('activate', function(gp) {
38755                         if (!gp.grid.rendered) {
38756                     //        gp.grid.render(el);
38757                         }
38758                     });
38759                 } else {
38760                   //  cfg.grid.render(el);
38761                 }
38762                 */
38763                 break;
38764            
38765            
38766             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38767                 // it was the old xcomponent building that caused this before.
38768                 // espeically if border is the top element in the tree.
38769                 ret = this;
38770                 break; 
38771                 
38772                     
38773                 
38774                 
38775                 
38776             default:
38777                 /*
38778                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38779                     
38780                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38781                     this.add(region, ret);
38782                 } else {
38783                 */
38784                     Roo.log(cfg);
38785                     throw "Can not add '" + cfg.xtype + "' to Border";
38786                     return null;
38787              
38788                                 
38789              
38790         }
38791         this.beginUpdate();
38792         // add children..
38793         var region = '';
38794         var abn = {};
38795         Roo.each(xitems, function(i)  {
38796             region = nb && i.region ? i.region : false;
38797             
38798             var add = ret.addxtype(i);
38799            
38800             if (region) {
38801                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38802                 if (!i.background) {
38803                     abn[region] = nb[region] ;
38804                 }
38805             }
38806             
38807         });
38808         this.endUpdate();
38809
38810         // make the last non-background panel active..
38811         //if (nb) { Roo.log(abn); }
38812         if (nb) {
38813             
38814             for(var r in abn) {
38815                 region = this.getRegion(r);
38816                 if (region) {
38817                     // tried using nb[r], but it does not work..
38818                      
38819                     region.showPanel(abn[r]);
38820                    
38821                 }
38822             }
38823         }
38824         return ret;
38825         
38826     },
38827     
38828     
38829 // private
38830     factory : function(cfg)
38831     {
38832         
38833         var validRegions = Roo.bootstrap.layout.Border.regions;
38834
38835         var target = cfg.region;
38836         cfg.mgr = this;
38837         
38838         var r = Roo.bootstrap.layout;
38839         Roo.log(target);
38840         switch(target){
38841             case "north":
38842                 return new r.North(cfg);
38843             case "south":
38844                 return new r.South(cfg);
38845             case "east":
38846                 return new r.East(cfg);
38847             case "west":
38848                 return new r.West(cfg);
38849             case "center":
38850                 return new r.Center(cfg);
38851         }
38852         throw 'Layout region "'+target+'" not supported.';
38853     }
38854     
38855     
38856 });
38857  /*
38858  * Based on:
38859  * Ext JS Library 1.1.1
38860  * Copyright(c) 2006-2007, Ext JS, LLC.
38861  *
38862  * Originally Released Under LGPL - original licence link has changed is not relivant.
38863  *
38864  * Fork - LGPL
38865  * <script type="text/javascript">
38866  */
38867  
38868 /**
38869  * @class Roo.bootstrap.layout.Basic
38870  * @extends Roo.util.Observable
38871  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38872  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38873  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38874  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38875  * @cfg {string}   region  the region that it inhabits..
38876  * @cfg {bool}   skipConfig skip config?
38877  * 
38878
38879  */
38880 Roo.bootstrap.layout.Basic = function(config){
38881     
38882     this.mgr = config.mgr;
38883     
38884     this.position = config.region;
38885     
38886     var skipConfig = config.skipConfig;
38887     
38888     this.events = {
38889         /**
38890          * @scope Roo.BasicLayoutRegion
38891          */
38892         
38893         /**
38894          * @event beforeremove
38895          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38896          * @param {Roo.LayoutRegion} this
38897          * @param {Roo.ContentPanel} panel The panel
38898          * @param {Object} e The cancel event object
38899          */
38900         "beforeremove" : true,
38901         /**
38902          * @event invalidated
38903          * Fires when the layout for this region is changed.
38904          * @param {Roo.LayoutRegion} this
38905          */
38906         "invalidated" : true,
38907         /**
38908          * @event visibilitychange
38909          * Fires when this region is shown or hidden 
38910          * @param {Roo.LayoutRegion} this
38911          * @param {Boolean} visibility true or false
38912          */
38913         "visibilitychange" : true,
38914         /**
38915          * @event paneladded
38916          * Fires when a panel is added. 
38917          * @param {Roo.LayoutRegion} this
38918          * @param {Roo.ContentPanel} panel The panel
38919          */
38920         "paneladded" : true,
38921         /**
38922          * @event panelremoved
38923          * Fires when a panel is removed. 
38924          * @param {Roo.LayoutRegion} this
38925          * @param {Roo.ContentPanel} panel The panel
38926          */
38927         "panelremoved" : true,
38928         /**
38929          * @event beforecollapse
38930          * Fires when this region before collapse.
38931          * @param {Roo.LayoutRegion} this
38932          */
38933         "beforecollapse" : true,
38934         /**
38935          * @event collapsed
38936          * Fires when this region is collapsed.
38937          * @param {Roo.LayoutRegion} this
38938          */
38939         "collapsed" : true,
38940         /**
38941          * @event expanded
38942          * Fires when this region is expanded.
38943          * @param {Roo.LayoutRegion} this
38944          */
38945         "expanded" : true,
38946         /**
38947          * @event slideshow
38948          * Fires when this region is slid into view.
38949          * @param {Roo.LayoutRegion} this
38950          */
38951         "slideshow" : true,
38952         /**
38953          * @event slidehide
38954          * Fires when this region slides out of view. 
38955          * @param {Roo.LayoutRegion} this
38956          */
38957         "slidehide" : true,
38958         /**
38959          * @event panelactivated
38960          * Fires when a panel is activated. 
38961          * @param {Roo.LayoutRegion} this
38962          * @param {Roo.ContentPanel} panel The activated panel
38963          */
38964         "panelactivated" : true,
38965         /**
38966          * @event resized
38967          * Fires when the user resizes this region. 
38968          * @param {Roo.LayoutRegion} this
38969          * @param {Number} newSize The new size (width for east/west, height for north/south)
38970          */
38971         "resized" : true
38972     };
38973     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38974     this.panels = new Roo.util.MixedCollection();
38975     this.panels.getKey = this.getPanelId.createDelegate(this);
38976     this.box = null;
38977     this.activePanel = null;
38978     // ensure listeners are added...
38979     
38980     if (config.listeners || config.events) {
38981         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38982             listeners : config.listeners || {},
38983             events : config.events || {}
38984         });
38985     }
38986     
38987     if(skipConfig !== true){
38988         this.applyConfig(config);
38989     }
38990 };
38991
38992 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38993 {
38994     getPanelId : function(p){
38995         return p.getId();
38996     },
38997     
38998     applyConfig : function(config){
38999         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39000         this.config = config;
39001         
39002     },
39003     
39004     /**
39005      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39006      * the width, for horizontal (north, south) the height.
39007      * @param {Number} newSize The new width or height
39008      */
39009     resizeTo : function(newSize){
39010         var el = this.el ? this.el :
39011                  (this.activePanel ? this.activePanel.getEl() : null);
39012         if(el){
39013             switch(this.position){
39014                 case "east":
39015                 case "west":
39016                     el.setWidth(newSize);
39017                     this.fireEvent("resized", this, newSize);
39018                 break;
39019                 case "north":
39020                 case "south":
39021                     el.setHeight(newSize);
39022                     this.fireEvent("resized", this, newSize);
39023                 break;                
39024             }
39025         }
39026     },
39027     
39028     getBox : function(){
39029         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39030     },
39031     
39032     getMargins : function(){
39033         return this.margins;
39034     },
39035     
39036     updateBox : function(box){
39037         this.box = box;
39038         var el = this.activePanel.getEl();
39039         el.dom.style.left = box.x + "px";
39040         el.dom.style.top = box.y + "px";
39041         this.activePanel.setSize(box.width, box.height);
39042     },
39043     
39044     /**
39045      * Returns the container element for this region.
39046      * @return {Roo.Element}
39047      */
39048     getEl : function(){
39049         return this.activePanel;
39050     },
39051     
39052     /**
39053      * Returns true if this region is currently visible.
39054      * @return {Boolean}
39055      */
39056     isVisible : function(){
39057         return this.activePanel ? true : false;
39058     },
39059     
39060     setActivePanel : function(panel){
39061         panel = this.getPanel(panel);
39062         if(this.activePanel && this.activePanel != panel){
39063             this.activePanel.setActiveState(false);
39064             this.activePanel.getEl().setLeftTop(-10000,-10000);
39065         }
39066         this.activePanel = panel;
39067         panel.setActiveState(true);
39068         if(this.box){
39069             panel.setSize(this.box.width, this.box.height);
39070         }
39071         this.fireEvent("panelactivated", this, panel);
39072         this.fireEvent("invalidated");
39073     },
39074     
39075     /**
39076      * Show the specified panel.
39077      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39078      * @return {Roo.ContentPanel} The shown panel or null
39079      */
39080     showPanel : function(panel){
39081         panel = this.getPanel(panel);
39082         if(panel){
39083             this.setActivePanel(panel);
39084         }
39085         return panel;
39086     },
39087     
39088     /**
39089      * Get the active panel for this region.
39090      * @return {Roo.ContentPanel} The active panel or null
39091      */
39092     getActivePanel : function(){
39093         return this.activePanel;
39094     },
39095     
39096     /**
39097      * Add the passed ContentPanel(s)
39098      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39099      * @return {Roo.ContentPanel} The panel added (if only one was added)
39100      */
39101     add : function(panel){
39102         if(arguments.length > 1){
39103             for(var i = 0, len = arguments.length; i < len; i++) {
39104                 this.add(arguments[i]);
39105             }
39106             return null;
39107         }
39108         if(this.hasPanel(panel)){
39109             this.showPanel(panel);
39110             return panel;
39111         }
39112         var el = panel.getEl();
39113         if(el.dom.parentNode != this.mgr.el.dom){
39114             this.mgr.el.dom.appendChild(el.dom);
39115         }
39116         if(panel.setRegion){
39117             panel.setRegion(this);
39118         }
39119         this.panels.add(panel);
39120         el.setStyle("position", "absolute");
39121         if(!panel.background){
39122             this.setActivePanel(panel);
39123             if(this.config.initialSize && this.panels.getCount()==1){
39124                 this.resizeTo(this.config.initialSize);
39125             }
39126         }
39127         this.fireEvent("paneladded", this, panel);
39128         return panel;
39129     },
39130     
39131     /**
39132      * Returns true if the panel is in this region.
39133      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39134      * @return {Boolean}
39135      */
39136     hasPanel : function(panel){
39137         if(typeof panel == "object"){ // must be panel obj
39138             panel = panel.getId();
39139         }
39140         return this.getPanel(panel) ? true : false;
39141     },
39142     
39143     /**
39144      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39145      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39146      * @param {Boolean} preservePanel Overrides the config preservePanel option
39147      * @return {Roo.ContentPanel} The panel that was removed
39148      */
39149     remove : function(panel, preservePanel){
39150         panel = this.getPanel(panel);
39151         if(!panel){
39152             return null;
39153         }
39154         var e = {};
39155         this.fireEvent("beforeremove", this, panel, e);
39156         if(e.cancel === true){
39157             return null;
39158         }
39159         var panelId = panel.getId();
39160         this.panels.removeKey(panelId);
39161         return panel;
39162     },
39163     
39164     /**
39165      * Returns the panel specified or null if it's not in this region.
39166      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39167      * @return {Roo.ContentPanel}
39168      */
39169     getPanel : function(id){
39170         if(typeof id == "object"){ // must be panel obj
39171             return id;
39172         }
39173         return this.panels.get(id);
39174     },
39175     
39176     /**
39177      * Returns this regions position (north/south/east/west/center).
39178      * @return {String} 
39179      */
39180     getPosition: function(){
39181         return this.position;    
39182     }
39183 });/*
39184  * Based on:
39185  * Ext JS Library 1.1.1
39186  * Copyright(c) 2006-2007, Ext JS, LLC.
39187  *
39188  * Originally Released Under LGPL - original licence link has changed is not relivant.
39189  *
39190  * Fork - LGPL
39191  * <script type="text/javascript">
39192  */
39193  
39194 /**
39195  * @class Roo.bootstrap.layout.Region
39196  * @extends Roo.bootstrap.layout.Basic
39197  * This class represents a region in a layout manager.
39198  
39199  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39200  * @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})
39201  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39202  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39203  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39204  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39205  * @cfg {String}    title           The title for the region (overrides panel titles)
39206  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39207  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39208  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39209  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39210  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39211  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39212  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39213  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39214  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39215  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39216
39217  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39218  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39219  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39220  * @cfg {Number}    width           For East/West panels
39221  * @cfg {Number}    height          For North/South panels
39222  * @cfg {Boolean}   split           To show the splitter
39223  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39224  * 
39225  * @cfg {string}   cls             Extra CSS classes to add to region
39226  * 
39227  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39228  * @cfg {string}   region  the region that it inhabits..
39229  *
39230
39231  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39232  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39233
39234  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39235  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39236  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39237  */
39238 Roo.bootstrap.layout.Region = function(config)
39239 {
39240     this.applyConfig(config);
39241
39242     var mgr = config.mgr;
39243     var pos = config.region;
39244     config.skipConfig = true;
39245     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39246     
39247     if (mgr.el) {
39248         this.onRender(mgr.el);   
39249     }
39250      
39251     this.visible = true;
39252     this.collapsed = false;
39253     this.unrendered_panels = [];
39254 };
39255
39256 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39257
39258     position: '', // set by wrapper (eg. north/south etc..)
39259     unrendered_panels : null,  // unrendered panels.
39260     
39261     tabPosition : false,
39262     
39263     mgr: false, // points to 'Border'
39264     
39265     
39266     createBody : function(){
39267         /** This region's body element 
39268         * @type Roo.Element */
39269         this.bodyEl = this.el.createChild({
39270                 tag: "div",
39271                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39272         });
39273     },
39274
39275     onRender: function(ctr, pos)
39276     {
39277         var dh = Roo.DomHelper;
39278         /** This region's container element 
39279         * @type Roo.Element */
39280         this.el = dh.append(ctr.dom, {
39281                 tag: "div",
39282                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39283             }, true);
39284         /** This region's title element 
39285         * @type Roo.Element */
39286     
39287         this.titleEl = dh.append(this.el.dom,  {
39288                 tag: "div",
39289                 unselectable: "on",
39290                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39291                 children:[
39292                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39293                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39294                 ]
39295             }, true);
39296         
39297         this.titleEl.enableDisplayMode();
39298         /** This region's title text element 
39299         * @type HTMLElement */
39300         this.titleTextEl = this.titleEl.dom.firstChild;
39301         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39302         /*
39303         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39304         this.closeBtn.enableDisplayMode();
39305         this.closeBtn.on("click", this.closeClicked, this);
39306         this.closeBtn.hide();
39307     */
39308         this.createBody(this.config);
39309         if(this.config.hideWhenEmpty){
39310             this.hide();
39311             this.on("paneladded", this.validateVisibility, this);
39312             this.on("panelremoved", this.validateVisibility, this);
39313         }
39314         if(this.autoScroll){
39315             this.bodyEl.setStyle("overflow", "auto");
39316         }else{
39317             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39318         }
39319         //if(c.titlebar !== false){
39320             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39321                 this.titleEl.hide();
39322             }else{
39323                 this.titleEl.show();
39324                 if(this.config.title){
39325                     this.titleTextEl.innerHTML = this.config.title;
39326                 }
39327             }
39328         //}
39329         if(this.config.collapsed){
39330             this.collapse(true);
39331         }
39332         if(this.config.hidden){
39333             this.hide();
39334         }
39335         
39336         if (this.unrendered_panels && this.unrendered_panels.length) {
39337             for (var i =0;i< this.unrendered_panels.length; i++) {
39338                 this.add(this.unrendered_panels[i]);
39339             }
39340             this.unrendered_panels = null;
39341             
39342         }
39343         
39344     },
39345     
39346     applyConfig : function(c)
39347     {
39348         /*
39349          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39350             var dh = Roo.DomHelper;
39351             if(c.titlebar !== false){
39352                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39353                 this.collapseBtn.on("click", this.collapse, this);
39354                 this.collapseBtn.enableDisplayMode();
39355                 /*
39356                 if(c.showPin === true || this.showPin){
39357                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39358                     this.stickBtn.enableDisplayMode();
39359                     this.stickBtn.on("click", this.expand, this);
39360                     this.stickBtn.hide();
39361                 }
39362                 
39363             }
39364             */
39365             /** This region's collapsed element
39366             * @type Roo.Element */
39367             /*
39368              *
39369             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39370                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39371             ]}, true);
39372             
39373             if(c.floatable !== false){
39374                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39375                this.collapsedEl.on("click", this.collapseClick, this);
39376             }
39377
39378             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39379                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39380                    id: "message", unselectable: "on", style:{"float":"left"}});
39381                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39382              }
39383             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39384             this.expandBtn.on("click", this.expand, this);
39385             
39386         }
39387         
39388         if(this.collapseBtn){
39389             this.collapseBtn.setVisible(c.collapsible == true);
39390         }
39391         
39392         this.cmargins = c.cmargins || this.cmargins ||
39393                          (this.position == "west" || this.position == "east" ?
39394                              {top: 0, left: 2, right:2, bottom: 0} :
39395                              {top: 2, left: 0, right:0, bottom: 2});
39396         */
39397         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39398         
39399         
39400         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39401         
39402         this.autoScroll = c.autoScroll || false;
39403         
39404         
39405        
39406         
39407         this.duration = c.duration || .30;
39408         this.slideDuration = c.slideDuration || .45;
39409         this.config = c;
39410        
39411     },
39412     /**
39413      * Returns true if this region is currently visible.
39414      * @return {Boolean}
39415      */
39416     isVisible : function(){
39417         return this.visible;
39418     },
39419
39420     /**
39421      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39422      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39423      */
39424     //setCollapsedTitle : function(title){
39425     //    title = title || "&#160;";
39426      //   if(this.collapsedTitleTextEl){
39427       //      this.collapsedTitleTextEl.innerHTML = title;
39428        // }
39429     //},
39430
39431     getBox : function(){
39432         var b;
39433       //  if(!this.collapsed){
39434             b = this.el.getBox(false, true);
39435        // }else{
39436           //  b = this.collapsedEl.getBox(false, true);
39437         //}
39438         return b;
39439     },
39440
39441     getMargins : function(){
39442         return this.margins;
39443         //return this.collapsed ? this.cmargins : this.margins;
39444     },
39445 /*
39446     highlight : function(){
39447         this.el.addClass("x-layout-panel-dragover");
39448     },
39449
39450     unhighlight : function(){
39451         this.el.removeClass("x-layout-panel-dragover");
39452     },
39453 */
39454     updateBox : function(box)
39455     {
39456         if (!this.bodyEl) {
39457             return; // not rendered yet..
39458         }
39459         
39460         this.box = box;
39461         if(!this.collapsed){
39462             this.el.dom.style.left = box.x + "px";
39463             this.el.dom.style.top = box.y + "px";
39464             this.updateBody(box.width, box.height);
39465         }else{
39466             this.collapsedEl.dom.style.left = box.x + "px";
39467             this.collapsedEl.dom.style.top = box.y + "px";
39468             this.collapsedEl.setSize(box.width, box.height);
39469         }
39470         if(this.tabs){
39471             this.tabs.autoSizeTabs();
39472         }
39473     },
39474
39475     updateBody : function(w, h)
39476     {
39477         if(w !== null){
39478             this.el.setWidth(w);
39479             w -= this.el.getBorderWidth("rl");
39480             if(this.config.adjustments){
39481                 w += this.config.adjustments[0];
39482             }
39483         }
39484         if(h !== null && h > 0){
39485             this.el.setHeight(h);
39486             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39487             h -= this.el.getBorderWidth("tb");
39488             if(this.config.adjustments){
39489                 h += this.config.adjustments[1];
39490             }
39491             this.bodyEl.setHeight(h);
39492             if(this.tabs){
39493                 h = this.tabs.syncHeight(h);
39494             }
39495         }
39496         if(this.panelSize){
39497             w = w !== null ? w : this.panelSize.width;
39498             h = h !== null ? h : this.panelSize.height;
39499         }
39500         if(this.activePanel){
39501             var el = this.activePanel.getEl();
39502             w = w !== null ? w : el.getWidth();
39503             h = h !== null ? h : el.getHeight();
39504             this.panelSize = {width: w, height: h};
39505             this.activePanel.setSize(w, h);
39506         }
39507         if(Roo.isIE && this.tabs){
39508             this.tabs.el.repaint();
39509         }
39510     },
39511
39512     /**
39513      * Returns the container element for this region.
39514      * @return {Roo.Element}
39515      */
39516     getEl : function(){
39517         return this.el;
39518     },
39519
39520     /**
39521      * Hides this region.
39522      */
39523     hide : function(){
39524         //if(!this.collapsed){
39525             this.el.dom.style.left = "-2000px";
39526             this.el.hide();
39527         //}else{
39528          //   this.collapsedEl.dom.style.left = "-2000px";
39529          //   this.collapsedEl.hide();
39530        // }
39531         this.visible = false;
39532         this.fireEvent("visibilitychange", this, false);
39533     },
39534
39535     /**
39536      * Shows this region if it was previously hidden.
39537      */
39538     show : function(){
39539         //if(!this.collapsed){
39540             this.el.show();
39541         //}else{
39542         //    this.collapsedEl.show();
39543        // }
39544         this.visible = true;
39545         this.fireEvent("visibilitychange", this, true);
39546     },
39547 /*
39548     closeClicked : function(){
39549         if(this.activePanel){
39550             this.remove(this.activePanel);
39551         }
39552     },
39553
39554     collapseClick : function(e){
39555         if(this.isSlid){
39556            e.stopPropagation();
39557            this.slideIn();
39558         }else{
39559            e.stopPropagation();
39560            this.slideOut();
39561         }
39562     },
39563 */
39564     /**
39565      * Collapses this region.
39566      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39567      */
39568     /*
39569     collapse : function(skipAnim, skipCheck = false){
39570         if(this.collapsed) {
39571             return;
39572         }
39573         
39574         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39575             
39576             this.collapsed = true;
39577             if(this.split){
39578                 this.split.el.hide();
39579             }
39580             if(this.config.animate && skipAnim !== true){
39581                 this.fireEvent("invalidated", this);
39582                 this.animateCollapse();
39583             }else{
39584                 this.el.setLocation(-20000,-20000);
39585                 this.el.hide();
39586                 this.collapsedEl.show();
39587                 this.fireEvent("collapsed", this);
39588                 this.fireEvent("invalidated", this);
39589             }
39590         }
39591         
39592     },
39593 */
39594     animateCollapse : function(){
39595         // overridden
39596     },
39597
39598     /**
39599      * Expands this region if it was previously collapsed.
39600      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39601      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39602      */
39603     /*
39604     expand : function(e, skipAnim){
39605         if(e) {
39606             e.stopPropagation();
39607         }
39608         if(!this.collapsed || this.el.hasActiveFx()) {
39609             return;
39610         }
39611         if(this.isSlid){
39612             this.afterSlideIn();
39613             skipAnim = true;
39614         }
39615         this.collapsed = false;
39616         if(this.config.animate && skipAnim !== true){
39617             this.animateExpand();
39618         }else{
39619             this.el.show();
39620             if(this.split){
39621                 this.split.el.show();
39622             }
39623             this.collapsedEl.setLocation(-2000,-2000);
39624             this.collapsedEl.hide();
39625             this.fireEvent("invalidated", this);
39626             this.fireEvent("expanded", this);
39627         }
39628     },
39629 */
39630     animateExpand : function(){
39631         // overridden
39632     },
39633
39634     initTabs : function()
39635     {
39636         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39637         
39638         var ts = new Roo.bootstrap.panel.Tabs({
39639             el: this.bodyEl.dom,
39640             region : this,
39641             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39642             disableTooltips: this.config.disableTabTips,
39643             toolbar : this.config.toolbar
39644         });
39645         
39646         if(this.config.hideTabs){
39647             ts.stripWrap.setDisplayed(false);
39648         }
39649         this.tabs = ts;
39650         ts.resizeTabs = this.config.resizeTabs === true;
39651         ts.minTabWidth = this.config.minTabWidth || 40;
39652         ts.maxTabWidth = this.config.maxTabWidth || 250;
39653         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39654         ts.monitorResize = false;
39655         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39656         ts.bodyEl.addClass('roo-layout-tabs-body');
39657         this.panels.each(this.initPanelAsTab, this);
39658     },
39659
39660     initPanelAsTab : function(panel){
39661         var ti = this.tabs.addTab(
39662             panel.getEl().id,
39663             panel.getTitle(),
39664             null,
39665             this.config.closeOnTab && panel.isClosable(),
39666             panel.tpl
39667         );
39668         if(panel.tabTip !== undefined){
39669             ti.setTooltip(panel.tabTip);
39670         }
39671         ti.on("activate", function(){
39672               this.setActivePanel(panel);
39673         }, this);
39674         
39675         if(this.config.closeOnTab){
39676             ti.on("beforeclose", function(t, e){
39677                 e.cancel = true;
39678                 this.remove(panel);
39679             }, this);
39680         }
39681         
39682         panel.tabItem = ti;
39683         
39684         return ti;
39685     },
39686
39687     updatePanelTitle : function(panel, title)
39688     {
39689         if(this.activePanel == panel){
39690             this.updateTitle(title);
39691         }
39692         if(this.tabs){
39693             var ti = this.tabs.getTab(panel.getEl().id);
39694             ti.setText(title);
39695             if(panel.tabTip !== undefined){
39696                 ti.setTooltip(panel.tabTip);
39697             }
39698         }
39699     },
39700
39701     updateTitle : function(title){
39702         if(this.titleTextEl && !this.config.title){
39703             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39704         }
39705     },
39706
39707     setActivePanel : function(panel)
39708     {
39709         panel = this.getPanel(panel);
39710         if(this.activePanel && this.activePanel != panel){
39711             if(this.activePanel.setActiveState(false) === false){
39712                 return;
39713             }
39714         }
39715         this.activePanel = panel;
39716         panel.setActiveState(true);
39717         if(this.panelSize){
39718             panel.setSize(this.panelSize.width, this.panelSize.height);
39719         }
39720         if(this.closeBtn){
39721             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39722         }
39723         this.updateTitle(panel.getTitle());
39724         if(this.tabs){
39725             this.fireEvent("invalidated", this);
39726         }
39727         this.fireEvent("panelactivated", this, panel);
39728     },
39729
39730     /**
39731      * Shows the specified panel.
39732      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39733      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39734      */
39735     showPanel : function(panel)
39736     {
39737         panel = this.getPanel(panel);
39738         if(panel){
39739             if(this.tabs){
39740                 var tab = this.tabs.getTab(panel.getEl().id);
39741                 if(tab.isHidden()){
39742                     this.tabs.unhideTab(tab.id);
39743                 }
39744                 tab.activate();
39745             }else{
39746                 this.setActivePanel(panel);
39747             }
39748         }
39749         return panel;
39750     },
39751
39752     /**
39753      * Get the active panel for this region.
39754      * @return {Roo.ContentPanel} The active panel or null
39755      */
39756     getActivePanel : function(){
39757         return this.activePanel;
39758     },
39759
39760     validateVisibility : function(){
39761         if(this.panels.getCount() < 1){
39762             this.updateTitle("&#160;");
39763             this.closeBtn.hide();
39764             this.hide();
39765         }else{
39766             if(!this.isVisible()){
39767                 this.show();
39768             }
39769         }
39770     },
39771
39772     /**
39773      * Adds the passed ContentPanel(s) to this region.
39774      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39775      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39776      */
39777     add : function(panel)
39778     {
39779         if(arguments.length > 1){
39780             for(var i = 0, len = arguments.length; i < len; i++) {
39781                 this.add(arguments[i]);
39782             }
39783             return null;
39784         }
39785         
39786         // if we have not been rendered yet, then we can not really do much of this..
39787         if (!this.bodyEl) {
39788             this.unrendered_panels.push(panel);
39789             return panel;
39790         }
39791         
39792         
39793         
39794         
39795         if(this.hasPanel(panel)){
39796             this.showPanel(panel);
39797             return panel;
39798         }
39799         panel.setRegion(this);
39800         this.panels.add(panel);
39801        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39802             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39803             // and hide them... ???
39804             this.bodyEl.dom.appendChild(panel.getEl().dom);
39805             if(panel.background !== true){
39806                 this.setActivePanel(panel);
39807             }
39808             this.fireEvent("paneladded", this, panel);
39809             return panel;
39810         }
39811         */
39812         if(!this.tabs){
39813             this.initTabs();
39814         }else{
39815             this.initPanelAsTab(panel);
39816         }
39817         
39818         
39819         if(panel.background !== true){
39820             this.tabs.activate(panel.getEl().id);
39821         }
39822         this.fireEvent("paneladded", this, panel);
39823         return panel;
39824     },
39825
39826     /**
39827      * Hides the tab for the specified panel.
39828      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39829      */
39830     hidePanel : function(panel){
39831         if(this.tabs && (panel = this.getPanel(panel))){
39832             this.tabs.hideTab(panel.getEl().id);
39833         }
39834     },
39835
39836     /**
39837      * Unhides the tab for a previously hidden panel.
39838      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39839      */
39840     unhidePanel : function(panel){
39841         if(this.tabs && (panel = this.getPanel(panel))){
39842             this.tabs.unhideTab(panel.getEl().id);
39843         }
39844     },
39845
39846     clearPanels : function(){
39847         while(this.panels.getCount() > 0){
39848              this.remove(this.panels.first());
39849         }
39850     },
39851
39852     /**
39853      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39854      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39855      * @param {Boolean} preservePanel Overrides the config preservePanel option
39856      * @return {Roo.ContentPanel} The panel that was removed
39857      */
39858     remove : function(panel, preservePanel)
39859     {
39860         panel = this.getPanel(panel);
39861         if(!panel){
39862             return null;
39863         }
39864         var e = {};
39865         this.fireEvent("beforeremove", this, panel, e);
39866         if(e.cancel === true){
39867             return null;
39868         }
39869         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39870         var panelId = panel.getId();
39871         this.panels.removeKey(panelId);
39872         if(preservePanel){
39873             document.body.appendChild(panel.getEl().dom);
39874         }
39875         if(this.tabs){
39876             this.tabs.removeTab(panel.getEl().id);
39877         }else if (!preservePanel){
39878             this.bodyEl.dom.removeChild(panel.getEl().dom);
39879         }
39880         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39881             var p = this.panels.first();
39882             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39883             tempEl.appendChild(p.getEl().dom);
39884             this.bodyEl.update("");
39885             this.bodyEl.dom.appendChild(p.getEl().dom);
39886             tempEl = null;
39887             this.updateTitle(p.getTitle());
39888             this.tabs = null;
39889             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39890             this.setActivePanel(p);
39891         }
39892         panel.setRegion(null);
39893         if(this.activePanel == panel){
39894             this.activePanel = null;
39895         }
39896         if(this.config.autoDestroy !== false && preservePanel !== true){
39897             try{panel.destroy();}catch(e){}
39898         }
39899         this.fireEvent("panelremoved", this, panel);
39900         return panel;
39901     },
39902
39903     /**
39904      * Returns the TabPanel component used by this region
39905      * @return {Roo.TabPanel}
39906      */
39907     getTabs : function(){
39908         return this.tabs;
39909     },
39910
39911     createTool : function(parentEl, className){
39912         var btn = Roo.DomHelper.append(parentEl, {
39913             tag: "div",
39914             cls: "x-layout-tools-button",
39915             children: [ {
39916                 tag: "div",
39917                 cls: "roo-layout-tools-button-inner " + className,
39918                 html: "&#160;"
39919             }]
39920         }, true);
39921         btn.addClassOnOver("roo-layout-tools-button-over");
39922         return btn;
39923     }
39924 });/*
39925  * Based on:
39926  * Ext JS Library 1.1.1
39927  * Copyright(c) 2006-2007, Ext JS, LLC.
39928  *
39929  * Originally Released Under LGPL - original licence link has changed is not relivant.
39930  *
39931  * Fork - LGPL
39932  * <script type="text/javascript">
39933  */
39934  
39935
39936
39937 /**
39938  * @class Roo.SplitLayoutRegion
39939  * @extends Roo.LayoutRegion
39940  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39941  */
39942 Roo.bootstrap.layout.Split = function(config){
39943     this.cursor = config.cursor;
39944     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39945 };
39946
39947 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39948 {
39949     splitTip : "Drag to resize.",
39950     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39951     useSplitTips : false,
39952
39953     applyConfig : function(config){
39954         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39955     },
39956     
39957     onRender : function(ctr,pos) {
39958         
39959         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39960         if(!this.config.split){
39961             return;
39962         }
39963         if(!this.split){
39964             
39965             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39966                             tag: "div",
39967                             id: this.el.id + "-split",
39968                             cls: "roo-layout-split roo-layout-split-"+this.position,
39969                             html: "&#160;"
39970             });
39971             /** The SplitBar for this region 
39972             * @type Roo.SplitBar */
39973             // does not exist yet...
39974             Roo.log([this.position, this.orientation]);
39975             
39976             this.split = new Roo.bootstrap.SplitBar({
39977                 dragElement : splitEl,
39978                 resizingElement: this.el,
39979                 orientation : this.orientation
39980             });
39981             
39982             this.split.on("moved", this.onSplitMove, this);
39983             this.split.useShim = this.config.useShim === true;
39984             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39985             if(this.useSplitTips){
39986                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39987             }
39988             //if(config.collapsible){
39989             //    this.split.el.on("dblclick", this.collapse,  this);
39990             //}
39991         }
39992         if(typeof this.config.minSize != "undefined"){
39993             this.split.minSize = this.config.minSize;
39994         }
39995         if(typeof this.config.maxSize != "undefined"){
39996             this.split.maxSize = this.config.maxSize;
39997         }
39998         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39999             this.hideSplitter();
40000         }
40001         
40002     },
40003
40004     getHMaxSize : function(){
40005          var cmax = this.config.maxSize || 10000;
40006          var center = this.mgr.getRegion("center");
40007          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40008     },
40009
40010     getVMaxSize : function(){
40011          var cmax = this.config.maxSize || 10000;
40012          var center = this.mgr.getRegion("center");
40013          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40014     },
40015
40016     onSplitMove : function(split, newSize){
40017         this.fireEvent("resized", this, newSize);
40018     },
40019     
40020     /** 
40021      * Returns the {@link Roo.SplitBar} for this region.
40022      * @return {Roo.SplitBar}
40023      */
40024     getSplitBar : function(){
40025         return this.split;
40026     },
40027     
40028     hide : function(){
40029         this.hideSplitter();
40030         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40031     },
40032
40033     hideSplitter : function(){
40034         if(this.split){
40035             this.split.el.setLocation(-2000,-2000);
40036             this.split.el.hide();
40037         }
40038     },
40039
40040     show : function(){
40041         if(this.split){
40042             this.split.el.show();
40043         }
40044         Roo.bootstrap.layout.Split.superclass.show.call(this);
40045     },
40046     
40047     beforeSlide: function(){
40048         if(Roo.isGecko){// firefox overflow auto bug workaround
40049             this.bodyEl.clip();
40050             if(this.tabs) {
40051                 this.tabs.bodyEl.clip();
40052             }
40053             if(this.activePanel){
40054                 this.activePanel.getEl().clip();
40055                 
40056                 if(this.activePanel.beforeSlide){
40057                     this.activePanel.beforeSlide();
40058                 }
40059             }
40060         }
40061     },
40062     
40063     afterSlide : function(){
40064         if(Roo.isGecko){// firefox overflow auto bug workaround
40065             this.bodyEl.unclip();
40066             if(this.tabs) {
40067                 this.tabs.bodyEl.unclip();
40068             }
40069             if(this.activePanel){
40070                 this.activePanel.getEl().unclip();
40071                 if(this.activePanel.afterSlide){
40072                     this.activePanel.afterSlide();
40073                 }
40074             }
40075         }
40076     },
40077
40078     initAutoHide : function(){
40079         if(this.autoHide !== false){
40080             if(!this.autoHideHd){
40081                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40082                 this.autoHideHd = {
40083                     "mouseout": function(e){
40084                         if(!e.within(this.el, true)){
40085                             st.delay(500);
40086                         }
40087                     },
40088                     "mouseover" : function(e){
40089                         st.cancel();
40090                     },
40091                     scope : this
40092                 };
40093             }
40094             this.el.on(this.autoHideHd);
40095         }
40096     },
40097
40098     clearAutoHide : function(){
40099         if(this.autoHide !== false){
40100             this.el.un("mouseout", this.autoHideHd.mouseout);
40101             this.el.un("mouseover", this.autoHideHd.mouseover);
40102         }
40103     },
40104
40105     clearMonitor : function(){
40106         Roo.get(document).un("click", this.slideInIf, this);
40107     },
40108
40109     // these names are backwards but not changed for compat
40110     slideOut : function(){
40111         if(this.isSlid || this.el.hasActiveFx()){
40112             return;
40113         }
40114         this.isSlid = true;
40115         if(this.collapseBtn){
40116             this.collapseBtn.hide();
40117         }
40118         this.closeBtnState = this.closeBtn.getStyle('display');
40119         this.closeBtn.hide();
40120         if(this.stickBtn){
40121             this.stickBtn.show();
40122         }
40123         this.el.show();
40124         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40125         this.beforeSlide();
40126         this.el.setStyle("z-index", 10001);
40127         this.el.slideIn(this.getSlideAnchor(), {
40128             callback: function(){
40129                 this.afterSlide();
40130                 this.initAutoHide();
40131                 Roo.get(document).on("click", this.slideInIf, this);
40132                 this.fireEvent("slideshow", this);
40133             },
40134             scope: this,
40135             block: true
40136         });
40137     },
40138
40139     afterSlideIn : function(){
40140         this.clearAutoHide();
40141         this.isSlid = false;
40142         this.clearMonitor();
40143         this.el.setStyle("z-index", "");
40144         if(this.collapseBtn){
40145             this.collapseBtn.show();
40146         }
40147         this.closeBtn.setStyle('display', this.closeBtnState);
40148         if(this.stickBtn){
40149             this.stickBtn.hide();
40150         }
40151         this.fireEvent("slidehide", this);
40152     },
40153
40154     slideIn : function(cb){
40155         if(!this.isSlid || this.el.hasActiveFx()){
40156             Roo.callback(cb);
40157             return;
40158         }
40159         this.isSlid = false;
40160         this.beforeSlide();
40161         this.el.slideOut(this.getSlideAnchor(), {
40162             callback: function(){
40163                 this.el.setLeftTop(-10000, -10000);
40164                 this.afterSlide();
40165                 this.afterSlideIn();
40166                 Roo.callback(cb);
40167             },
40168             scope: this,
40169             block: true
40170         });
40171     },
40172     
40173     slideInIf : function(e){
40174         if(!e.within(this.el)){
40175             this.slideIn();
40176         }
40177     },
40178
40179     animateCollapse : function(){
40180         this.beforeSlide();
40181         this.el.setStyle("z-index", 20000);
40182         var anchor = this.getSlideAnchor();
40183         this.el.slideOut(anchor, {
40184             callback : function(){
40185                 this.el.setStyle("z-index", "");
40186                 this.collapsedEl.slideIn(anchor, {duration:.3});
40187                 this.afterSlide();
40188                 this.el.setLocation(-10000,-10000);
40189                 this.el.hide();
40190                 this.fireEvent("collapsed", this);
40191             },
40192             scope: this,
40193             block: true
40194         });
40195     },
40196
40197     animateExpand : function(){
40198         this.beforeSlide();
40199         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40200         this.el.setStyle("z-index", 20000);
40201         this.collapsedEl.hide({
40202             duration:.1
40203         });
40204         this.el.slideIn(this.getSlideAnchor(), {
40205             callback : function(){
40206                 this.el.setStyle("z-index", "");
40207                 this.afterSlide();
40208                 if(this.split){
40209                     this.split.el.show();
40210                 }
40211                 this.fireEvent("invalidated", this);
40212                 this.fireEvent("expanded", this);
40213             },
40214             scope: this,
40215             block: true
40216         });
40217     },
40218
40219     anchors : {
40220         "west" : "left",
40221         "east" : "right",
40222         "north" : "top",
40223         "south" : "bottom"
40224     },
40225
40226     sanchors : {
40227         "west" : "l",
40228         "east" : "r",
40229         "north" : "t",
40230         "south" : "b"
40231     },
40232
40233     canchors : {
40234         "west" : "tl-tr",
40235         "east" : "tr-tl",
40236         "north" : "tl-bl",
40237         "south" : "bl-tl"
40238     },
40239
40240     getAnchor : function(){
40241         return this.anchors[this.position];
40242     },
40243
40244     getCollapseAnchor : function(){
40245         return this.canchors[this.position];
40246     },
40247
40248     getSlideAnchor : function(){
40249         return this.sanchors[this.position];
40250     },
40251
40252     getAlignAdj : function(){
40253         var cm = this.cmargins;
40254         switch(this.position){
40255             case "west":
40256                 return [0, 0];
40257             break;
40258             case "east":
40259                 return [0, 0];
40260             break;
40261             case "north":
40262                 return [0, 0];
40263             break;
40264             case "south":
40265                 return [0, 0];
40266             break;
40267         }
40268     },
40269
40270     getExpandAdj : function(){
40271         var c = this.collapsedEl, cm = this.cmargins;
40272         switch(this.position){
40273             case "west":
40274                 return [-(cm.right+c.getWidth()+cm.left), 0];
40275             break;
40276             case "east":
40277                 return [cm.right+c.getWidth()+cm.left, 0];
40278             break;
40279             case "north":
40280                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40281             break;
40282             case "south":
40283                 return [0, cm.top+cm.bottom+c.getHeight()];
40284             break;
40285         }
40286     }
40287 });/*
40288  * Based on:
40289  * Ext JS Library 1.1.1
40290  * Copyright(c) 2006-2007, Ext JS, LLC.
40291  *
40292  * Originally Released Under LGPL - original licence link has changed is not relivant.
40293  *
40294  * Fork - LGPL
40295  * <script type="text/javascript">
40296  */
40297 /*
40298  * These classes are private internal classes
40299  */
40300 Roo.bootstrap.layout.Center = function(config){
40301     config.region = "center";
40302     Roo.bootstrap.layout.Region.call(this, config);
40303     this.visible = true;
40304     this.minWidth = config.minWidth || 20;
40305     this.minHeight = config.minHeight || 20;
40306 };
40307
40308 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40309     hide : function(){
40310         // center panel can't be hidden
40311     },
40312     
40313     show : function(){
40314         // center panel can't be hidden
40315     },
40316     
40317     getMinWidth: function(){
40318         return this.minWidth;
40319     },
40320     
40321     getMinHeight: function(){
40322         return this.minHeight;
40323     }
40324 });
40325
40326
40327
40328
40329  
40330
40331
40332
40333
40334
40335
40336 Roo.bootstrap.layout.North = function(config)
40337 {
40338     config.region = 'north';
40339     config.cursor = 'n-resize';
40340     
40341     Roo.bootstrap.layout.Split.call(this, config);
40342     
40343     
40344     if(this.split){
40345         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40346         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40347         this.split.el.addClass("roo-layout-split-v");
40348     }
40349     //var size = config.initialSize || config.height;
40350     //if(this.el && typeof size != "undefined"){
40351     //    this.el.setHeight(size);
40352     //}
40353 };
40354 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40355 {
40356     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40357      
40358      
40359     onRender : function(ctr, pos)
40360     {
40361         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40362         var size = this.config.initialSize || this.config.height;
40363         if(this.el && typeof size != "undefined"){
40364             this.el.setHeight(size);
40365         }
40366     
40367     },
40368     
40369     getBox : function(){
40370         if(this.collapsed){
40371             return this.collapsedEl.getBox();
40372         }
40373         var box = this.el.getBox();
40374         if(this.split){
40375             box.height += this.split.el.getHeight();
40376         }
40377         return box;
40378     },
40379     
40380     updateBox : function(box){
40381         if(this.split && !this.collapsed){
40382             box.height -= this.split.el.getHeight();
40383             this.split.el.setLeft(box.x);
40384             this.split.el.setTop(box.y+box.height);
40385             this.split.el.setWidth(box.width);
40386         }
40387         if(this.collapsed){
40388             this.updateBody(box.width, null);
40389         }
40390         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40391     }
40392 });
40393
40394
40395
40396
40397
40398 Roo.bootstrap.layout.South = function(config){
40399     config.region = 'south';
40400     config.cursor = 's-resize';
40401     Roo.bootstrap.layout.Split.call(this, config);
40402     if(this.split){
40403         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40404         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40405         this.split.el.addClass("roo-layout-split-v");
40406     }
40407     
40408 };
40409
40410 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40411     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40412     
40413     onRender : function(ctr, pos)
40414     {
40415         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40416         var size = this.config.initialSize || this.config.height;
40417         if(this.el && typeof size != "undefined"){
40418             this.el.setHeight(size);
40419         }
40420     
40421     },
40422     
40423     getBox : function(){
40424         if(this.collapsed){
40425             return this.collapsedEl.getBox();
40426         }
40427         var box = this.el.getBox();
40428         if(this.split){
40429             var sh = this.split.el.getHeight();
40430             box.height += sh;
40431             box.y -= sh;
40432         }
40433         return box;
40434     },
40435     
40436     updateBox : function(box){
40437         if(this.split && !this.collapsed){
40438             var sh = this.split.el.getHeight();
40439             box.height -= sh;
40440             box.y += sh;
40441             this.split.el.setLeft(box.x);
40442             this.split.el.setTop(box.y-sh);
40443             this.split.el.setWidth(box.width);
40444         }
40445         if(this.collapsed){
40446             this.updateBody(box.width, null);
40447         }
40448         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40449     }
40450 });
40451
40452 Roo.bootstrap.layout.East = function(config){
40453     config.region = "east";
40454     config.cursor = "e-resize";
40455     Roo.bootstrap.layout.Split.call(this, config);
40456     if(this.split){
40457         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40458         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40459         this.split.el.addClass("roo-layout-split-h");
40460     }
40461     
40462 };
40463 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40464     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40465     
40466     onRender : function(ctr, pos)
40467     {
40468         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40469         var size = this.config.initialSize || this.config.width;
40470         if(this.el && typeof size != "undefined"){
40471             this.el.setWidth(size);
40472         }
40473     
40474     },
40475     
40476     getBox : function(){
40477         if(this.collapsed){
40478             return this.collapsedEl.getBox();
40479         }
40480         var box = this.el.getBox();
40481         if(this.split){
40482             var sw = this.split.el.getWidth();
40483             box.width += sw;
40484             box.x -= sw;
40485         }
40486         return box;
40487     },
40488
40489     updateBox : function(box){
40490         if(this.split && !this.collapsed){
40491             var sw = this.split.el.getWidth();
40492             box.width -= sw;
40493             this.split.el.setLeft(box.x);
40494             this.split.el.setTop(box.y);
40495             this.split.el.setHeight(box.height);
40496             box.x += sw;
40497         }
40498         if(this.collapsed){
40499             this.updateBody(null, box.height);
40500         }
40501         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40502     }
40503 });
40504
40505 Roo.bootstrap.layout.West = function(config){
40506     config.region = "west";
40507     config.cursor = "w-resize";
40508     
40509     Roo.bootstrap.layout.Split.call(this, config);
40510     if(this.split){
40511         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40512         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40513         this.split.el.addClass("roo-layout-split-h");
40514     }
40515     
40516 };
40517 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40518     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40519     
40520     onRender: function(ctr, pos)
40521     {
40522         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40523         var size = this.config.initialSize || this.config.width;
40524         if(typeof size != "undefined"){
40525             this.el.setWidth(size);
40526         }
40527     },
40528     
40529     getBox : function(){
40530         if(this.collapsed){
40531             return this.collapsedEl.getBox();
40532         }
40533         var box = this.el.getBox();
40534         if (box.width == 0) {
40535             box.width = this.config.width; // kludge?
40536         }
40537         if(this.split){
40538             box.width += this.split.el.getWidth();
40539         }
40540         return box;
40541     },
40542     
40543     updateBox : function(box){
40544         if(this.split && !this.collapsed){
40545             var sw = this.split.el.getWidth();
40546             box.width -= sw;
40547             this.split.el.setLeft(box.x+box.width);
40548             this.split.el.setTop(box.y);
40549             this.split.el.setHeight(box.height);
40550         }
40551         if(this.collapsed){
40552             this.updateBody(null, box.height);
40553         }
40554         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40555     }
40556 });Roo.namespace("Roo.bootstrap.panel");/*
40557  * Based on:
40558  * Ext JS Library 1.1.1
40559  * Copyright(c) 2006-2007, Ext JS, LLC.
40560  *
40561  * Originally Released Under LGPL - original licence link has changed is not relivant.
40562  *
40563  * Fork - LGPL
40564  * <script type="text/javascript">
40565  */
40566 /**
40567  * @class Roo.ContentPanel
40568  * @extends Roo.util.Observable
40569  * @builder-top
40570  * A basic ContentPanel element.
40571  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40572  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40573  * @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
40574  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40575  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40576  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40577  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40578  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40579  * @cfg {String} title          The title for this panel
40580  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40581  * @cfg {String} url            Calls {@link #setUrl} with this value
40582  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40583  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40584  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40585  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40586  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40587  * @cfg {Boolean} badges render the badges
40588  * @cfg {String} cls  extra classes to use  
40589  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40590
40591  * @constructor
40592  * Create a new ContentPanel.
40593  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40594  * @param {String/Object} config A string to set only the title or a config object
40595  * @param {String} content (optional) Set the HTML content for this panel
40596  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40597  */
40598 Roo.bootstrap.panel.Content = function( config){
40599     
40600     this.tpl = config.tpl || false;
40601     
40602     var el = config.el;
40603     var content = config.content;
40604
40605     if(config.autoCreate){ // xtype is available if this is called from factory
40606         el = Roo.id();
40607     }
40608     this.el = Roo.get(el);
40609     if(!this.el && config && config.autoCreate){
40610         if(typeof config.autoCreate == "object"){
40611             if(!config.autoCreate.id){
40612                 config.autoCreate.id = config.id||el;
40613             }
40614             this.el = Roo.DomHelper.append(document.body,
40615                         config.autoCreate, true);
40616         }else{
40617             var elcfg =  {
40618                 tag: "div",
40619                 cls: (config.cls || '') +
40620                     (config.background ? ' bg-' + config.background : '') +
40621                     " roo-layout-inactive-content",
40622                 id: config.id||el
40623             };
40624             if (config.iframe) {
40625                 elcfg.cn = [
40626                     {
40627                         tag : 'iframe',
40628                         style : 'border: 0px',
40629                         src : 'about:blank'
40630                     }
40631                 ];
40632             }
40633               
40634             if (config.html) {
40635                 elcfg.html = config.html;
40636                 
40637             }
40638                         
40639             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40640             if (config.iframe) {
40641                 this.iframeEl = this.el.select('iframe',true).first();
40642             }
40643             
40644         }
40645     } 
40646     this.closable = false;
40647     this.loaded = false;
40648     this.active = false;
40649    
40650       
40651     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40652         
40653         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40654         
40655         this.wrapEl = this.el; //this.el.wrap();
40656         var ti = [];
40657         if (config.toolbar.items) {
40658             ti = config.toolbar.items ;
40659             delete config.toolbar.items ;
40660         }
40661         
40662         var nitems = [];
40663         this.toolbar.render(this.wrapEl, 'before');
40664         for(var i =0;i < ti.length;i++) {
40665           //  Roo.log(['add child', items[i]]);
40666             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40667         }
40668         this.toolbar.items = nitems;
40669         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40670         delete config.toolbar;
40671         
40672     }
40673     /*
40674     // xtype created footer. - not sure if will work as we normally have to render first..
40675     if (this.footer && !this.footer.el && this.footer.xtype) {
40676         if (!this.wrapEl) {
40677             this.wrapEl = this.el.wrap();
40678         }
40679     
40680         this.footer.container = this.wrapEl.createChild();
40681          
40682         this.footer = Roo.factory(this.footer, Roo);
40683         
40684     }
40685     */
40686     
40687      if(typeof config == "string"){
40688         this.title = config;
40689     }else{
40690         Roo.apply(this, config);
40691     }
40692     
40693     if(this.resizeEl){
40694         this.resizeEl = Roo.get(this.resizeEl, true);
40695     }else{
40696         this.resizeEl = this.el;
40697     }
40698     // handle view.xtype
40699     
40700  
40701     
40702     
40703     this.addEvents({
40704         /**
40705          * @event activate
40706          * Fires when this panel is activated. 
40707          * @param {Roo.ContentPanel} this
40708          */
40709         "activate" : true,
40710         /**
40711          * @event deactivate
40712          * Fires when this panel is activated. 
40713          * @param {Roo.ContentPanel} this
40714          */
40715         "deactivate" : true,
40716
40717         /**
40718          * @event resize
40719          * Fires when this panel is resized if fitToFrame is true.
40720          * @param {Roo.ContentPanel} this
40721          * @param {Number} width The width after any component adjustments
40722          * @param {Number} height The height after any component adjustments
40723          */
40724         "resize" : true,
40725         
40726          /**
40727          * @event render
40728          * Fires when this tab is created
40729          * @param {Roo.ContentPanel} this
40730          */
40731         "render" : true,
40732         
40733           /**
40734          * @event scroll
40735          * Fires when this content is scrolled
40736          * @param {Roo.ContentPanel} this
40737          * @param {Event} scrollEvent
40738          */
40739         "scroll" : true
40740         
40741         
40742         
40743     });
40744     
40745
40746     
40747     
40748     if(this.autoScroll && !this.iframe){
40749         this.resizeEl.setStyle("overflow", "auto");
40750         this.resizeEl.on('scroll', this.onScroll, this);
40751     } else {
40752         // fix randome scrolling
40753         //this.el.on('scroll', function() {
40754         //    Roo.log('fix random scolling');
40755         //    this.scrollTo('top',0); 
40756         //});
40757     }
40758     content = content || this.content;
40759     if(content){
40760         this.setContent(content);
40761     }
40762     if(config && config.url){
40763         this.setUrl(this.url, this.params, this.loadOnce);
40764     }
40765     
40766     
40767     
40768     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40769     
40770     if (this.view && typeof(this.view.xtype) != 'undefined') {
40771         this.view.el = this.el.appendChild(document.createElement("div"));
40772         this.view = Roo.factory(this.view); 
40773         this.view.render  &&  this.view.render(false, '');  
40774     }
40775     
40776     
40777     this.fireEvent('render', this);
40778 };
40779
40780 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40781     
40782     cls : '',
40783     background : '',
40784     
40785     tabTip : '',
40786     
40787     iframe : false,
40788     iframeEl : false,
40789     
40790     /* Resize Element - use this to work out scroll etc. */
40791     resizeEl : false,
40792     
40793     setRegion : function(region){
40794         this.region = region;
40795         this.setActiveClass(region && !this.background);
40796     },
40797     
40798     
40799     setActiveClass: function(state)
40800     {
40801         if(state){
40802            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40803            this.el.setStyle('position','relative');
40804         }else{
40805            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40806            this.el.setStyle('position', 'absolute');
40807         } 
40808     },
40809     
40810     /**
40811      * Returns the toolbar for this Panel if one was configured. 
40812      * @return {Roo.Toolbar} 
40813      */
40814     getToolbar : function(){
40815         return this.toolbar;
40816     },
40817     
40818     setActiveState : function(active)
40819     {
40820         this.active = active;
40821         this.setActiveClass(active);
40822         if(!active){
40823             if(this.fireEvent("deactivate", this) === false){
40824                 return false;
40825             }
40826             return true;
40827         }
40828         this.fireEvent("activate", this);
40829         return true;
40830     },
40831     /**
40832      * Updates this panel's element (not for iframe)
40833      * @param {String} content The new content
40834      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40835     */
40836     setContent : function(content, loadScripts){
40837         if (this.iframe) {
40838             return;
40839         }
40840         
40841         this.el.update(content, loadScripts);
40842     },
40843
40844     ignoreResize : function(w, h){
40845         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40846             return true;
40847         }else{
40848             this.lastSize = {width: w, height: h};
40849             return false;
40850         }
40851     },
40852     /**
40853      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40854      * @return {Roo.UpdateManager} The UpdateManager
40855      */
40856     getUpdateManager : function(){
40857         if (this.iframe) {
40858             return false;
40859         }
40860         return this.el.getUpdateManager();
40861     },
40862      /**
40863      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40864      * Does not work with IFRAME contents
40865      * @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:
40866 <pre><code>
40867 panel.load({
40868     url: "your-url.php",
40869     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40870     callback: yourFunction,
40871     scope: yourObject, //(optional scope)
40872     discardUrl: false,
40873     nocache: false,
40874     text: "Loading...",
40875     timeout: 30,
40876     scripts: false
40877 });
40878 </code></pre>
40879      
40880      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40881      * 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.
40882      * @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}
40883      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40884      * @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.
40885      * @return {Roo.ContentPanel} this
40886      */
40887     load : function(){
40888         
40889         if (this.iframe) {
40890             return this;
40891         }
40892         
40893         var um = this.el.getUpdateManager();
40894         um.update.apply(um, arguments);
40895         return this;
40896     },
40897
40898
40899     /**
40900      * 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.
40901      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40902      * @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)
40903      * @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)
40904      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40905      */
40906     setUrl : function(url, params, loadOnce){
40907         if (this.iframe) {
40908             this.iframeEl.dom.src = url;
40909             return false;
40910         }
40911         
40912         if(this.refreshDelegate){
40913             this.removeListener("activate", this.refreshDelegate);
40914         }
40915         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40916         this.on("activate", this.refreshDelegate);
40917         return this.el.getUpdateManager();
40918     },
40919     
40920     _handleRefresh : function(url, params, loadOnce){
40921         if(!loadOnce || !this.loaded){
40922             var updater = this.el.getUpdateManager();
40923             updater.update(url, params, this._setLoaded.createDelegate(this));
40924         }
40925     },
40926     
40927     _setLoaded : function(){
40928         this.loaded = true;
40929     }, 
40930     
40931     /**
40932      * Returns this panel's id
40933      * @return {String} 
40934      */
40935     getId : function(){
40936         return this.el.id;
40937     },
40938     
40939     /** 
40940      * Returns this panel's element - used by regiosn to add.
40941      * @return {Roo.Element} 
40942      */
40943     getEl : function(){
40944         return this.wrapEl || this.el;
40945     },
40946     
40947    
40948     
40949     adjustForComponents : function(width, height)
40950     {
40951         //Roo.log('adjustForComponents ');
40952         if(this.resizeEl != this.el){
40953             width -= this.el.getFrameWidth('lr');
40954             height -= this.el.getFrameWidth('tb');
40955         }
40956         if(this.toolbar){
40957             var te = this.toolbar.getEl();
40958             te.setWidth(width);
40959             height -= te.getHeight();
40960         }
40961         if(this.footer){
40962             var te = this.footer.getEl();
40963             te.setWidth(width);
40964             height -= te.getHeight();
40965         }
40966         
40967         
40968         if(this.adjustments){
40969             width += this.adjustments[0];
40970             height += this.adjustments[1];
40971         }
40972         return {"width": width, "height": height};
40973     },
40974     
40975     setSize : function(width, height){
40976         if(this.fitToFrame && !this.ignoreResize(width, height)){
40977             if(this.fitContainer && this.resizeEl != this.el){
40978                 this.el.setSize(width, height);
40979             }
40980             var size = this.adjustForComponents(width, height);
40981             if (this.iframe) {
40982                 this.iframeEl.setSize(width,height);
40983             }
40984             
40985             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40986             this.fireEvent('resize', this, size.width, size.height);
40987             
40988             
40989         }
40990     },
40991     
40992     /**
40993      * Returns this panel's title
40994      * @return {String} 
40995      */
40996     getTitle : function(){
40997         
40998         if (typeof(this.title) != 'object') {
40999             return this.title;
41000         }
41001         
41002         var t = '';
41003         for (var k in this.title) {
41004             if (!this.title.hasOwnProperty(k)) {
41005                 continue;
41006             }
41007             
41008             if (k.indexOf('-') >= 0) {
41009                 var s = k.split('-');
41010                 for (var i = 0; i<s.length; i++) {
41011                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41012                 }
41013             } else {
41014                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41015             }
41016         }
41017         return t;
41018     },
41019     
41020     /**
41021      * Set this panel's title
41022      * @param {String} title
41023      */
41024     setTitle : function(title){
41025         this.title = title;
41026         if(this.region){
41027             this.region.updatePanelTitle(this, title);
41028         }
41029     },
41030     
41031     /**
41032      * Returns true is this panel was configured to be closable
41033      * @return {Boolean} 
41034      */
41035     isClosable : function(){
41036         return this.closable;
41037     },
41038     
41039     beforeSlide : function(){
41040         this.el.clip();
41041         this.resizeEl.clip();
41042     },
41043     
41044     afterSlide : function(){
41045         this.el.unclip();
41046         this.resizeEl.unclip();
41047     },
41048     
41049     /**
41050      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41051      *   Will fail silently if the {@link #setUrl} method has not been called.
41052      *   This does not activate the panel, just updates its content.
41053      */
41054     refresh : function(){
41055         if(this.refreshDelegate){
41056            this.loaded = false;
41057            this.refreshDelegate();
41058         }
41059     },
41060     
41061     /**
41062      * Destroys this panel
41063      */
41064     destroy : function(){
41065         this.el.removeAllListeners();
41066         var tempEl = document.createElement("span");
41067         tempEl.appendChild(this.el.dom);
41068         tempEl.innerHTML = "";
41069         this.el.remove();
41070         this.el = null;
41071     },
41072     
41073     /**
41074      * form - if the content panel contains a form - this is a reference to it.
41075      * @type {Roo.form.Form}
41076      */
41077     form : false,
41078     /**
41079      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41080      *    This contains a reference to it.
41081      * @type {Roo.View}
41082      */
41083     view : false,
41084     
41085       /**
41086      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41087      * <pre><code>
41088
41089 layout.addxtype({
41090        xtype : 'Form',
41091        items: [ .... ]
41092    }
41093 );
41094
41095 </code></pre>
41096      * @param {Object} cfg Xtype definition of item to add.
41097      */
41098     
41099     
41100     getChildContainer: function () {
41101         return this.getEl();
41102     },
41103     
41104     
41105     onScroll : function(e)
41106     {
41107         this.fireEvent('scroll', this, e);
41108     }
41109     
41110     
41111     /*
41112         var  ret = new Roo.factory(cfg);
41113         return ret;
41114         
41115         
41116         // add form..
41117         if (cfg.xtype.match(/^Form$/)) {
41118             
41119             var el;
41120             //if (this.footer) {
41121             //    el = this.footer.container.insertSibling(false, 'before');
41122             //} else {
41123                 el = this.el.createChild();
41124             //}
41125
41126             this.form = new  Roo.form.Form(cfg);
41127             
41128             
41129             if ( this.form.allItems.length) {
41130                 this.form.render(el.dom);
41131             }
41132             return this.form;
41133         }
41134         // should only have one of theses..
41135         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41136             // views.. should not be just added - used named prop 'view''
41137             
41138             cfg.el = this.el.appendChild(document.createElement("div"));
41139             // factory?
41140             
41141             var ret = new Roo.factory(cfg);
41142              
41143              ret.render && ret.render(false, ''); // render blank..
41144             this.view = ret;
41145             return ret;
41146         }
41147         return false;
41148     }
41149     \*/
41150 });
41151  
41152 /**
41153  * @class Roo.bootstrap.panel.Grid
41154  * @extends Roo.bootstrap.panel.Content
41155  * @constructor
41156  * Create a new GridPanel.
41157  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41158  * @param {Object} config A the config object
41159   
41160  */
41161
41162
41163
41164 Roo.bootstrap.panel.Grid = function(config)
41165 {
41166     
41167       
41168     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41169         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41170
41171     config.el = this.wrapper;
41172     //this.el = this.wrapper;
41173     
41174       if (config.container) {
41175         // ctor'ed from a Border/panel.grid
41176         
41177         
41178         this.wrapper.setStyle("overflow", "hidden");
41179         this.wrapper.addClass('roo-grid-container');
41180
41181     }
41182     
41183     
41184     if(config.toolbar){
41185         var tool_el = this.wrapper.createChild();    
41186         this.toolbar = Roo.factory(config.toolbar);
41187         var ti = [];
41188         if (config.toolbar.items) {
41189             ti = config.toolbar.items ;
41190             delete config.toolbar.items ;
41191         }
41192         
41193         var nitems = [];
41194         this.toolbar.render(tool_el);
41195         for(var i =0;i < ti.length;i++) {
41196           //  Roo.log(['add child', items[i]]);
41197             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41198         }
41199         this.toolbar.items = nitems;
41200         
41201         delete config.toolbar;
41202     }
41203     
41204     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41205     config.grid.scrollBody = true;;
41206     config.grid.monitorWindowResize = false; // turn off autosizing
41207     config.grid.autoHeight = false;
41208     config.grid.autoWidth = false;
41209     
41210     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41211     
41212     if (config.background) {
41213         // render grid on panel activation (if panel background)
41214         this.on('activate', function(gp) {
41215             if (!gp.grid.rendered) {
41216                 gp.grid.render(this.wrapper);
41217                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41218             }
41219         });
41220             
41221     } else {
41222         this.grid.render(this.wrapper);
41223         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41224
41225     }
41226     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41227     // ??? needed ??? config.el = this.wrapper;
41228     
41229     
41230     
41231   
41232     // xtype created footer. - not sure if will work as we normally have to render first..
41233     if (this.footer && !this.footer.el && this.footer.xtype) {
41234         
41235         var ctr = this.grid.getView().getFooterPanel(true);
41236         this.footer.dataSource = this.grid.dataSource;
41237         this.footer = Roo.factory(this.footer, Roo);
41238         this.footer.render(ctr);
41239         
41240     }
41241     
41242     
41243     
41244     
41245      
41246 };
41247
41248 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41249     getId : function(){
41250         return this.grid.id;
41251     },
41252     
41253     /**
41254      * Returns the grid for this panel
41255      * @return {Roo.bootstrap.Table} 
41256      */
41257     getGrid : function(){
41258         return this.grid;    
41259     },
41260     
41261     setSize : function(width, height){
41262         if(!this.ignoreResize(width, height)){
41263             var grid = this.grid;
41264             var size = this.adjustForComponents(width, height);
41265             // tfoot is not a footer?
41266           
41267             
41268             var gridel = grid.getGridEl();
41269             gridel.setSize(size.width, size.height);
41270             
41271             var tbd = grid.getGridEl().select('tbody', true).first();
41272             var thd = grid.getGridEl().select('thead',true).first();
41273             var tbf= grid.getGridEl().select('tfoot', true).first();
41274
41275             if (tbf) {
41276                 size.height -= tbf.getHeight();
41277             }
41278             if (thd) {
41279                 size.height -= thd.getHeight();
41280             }
41281             
41282             tbd.setSize(size.width, size.height );
41283             // this is for the account management tab -seems to work there.
41284             var thd = grid.getGridEl().select('thead',true).first();
41285             //if (tbd) {
41286             //    tbd.setSize(size.width, size.height - thd.getHeight());
41287             //}
41288              
41289             grid.autoSize();
41290         }
41291     },
41292      
41293     
41294     
41295     beforeSlide : function(){
41296         this.grid.getView().scroller.clip();
41297     },
41298     
41299     afterSlide : function(){
41300         this.grid.getView().scroller.unclip();
41301     },
41302     
41303     destroy : function(){
41304         this.grid.destroy();
41305         delete this.grid;
41306         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41307     }
41308 });
41309
41310 /**
41311  * @class Roo.bootstrap.panel.Nest
41312  * @extends Roo.bootstrap.panel.Content
41313  * @constructor
41314  * Create a new Panel, that can contain a layout.Border.
41315  * 
41316  * 
41317  * @param {Roo.BorderLayout} layout The layout for this panel
41318  * @param {String/Object} config A string to set only the title or a config object
41319  */
41320 Roo.bootstrap.panel.Nest = function(config)
41321 {
41322     // construct with only one argument..
41323     /* FIXME - implement nicer consturctors
41324     if (layout.layout) {
41325         config = layout;
41326         layout = config.layout;
41327         delete config.layout;
41328     }
41329     if (layout.xtype && !layout.getEl) {
41330         // then layout needs constructing..
41331         layout = Roo.factory(layout, Roo);
41332     }
41333     */
41334     
41335     config.el =  config.layout.getEl();
41336     
41337     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41338     
41339     config.layout.monitorWindowResize = false; // turn off autosizing
41340     this.layout = config.layout;
41341     this.layout.getEl().addClass("roo-layout-nested-layout");
41342     this.layout.parent = this;
41343     
41344     
41345     
41346     
41347 };
41348
41349 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41350
41351     setSize : function(width, height){
41352         if(!this.ignoreResize(width, height)){
41353             var size = this.adjustForComponents(width, height);
41354             var el = this.layout.getEl();
41355             if (size.height < 1) {
41356                 el.setWidth(size.width);   
41357             } else {
41358                 el.setSize(size.width, size.height);
41359             }
41360             var touch = el.dom.offsetWidth;
41361             this.layout.layout();
41362             // ie requires a double layout on the first pass
41363             if(Roo.isIE && !this.initialized){
41364                 this.initialized = true;
41365                 this.layout.layout();
41366             }
41367         }
41368     },
41369     
41370     // activate all subpanels if not currently active..
41371     
41372     setActiveState : function(active){
41373         this.active = active;
41374         this.setActiveClass(active);
41375         
41376         if(!active){
41377             this.fireEvent("deactivate", this);
41378             return;
41379         }
41380         
41381         this.fireEvent("activate", this);
41382         // not sure if this should happen before or after..
41383         if (!this.layout) {
41384             return; // should not happen..
41385         }
41386         var reg = false;
41387         for (var r in this.layout.regions) {
41388             reg = this.layout.getRegion(r);
41389             if (reg.getActivePanel()) {
41390                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41391                 reg.setActivePanel(reg.getActivePanel());
41392                 continue;
41393             }
41394             if (!reg.panels.length) {
41395                 continue;
41396             }
41397             reg.showPanel(reg.getPanel(0));
41398         }
41399         
41400         
41401         
41402         
41403     },
41404     
41405     /**
41406      * Returns the nested BorderLayout for this panel
41407      * @return {Roo.BorderLayout} 
41408      */
41409     getLayout : function(){
41410         return this.layout;
41411     },
41412     
41413      /**
41414      * Adds a xtype elements to the layout of the nested panel
41415      * <pre><code>
41416
41417 panel.addxtype({
41418        xtype : 'ContentPanel',
41419        region: 'west',
41420        items: [ .... ]
41421    }
41422 );
41423
41424 panel.addxtype({
41425         xtype : 'NestedLayoutPanel',
41426         region: 'west',
41427         layout: {
41428            center: { },
41429            west: { }   
41430         },
41431         items : [ ... list of content panels or nested layout panels.. ]
41432    }
41433 );
41434 </code></pre>
41435      * @param {Object} cfg Xtype definition of item to add.
41436      */
41437     addxtype : function(cfg) {
41438         return this.layout.addxtype(cfg);
41439     
41440     }
41441 });/*
41442  * Based on:
41443  * Ext JS Library 1.1.1
41444  * Copyright(c) 2006-2007, Ext JS, LLC.
41445  *
41446  * Originally Released Under LGPL - original licence link has changed is not relivant.
41447  *
41448  * Fork - LGPL
41449  * <script type="text/javascript">
41450  */
41451 /**
41452  * @class Roo.TabPanel
41453  * @extends Roo.util.Observable
41454  * A lightweight tab container.
41455  * <br><br>
41456  * Usage:
41457  * <pre><code>
41458 // basic tabs 1, built from existing content
41459 var tabs = new Roo.TabPanel("tabs1");
41460 tabs.addTab("script", "View Script");
41461 tabs.addTab("markup", "View Markup");
41462 tabs.activate("script");
41463
41464 // more advanced tabs, built from javascript
41465 var jtabs = new Roo.TabPanel("jtabs");
41466 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41467
41468 // set up the UpdateManager
41469 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41470 var updater = tab2.getUpdateManager();
41471 updater.setDefaultUrl("ajax1.htm");
41472 tab2.on('activate', updater.refresh, updater, true);
41473
41474 // Use setUrl for Ajax loading
41475 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41476 tab3.setUrl("ajax2.htm", null, true);
41477
41478 // Disabled tab
41479 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41480 tab4.disable();
41481
41482 jtabs.activate("jtabs-1");
41483  * </code></pre>
41484  * @constructor
41485  * Create a new TabPanel.
41486  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41487  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41488  */
41489 Roo.bootstrap.panel.Tabs = function(config){
41490     /**
41491     * The container element for this TabPanel.
41492     * @type Roo.Element
41493     */
41494     this.el = Roo.get(config.el);
41495     delete config.el;
41496     if(config){
41497         if(typeof config == "boolean"){
41498             this.tabPosition = config ? "bottom" : "top";
41499         }else{
41500             Roo.apply(this, config);
41501         }
41502     }
41503     
41504     if(this.tabPosition == "bottom"){
41505         // if tabs are at the bottom = create the body first.
41506         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41507         this.el.addClass("roo-tabs-bottom");
41508     }
41509     // next create the tabs holders
41510     
41511     if (this.tabPosition == "west"){
41512         
41513         var reg = this.region; // fake it..
41514         while (reg) {
41515             if (!reg.mgr.parent) {
41516                 break;
41517             }
41518             reg = reg.mgr.parent.region;
41519         }
41520         Roo.log("got nest?");
41521         Roo.log(reg);
41522         if (reg.mgr.getRegion('west')) {
41523             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41524             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41525             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41526             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41527             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41528         
41529             
41530         }
41531         
41532         
41533     } else {
41534      
41535         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41536         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41537         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41538         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41539     }
41540     
41541     
41542     if(Roo.isIE){
41543         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41544     }
41545     
41546     // finally - if tabs are at the top, then create the body last..
41547     if(this.tabPosition != "bottom"){
41548         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41549          * @type Roo.Element
41550          */
41551         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41552         this.el.addClass("roo-tabs-top");
41553     }
41554     this.items = [];
41555
41556     this.bodyEl.setStyle("position", "relative");
41557
41558     this.active = null;
41559     this.activateDelegate = this.activate.createDelegate(this);
41560
41561     this.addEvents({
41562         /**
41563          * @event tabchange
41564          * Fires when the active tab changes
41565          * @param {Roo.TabPanel} this
41566          * @param {Roo.TabPanelItem} activePanel The new active tab
41567          */
41568         "tabchange": true,
41569         /**
41570          * @event beforetabchange
41571          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41572          * @param {Roo.TabPanel} this
41573          * @param {Object} e Set cancel to true on this object to cancel the tab change
41574          * @param {Roo.TabPanelItem} tab The tab being changed to
41575          */
41576         "beforetabchange" : true
41577     });
41578
41579     Roo.EventManager.onWindowResize(this.onResize, this);
41580     this.cpad = this.el.getPadding("lr");
41581     this.hiddenCount = 0;
41582
41583
41584     // toolbar on the tabbar support...
41585     if (this.toolbar) {
41586         alert("no toolbar support yet");
41587         this.toolbar  = false;
41588         /*
41589         var tcfg = this.toolbar;
41590         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41591         this.toolbar = new Roo.Toolbar(tcfg);
41592         if (Roo.isSafari) {
41593             var tbl = tcfg.container.child('table', true);
41594             tbl.setAttribute('width', '100%');
41595         }
41596         */
41597         
41598     }
41599    
41600
41601
41602     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41603 };
41604
41605 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41606     /*
41607      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41608      */
41609     tabPosition : "top",
41610     /*
41611      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41612      */
41613     currentTabWidth : 0,
41614     /*
41615      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41616      */
41617     minTabWidth : 40,
41618     /*
41619      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41620      */
41621     maxTabWidth : 250,
41622     /*
41623      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41624      */
41625     preferredTabWidth : 175,
41626     /*
41627      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41628      */
41629     resizeTabs : false,
41630     /*
41631      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41632      */
41633     monitorResize : true,
41634     /*
41635      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41636      */
41637     toolbar : false,  // set by caller..
41638     
41639     region : false, /// set by caller
41640     
41641     disableTooltips : true, // not used yet...
41642
41643     /**
41644      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41645      * @param {String} id The id of the div to use <b>or create</b>
41646      * @param {String} text The text for the tab
41647      * @param {String} content (optional) Content to put in the TabPanelItem body
41648      * @param {Boolean} closable (optional) True to create a close icon on the tab
41649      * @return {Roo.TabPanelItem} The created TabPanelItem
41650      */
41651     addTab : function(id, text, content, closable, tpl)
41652     {
41653         var item = new Roo.bootstrap.panel.TabItem({
41654             panel: this,
41655             id : id,
41656             text : text,
41657             closable : closable,
41658             tpl : tpl
41659         });
41660         this.addTabItem(item);
41661         if(content){
41662             item.setContent(content);
41663         }
41664         return item;
41665     },
41666
41667     /**
41668      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41669      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41670      * @return {Roo.TabPanelItem}
41671      */
41672     getTab : function(id){
41673         return this.items[id];
41674     },
41675
41676     /**
41677      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41678      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41679      */
41680     hideTab : function(id){
41681         var t = this.items[id];
41682         if(!t.isHidden()){
41683            t.setHidden(true);
41684            this.hiddenCount++;
41685            this.autoSizeTabs();
41686         }
41687     },
41688
41689     /**
41690      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41691      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41692      */
41693     unhideTab : function(id){
41694         var t = this.items[id];
41695         if(t.isHidden()){
41696            t.setHidden(false);
41697            this.hiddenCount--;
41698            this.autoSizeTabs();
41699         }
41700     },
41701
41702     /**
41703      * Adds an existing {@link Roo.TabPanelItem}.
41704      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41705      */
41706     addTabItem : function(item)
41707     {
41708         this.items[item.id] = item;
41709         this.items.push(item);
41710         this.autoSizeTabs();
41711       //  if(this.resizeTabs){
41712     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41713   //         this.autoSizeTabs();
41714 //        }else{
41715 //            item.autoSize();
41716        // }
41717     },
41718
41719     /**
41720      * Removes a {@link Roo.TabPanelItem}.
41721      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41722      */
41723     removeTab : function(id){
41724         var items = this.items;
41725         var tab = items[id];
41726         if(!tab) { return; }
41727         var index = items.indexOf(tab);
41728         if(this.active == tab && items.length > 1){
41729             var newTab = this.getNextAvailable(index);
41730             if(newTab) {
41731                 newTab.activate();
41732             }
41733         }
41734         this.stripEl.dom.removeChild(tab.pnode.dom);
41735         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41736             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41737         }
41738         items.splice(index, 1);
41739         delete this.items[tab.id];
41740         tab.fireEvent("close", tab);
41741         tab.purgeListeners();
41742         this.autoSizeTabs();
41743     },
41744
41745     getNextAvailable : function(start){
41746         var items = this.items;
41747         var index = start;
41748         // look for a next tab that will slide over to
41749         // replace the one being removed
41750         while(index < items.length){
41751             var item = items[++index];
41752             if(item && !item.isHidden()){
41753                 return item;
41754             }
41755         }
41756         // if one isn't found select the previous tab (on the left)
41757         index = start;
41758         while(index >= 0){
41759             var item = items[--index];
41760             if(item && !item.isHidden()){
41761                 return item;
41762             }
41763         }
41764         return null;
41765     },
41766
41767     /**
41768      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41769      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41770      */
41771     disableTab : function(id){
41772         var tab = this.items[id];
41773         if(tab && this.active != tab){
41774             tab.disable();
41775         }
41776     },
41777
41778     /**
41779      * Enables a {@link Roo.TabPanelItem} that is disabled.
41780      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41781      */
41782     enableTab : function(id){
41783         var tab = this.items[id];
41784         tab.enable();
41785     },
41786
41787     /**
41788      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41789      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41790      * @return {Roo.TabPanelItem} The TabPanelItem.
41791      */
41792     activate : function(id)
41793     {
41794         //Roo.log('activite:'  + id);
41795         
41796         var tab = this.items[id];
41797         if(!tab){
41798             return null;
41799         }
41800         if(tab == this.active || tab.disabled){
41801             return tab;
41802         }
41803         var e = {};
41804         this.fireEvent("beforetabchange", this, e, tab);
41805         if(e.cancel !== true && !tab.disabled){
41806             if(this.active){
41807                 this.active.hide();
41808             }
41809             this.active = this.items[id];
41810             this.active.show();
41811             this.fireEvent("tabchange", this, this.active);
41812         }
41813         return tab;
41814     },
41815
41816     /**
41817      * Gets the active {@link Roo.TabPanelItem}.
41818      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41819      */
41820     getActiveTab : function(){
41821         return this.active;
41822     },
41823
41824     /**
41825      * Updates the tab body element to fit the height of the container element
41826      * for overflow scrolling
41827      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41828      */
41829     syncHeight : function(targetHeight){
41830         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41831         var bm = this.bodyEl.getMargins();
41832         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41833         this.bodyEl.setHeight(newHeight);
41834         return newHeight;
41835     },
41836
41837     onResize : function(){
41838         if(this.monitorResize){
41839             this.autoSizeTabs();
41840         }
41841     },
41842
41843     /**
41844      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41845      */
41846     beginUpdate : function(){
41847         this.updating = true;
41848     },
41849
41850     /**
41851      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41852      */
41853     endUpdate : function(){
41854         this.updating = false;
41855         this.autoSizeTabs();
41856     },
41857
41858     /**
41859      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41860      */
41861     autoSizeTabs : function()
41862     {
41863         var count = this.items.length;
41864         var vcount = count - this.hiddenCount;
41865         
41866         if (vcount < 2) {
41867             this.stripEl.hide();
41868         } else {
41869             this.stripEl.show();
41870         }
41871         
41872         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41873             return;
41874         }
41875         
41876         
41877         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41878         var availWidth = Math.floor(w / vcount);
41879         var b = this.stripBody;
41880         if(b.getWidth() > w){
41881             var tabs = this.items;
41882             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41883             if(availWidth < this.minTabWidth){
41884                 /*if(!this.sleft){    // incomplete scrolling code
41885                     this.createScrollButtons();
41886                 }
41887                 this.showScroll();
41888                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41889             }
41890         }else{
41891             if(this.currentTabWidth < this.preferredTabWidth){
41892                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41893             }
41894         }
41895     },
41896
41897     /**
41898      * Returns the number of tabs in this TabPanel.
41899      * @return {Number}
41900      */
41901      getCount : function(){
41902          return this.items.length;
41903      },
41904
41905     /**
41906      * Resizes all the tabs to the passed width
41907      * @param {Number} The new width
41908      */
41909     setTabWidth : function(width){
41910         this.currentTabWidth = width;
41911         for(var i = 0, len = this.items.length; i < len; i++) {
41912                 if(!this.items[i].isHidden()) {
41913                 this.items[i].setWidth(width);
41914             }
41915         }
41916     },
41917
41918     /**
41919      * Destroys this TabPanel
41920      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41921      */
41922     destroy : function(removeEl){
41923         Roo.EventManager.removeResizeListener(this.onResize, this);
41924         for(var i = 0, len = this.items.length; i < len; i++){
41925             this.items[i].purgeListeners();
41926         }
41927         if(removeEl === true){
41928             this.el.update("");
41929             this.el.remove();
41930         }
41931     },
41932     
41933     createStrip : function(container)
41934     {
41935         var strip = document.createElement("nav");
41936         strip.className = Roo.bootstrap.version == 4 ?
41937             "navbar-light bg-light" : 
41938             "navbar navbar-default"; //"x-tabs-wrap";
41939         container.appendChild(strip);
41940         return strip;
41941     },
41942     
41943     createStripList : function(strip)
41944     {
41945         // div wrapper for retard IE
41946         // returns the "tr" element.
41947         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41948         //'<div class="x-tabs-strip-wrap">'+
41949           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41950           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41951         return strip.firstChild; //.firstChild.firstChild.firstChild;
41952     },
41953     createBody : function(container)
41954     {
41955         var body = document.createElement("div");
41956         Roo.id(body, "tab-body");
41957         //Roo.fly(body).addClass("x-tabs-body");
41958         Roo.fly(body).addClass("tab-content");
41959         container.appendChild(body);
41960         return body;
41961     },
41962     createItemBody :function(bodyEl, id){
41963         var body = Roo.getDom(id);
41964         if(!body){
41965             body = document.createElement("div");
41966             body.id = id;
41967         }
41968         //Roo.fly(body).addClass("x-tabs-item-body");
41969         Roo.fly(body).addClass("tab-pane");
41970          bodyEl.insertBefore(body, bodyEl.firstChild);
41971         return body;
41972     },
41973     /** @private */
41974     createStripElements :  function(stripEl, text, closable, tpl)
41975     {
41976         var td = document.createElement("li"); // was td..
41977         td.className = 'nav-item';
41978         
41979         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41980         
41981         
41982         stripEl.appendChild(td);
41983         /*if(closable){
41984             td.className = "x-tabs-closable";
41985             if(!this.closeTpl){
41986                 this.closeTpl = new Roo.Template(
41987                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41988                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41989                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41990                 );
41991             }
41992             var el = this.closeTpl.overwrite(td, {"text": text});
41993             var close = el.getElementsByTagName("div")[0];
41994             var inner = el.getElementsByTagName("em")[0];
41995             return {"el": el, "close": close, "inner": inner};
41996         } else {
41997         */
41998         // not sure what this is..
41999 //            if(!this.tabTpl){
42000                 //this.tabTpl = new Roo.Template(
42001                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42002                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42003                 //);
42004 //                this.tabTpl = new Roo.Template(
42005 //                   '<a href="#">' +
42006 //                   '<span unselectable="on"' +
42007 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42008 //                            ' >{text}</span></a>'
42009 //                );
42010 //                
42011 //            }
42012
42013
42014             var template = tpl || this.tabTpl || false;
42015             
42016             if(!template){
42017                 template =  new Roo.Template(
42018                         Roo.bootstrap.version == 4 ? 
42019                             (
42020                                 '<a class="nav-link" href="#" unselectable="on"' +
42021                                      (this.disableTooltips ? '' : ' title="{text}"') +
42022                                      ' >{text}</a>'
42023                             ) : (
42024                                 '<a class="nav-link" href="#">' +
42025                                 '<span unselectable="on"' +
42026                                          (this.disableTooltips ? '' : ' title="{text}"') +
42027                                     ' >{text}</span></a>'
42028                             )
42029                 );
42030             }
42031             
42032             switch (typeof(template)) {
42033                 case 'object' :
42034                     break;
42035                 case 'string' :
42036                     template = new Roo.Template(template);
42037                     break;
42038                 default :
42039                     break;
42040             }
42041             
42042             var el = template.overwrite(td, {"text": text});
42043             
42044             var inner = el.getElementsByTagName("span")[0];
42045             
42046             return {"el": el, "inner": inner};
42047             
42048     }
42049         
42050     
42051 });
42052
42053 /**
42054  * @class Roo.TabPanelItem
42055  * @extends Roo.util.Observable
42056  * Represents an individual item (tab plus body) in a TabPanel.
42057  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42058  * @param {String} id The id of this TabPanelItem
42059  * @param {String} text The text for the tab of this TabPanelItem
42060  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42061  */
42062 Roo.bootstrap.panel.TabItem = function(config){
42063     /**
42064      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42065      * @type Roo.TabPanel
42066      */
42067     this.tabPanel = config.panel;
42068     /**
42069      * The id for this TabPanelItem
42070      * @type String
42071      */
42072     this.id = config.id;
42073     /** @private */
42074     this.disabled = false;
42075     /** @private */
42076     this.text = config.text;
42077     /** @private */
42078     this.loaded = false;
42079     this.closable = config.closable;
42080
42081     /**
42082      * The body element for this TabPanelItem.
42083      * @type Roo.Element
42084      */
42085     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42086     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42087     this.bodyEl.setStyle("display", "block");
42088     this.bodyEl.setStyle("zoom", "1");
42089     //this.hideAction();
42090
42091     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42092     /** @private */
42093     this.el = Roo.get(els.el);
42094     this.inner = Roo.get(els.inner, true);
42095      this.textEl = Roo.bootstrap.version == 4 ?
42096         this.el : Roo.get(this.el.dom.firstChild, true);
42097
42098     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42099     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42100
42101     
42102 //    this.el.on("mousedown", this.onTabMouseDown, this);
42103     this.el.on("click", this.onTabClick, this);
42104     /** @private */
42105     if(config.closable){
42106         var c = Roo.get(els.close, true);
42107         c.dom.title = this.closeText;
42108         c.addClassOnOver("close-over");
42109         c.on("click", this.closeClick, this);
42110      }
42111
42112     this.addEvents({
42113          /**
42114          * @event activate
42115          * Fires when this tab becomes the active tab.
42116          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42117          * @param {Roo.TabPanelItem} this
42118          */
42119         "activate": true,
42120         /**
42121          * @event beforeclose
42122          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42123          * @param {Roo.TabPanelItem} this
42124          * @param {Object} e Set cancel to true on this object to cancel the close.
42125          */
42126         "beforeclose": true,
42127         /**
42128          * @event close
42129          * Fires when this tab is closed.
42130          * @param {Roo.TabPanelItem} this
42131          */
42132          "close": true,
42133         /**
42134          * @event deactivate
42135          * Fires when this tab is no longer the active tab.
42136          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42137          * @param {Roo.TabPanelItem} this
42138          */
42139          "deactivate" : true
42140     });
42141     this.hidden = false;
42142
42143     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42144 };
42145
42146 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42147            {
42148     purgeListeners : function(){
42149        Roo.util.Observable.prototype.purgeListeners.call(this);
42150        this.el.removeAllListeners();
42151     },
42152     /**
42153      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42154      */
42155     show : function(){
42156         this.status_node.addClass("active");
42157         this.showAction();
42158         if(Roo.isOpera){
42159             this.tabPanel.stripWrap.repaint();
42160         }
42161         this.fireEvent("activate", this.tabPanel, this);
42162     },
42163
42164     /**
42165      * Returns true if this tab is the active tab.
42166      * @return {Boolean}
42167      */
42168     isActive : function(){
42169         return this.tabPanel.getActiveTab() == this;
42170     },
42171
42172     /**
42173      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42174      */
42175     hide : function(){
42176         this.status_node.removeClass("active");
42177         this.hideAction();
42178         this.fireEvent("deactivate", this.tabPanel, this);
42179     },
42180
42181     hideAction : function(){
42182         this.bodyEl.hide();
42183         this.bodyEl.setStyle("position", "absolute");
42184         this.bodyEl.setLeft("-20000px");
42185         this.bodyEl.setTop("-20000px");
42186     },
42187
42188     showAction : function(){
42189         this.bodyEl.setStyle("position", "relative");
42190         this.bodyEl.setTop("");
42191         this.bodyEl.setLeft("");
42192         this.bodyEl.show();
42193     },
42194
42195     /**
42196      * Set the tooltip for the tab.
42197      * @param {String} tooltip The tab's tooltip
42198      */
42199     setTooltip : function(text){
42200         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42201             this.textEl.dom.qtip = text;
42202             this.textEl.dom.removeAttribute('title');
42203         }else{
42204             this.textEl.dom.title = text;
42205         }
42206     },
42207
42208     onTabClick : function(e){
42209         e.preventDefault();
42210         this.tabPanel.activate(this.id);
42211     },
42212
42213     onTabMouseDown : function(e){
42214         e.preventDefault();
42215         this.tabPanel.activate(this.id);
42216     },
42217 /*
42218     getWidth : function(){
42219         return this.inner.getWidth();
42220     },
42221
42222     setWidth : function(width){
42223         var iwidth = width - this.linode.getPadding("lr");
42224         this.inner.setWidth(iwidth);
42225         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42226         this.linode.setWidth(width);
42227     },
42228 */
42229     /**
42230      * Show or hide the tab
42231      * @param {Boolean} hidden True to hide or false to show.
42232      */
42233     setHidden : function(hidden){
42234         this.hidden = hidden;
42235         this.linode.setStyle("display", hidden ? "none" : "");
42236     },
42237
42238     /**
42239      * Returns true if this tab is "hidden"
42240      * @return {Boolean}
42241      */
42242     isHidden : function(){
42243         return this.hidden;
42244     },
42245
42246     /**
42247      * Returns the text for this tab
42248      * @return {String}
42249      */
42250     getText : function(){
42251         return this.text;
42252     },
42253     /*
42254     autoSize : function(){
42255         //this.el.beginMeasure();
42256         this.textEl.setWidth(1);
42257         /*
42258          *  #2804 [new] Tabs in Roojs
42259          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42260          */
42261         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42262         //this.el.endMeasure();
42263     //},
42264
42265     /**
42266      * Sets the text for the tab (Note: this also sets the tooltip text)
42267      * @param {String} text The tab's text and tooltip
42268      */
42269     setText : function(text){
42270         this.text = text;
42271         this.textEl.update(text);
42272         this.setTooltip(text);
42273         //if(!this.tabPanel.resizeTabs){
42274         //    this.autoSize();
42275         //}
42276     },
42277     /**
42278      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42279      */
42280     activate : function(){
42281         this.tabPanel.activate(this.id);
42282     },
42283
42284     /**
42285      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42286      */
42287     disable : function(){
42288         if(this.tabPanel.active != this){
42289             this.disabled = true;
42290             this.status_node.addClass("disabled");
42291         }
42292     },
42293
42294     /**
42295      * Enables this TabPanelItem if it was previously disabled.
42296      */
42297     enable : function(){
42298         this.disabled = false;
42299         this.status_node.removeClass("disabled");
42300     },
42301
42302     /**
42303      * Sets the content for this TabPanelItem.
42304      * @param {String} content The content
42305      * @param {Boolean} loadScripts true to look for and load scripts
42306      */
42307     setContent : function(content, loadScripts){
42308         this.bodyEl.update(content, loadScripts);
42309     },
42310
42311     /**
42312      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42313      * @return {Roo.UpdateManager} The UpdateManager
42314      */
42315     getUpdateManager : function(){
42316         return this.bodyEl.getUpdateManager();
42317     },
42318
42319     /**
42320      * Set a URL to be used to load the content for this TabPanelItem.
42321      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42322      * @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)
42323      * @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)
42324      * @return {Roo.UpdateManager} The UpdateManager
42325      */
42326     setUrl : function(url, params, loadOnce){
42327         if(this.refreshDelegate){
42328             this.un('activate', this.refreshDelegate);
42329         }
42330         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42331         this.on("activate", this.refreshDelegate);
42332         return this.bodyEl.getUpdateManager();
42333     },
42334
42335     /** @private */
42336     _handleRefresh : function(url, params, loadOnce){
42337         if(!loadOnce || !this.loaded){
42338             var updater = this.bodyEl.getUpdateManager();
42339             updater.update(url, params, this._setLoaded.createDelegate(this));
42340         }
42341     },
42342
42343     /**
42344      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42345      *   Will fail silently if the setUrl method has not been called.
42346      *   This does not activate the panel, just updates its content.
42347      */
42348     refresh : function(){
42349         if(this.refreshDelegate){
42350            this.loaded = false;
42351            this.refreshDelegate();
42352         }
42353     },
42354
42355     /** @private */
42356     _setLoaded : function(){
42357         this.loaded = true;
42358     },
42359
42360     /** @private */
42361     closeClick : function(e){
42362         var o = {};
42363         e.stopEvent();
42364         this.fireEvent("beforeclose", this, o);
42365         if(o.cancel !== true){
42366             this.tabPanel.removeTab(this.id);
42367         }
42368     },
42369     /**
42370      * The text displayed in the tooltip for the close icon.
42371      * @type String
42372      */
42373     closeText : "Close this tab"
42374 });
42375 /**
42376 *    This script refer to:
42377 *    Title: International Telephone Input
42378 *    Author: Jack O'Connor
42379 *    Code version:  v12.1.12
42380 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42381 **/
42382
42383 Roo.bootstrap.PhoneInputData = function() {
42384     var d = [
42385       [
42386         "Afghanistan (‫افغانستان‬‎)",
42387         "af",
42388         "93"
42389       ],
42390       [
42391         "Albania (Shqipëri)",
42392         "al",
42393         "355"
42394       ],
42395       [
42396         "Algeria (‫الجزائر‬‎)",
42397         "dz",
42398         "213"
42399       ],
42400       [
42401         "American Samoa",
42402         "as",
42403         "1684"
42404       ],
42405       [
42406         "Andorra",
42407         "ad",
42408         "376"
42409       ],
42410       [
42411         "Angola",
42412         "ao",
42413         "244"
42414       ],
42415       [
42416         "Anguilla",
42417         "ai",
42418         "1264"
42419       ],
42420       [
42421         "Antigua and Barbuda",
42422         "ag",
42423         "1268"
42424       ],
42425       [
42426         "Argentina",
42427         "ar",
42428         "54"
42429       ],
42430       [
42431         "Armenia (Հայաստան)",
42432         "am",
42433         "374"
42434       ],
42435       [
42436         "Aruba",
42437         "aw",
42438         "297"
42439       ],
42440       [
42441         "Australia",
42442         "au",
42443         "61",
42444         0
42445       ],
42446       [
42447         "Austria (Österreich)",
42448         "at",
42449         "43"
42450       ],
42451       [
42452         "Azerbaijan (Azərbaycan)",
42453         "az",
42454         "994"
42455       ],
42456       [
42457         "Bahamas",
42458         "bs",
42459         "1242"
42460       ],
42461       [
42462         "Bahrain (‫البحرين‬‎)",
42463         "bh",
42464         "973"
42465       ],
42466       [
42467         "Bangladesh (বাংলাদেশ)",
42468         "bd",
42469         "880"
42470       ],
42471       [
42472         "Barbados",
42473         "bb",
42474         "1246"
42475       ],
42476       [
42477         "Belarus (Беларусь)",
42478         "by",
42479         "375"
42480       ],
42481       [
42482         "Belgium (België)",
42483         "be",
42484         "32"
42485       ],
42486       [
42487         "Belize",
42488         "bz",
42489         "501"
42490       ],
42491       [
42492         "Benin (Bénin)",
42493         "bj",
42494         "229"
42495       ],
42496       [
42497         "Bermuda",
42498         "bm",
42499         "1441"
42500       ],
42501       [
42502         "Bhutan (འབྲུག)",
42503         "bt",
42504         "975"
42505       ],
42506       [
42507         "Bolivia",
42508         "bo",
42509         "591"
42510       ],
42511       [
42512         "Bosnia and Herzegovina (Босна и Херцеговина)",
42513         "ba",
42514         "387"
42515       ],
42516       [
42517         "Botswana",
42518         "bw",
42519         "267"
42520       ],
42521       [
42522         "Brazil (Brasil)",
42523         "br",
42524         "55"
42525       ],
42526       [
42527         "British Indian Ocean Territory",
42528         "io",
42529         "246"
42530       ],
42531       [
42532         "British Virgin Islands",
42533         "vg",
42534         "1284"
42535       ],
42536       [
42537         "Brunei",
42538         "bn",
42539         "673"
42540       ],
42541       [
42542         "Bulgaria (България)",
42543         "bg",
42544         "359"
42545       ],
42546       [
42547         "Burkina Faso",
42548         "bf",
42549         "226"
42550       ],
42551       [
42552         "Burundi (Uburundi)",
42553         "bi",
42554         "257"
42555       ],
42556       [
42557         "Cambodia (កម្ពុជា)",
42558         "kh",
42559         "855"
42560       ],
42561       [
42562         "Cameroon (Cameroun)",
42563         "cm",
42564         "237"
42565       ],
42566       [
42567         "Canada",
42568         "ca",
42569         "1",
42570         1,
42571         ["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"]
42572       ],
42573       [
42574         "Cape Verde (Kabu Verdi)",
42575         "cv",
42576         "238"
42577       ],
42578       [
42579         "Caribbean Netherlands",
42580         "bq",
42581         "599",
42582         1
42583       ],
42584       [
42585         "Cayman Islands",
42586         "ky",
42587         "1345"
42588       ],
42589       [
42590         "Central African Republic (République centrafricaine)",
42591         "cf",
42592         "236"
42593       ],
42594       [
42595         "Chad (Tchad)",
42596         "td",
42597         "235"
42598       ],
42599       [
42600         "Chile",
42601         "cl",
42602         "56"
42603       ],
42604       [
42605         "China (中国)",
42606         "cn",
42607         "86"
42608       ],
42609       [
42610         "Christmas Island",
42611         "cx",
42612         "61",
42613         2
42614       ],
42615       [
42616         "Cocos (Keeling) Islands",
42617         "cc",
42618         "61",
42619         1
42620       ],
42621       [
42622         "Colombia",
42623         "co",
42624         "57"
42625       ],
42626       [
42627         "Comoros (‫جزر القمر‬‎)",
42628         "km",
42629         "269"
42630       ],
42631       [
42632         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42633         "cd",
42634         "243"
42635       ],
42636       [
42637         "Congo (Republic) (Congo-Brazzaville)",
42638         "cg",
42639         "242"
42640       ],
42641       [
42642         "Cook Islands",
42643         "ck",
42644         "682"
42645       ],
42646       [
42647         "Costa Rica",
42648         "cr",
42649         "506"
42650       ],
42651       [
42652         "Côte d’Ivoire",
42653         "ci",
42654         "225"
42655       ],
42656       [
42657         "Croatia (Hrvatska)",
42658         "hr",
42659         "385"
42660       ],
42661       [
42662         "Cuba",
42663         "cu",
42664         "53"
42665       ],
42666       [
42667         "Curaçao",
42668         "cw",
42669         "599",
42670         0
42671       ],
42672       [
42673         "Cyprus (Κύπρος)",
42674         "cy",
42675         "357"
42676       ],
42677       [
42678         "Czech Republic (Česká republika)",
42679         "cz",
42680         "420"
42681       ],
42682       [
42683         "Denmark (Danmark)",
42684         "dk",
42685         "45"
42686       ],
42687       [
42688         "Djibouti",
42689         "dj",
42690         "253"
42691       ],
42692       [
42693         "Dominica",
42694         "dm",
42695         "1767"
42696       ],
42697       [
42698         "Dominican Republic (República Dominicana)",
42699         "do",
42700         "1",
42701         2,
42702         ["809", "829", "849"]
42703       ],
42704       [
42705         "Ecuador",
42706         "ec",
42707         "593"
42708       ],
42709       [
42710         "Egypt (‫مصر‬‎)",
42711         "eg",
42712         "20"
42713       ],
42714       [
42715         "El Salvador",
42716         "sv",
42717         "503"
42718       ],
42719       [
42720         "Equatorial Guinea (Guinea Ecuatorial)",
42721         "gq",
42722         "240"
42723       ],
42724       [
42725         "Eritrea",
42726         "er",
42727         "291"
42728       ],
42729       [
42730         "Estonia (Eesti)",
42731         "ee",
42732         "372"
42733       ],
42734       [
42735         "Ethiopia",
42736         "et",
42737         "251"
42738       ],
42739       [
42740         "Falkland Islands (Islas Malvinas)",
42741         "fk",
42742         "500"
42743       ],
42744       [
42745         "Faroe Islands (Føroyar)",
42746         "fo",
42747         "298"
42748       ],
42749       [
42750         "Fiji",
42751         "fj",
42752         "679"
42753       ],
42754       [
42755         "Finland (Suomi)",
42756         "fi",
42757         "358",
42758         0
42759       ],
42760       [
42761         "France",
42762         "fr",
42763         "33"
42764       ],
42765       [
42766         "French Guiana (Guyane française)",
42767         "gf",
42768         "594"
42769       ],
42770       [
42771         "French Polynesia (Polynésie française)",
42772         "pf",
42773         "689"
42774       ],
42775       [
42776         "Gabon",
42777         "ga",
42778         "241"
42779       ],
42780       [
42781         "Gambia",
42782         "gm",
42783         "220"
42784       ],
42785       [
42786         "Georgia (საქართველო)",
42787         "ge",
42788         "995"
42789       ],
42790       [
42791         "Germany (Deutschland)",
42792         "de",
42793         "49"
42794       ],
42795       [
42796         "Ghana (Gaana)",
42797         "gh",
42798         "233"
42799       ],
42800       [
42801         "Gibraltar",
42802         "gi",
42803         "350"
42804       ],
42805       [
42806         "Greece (Ελλάδα)",
42807         "gr",
42808         "30"
42809       ],
42810       [
42811         "Greenland (Kalaallit Nunaat)",
42812         "gl",
42813         "299"
42814       ],
42815       [
42816         "Grenada",
42817         "gd",
42818         "1473"
42819       ],
42820       [
42821         "Guadeloupe",
42822         "gp",
42823         "590",
42824         0
42825       ],
42826       [
42827         "Guam",
42828         "gu",
42829         "1671"
42830       ],
42831       [
42832         "Guatemala",
42833         "gt",
42834         "502"
42835       ],
42836       [
42837         "Guernsey",
42838         "gg",
42839         "44",
42840         1
42841       ],
42842       [
42843         "Guinea (Guinée)",
42844         "gn",
42845         "224"
42846       ],
42847       [
42848         "Guinea-Bissau (Guiné Bissau)",
42849         "gw",
42850         "245"
42851       ],
42852       [
42853         "Guyana",
42854         "gy",
42855         "592"
42856       ],
42857       [
42858         "Haiti",
42859         "ht",
42860         "509"
42861       ],
42862       [
42863         "Honduras",
42864         "hn",
42865         "504"
42866       ],
42867       [
42868         "Hong Kong (香港)",
42869         "hk",
42870         "852"
42871       ],
42872       [
42873         "Hungary (Magyarország)",
42874         "hu",
42875         "36"
42876       ],
42877       [
42878         "Iceland (Ísland)",
42879         "is",
42880         "354"
42881       ],
42882       [
42883         "India (भारत)",
42884         "in",
42885         "91"
42886       ],
42887       [
42888         "Indonesia",
42889         "id",
42890         "62"
42891       ],
42892       [
42893         "Iran (‫ایران‬‎)",
42894         "ir",
42895         "98"
42896       ],
42897       [
42898         "Iraq (‫العراق‬‎)",
42899         "iq",
42900         "964"
42901       ],
42902       [
42903         "Ireland",
42904         "ie",
42905         "353"
42906       ],
42907       [
42908         "Isle of Man",
42909         "im",
42910         "44",
42911         2
42912       ],
42913       [
42914         "Israel (‫ישראל‬‎)",
42915         "il",
42916         "972"
42917       ],
42918       [
42919         "Italy (Italia)",
42920         "it",
42921         "39",
42922         0
42923       ],
42924       [
42925         "Jamaica",
42926         "jm",
42927         "1876"
42928       ],
42929       [
42930         "Japan (日本)",
42931         "jp",
42932         "81"
42933       ],
42934       [
42935         "Jersey",
42936         "je",
42937         "44",
42938         3
42939       ],
42940       [
42941         "Jordan (‫الأردن‬‎)",
42942         "jo",
42943         "962"
42944       ],
42945       [
42946         "Kazakhstan (Казахстан)",
42947         "kz",
42948         "7",
42949         1
42950       ],
42951       [
42952         "Kenya",
42953         "ke",
42954         "254"
42955       ],
42956       [
42957         "Kiribati",
42958         "ki",
42959         "686"
42960       ],
42961       [
42962         "Kosovo",
42963         "xk",
42964         "383"
42965       ],
42966       [
42967         "Kuwait (‫الكويت‬‎)",
42968         "kw",
42969         "965"
42970       ],
42971       [
42972         "Kyrgyzstan (Кыргызстан)",
42973         "kg",
42974         "996"
42975       ],
42976       [
42977         "Laos (ລາວ)",
42978         "la",
42979         "856"
42980       ],
42981       [
42982         "Latvia (Latvija)",
42983         "lv",
42984         "371"
42985       ],
42986       [
42987         "Lebanon (‫لبنان‬‎)",
42988         "lb",
42989         "961"
42990       ],
42991       [
42992         "Lesotho",
42993         "ls",
42994         "266"
42995       ],
42996       [
42997         "Liberia",
42998         "lr",
42999         "231"
43000       ],
43001       [
43002         "Libya (‫ليبيا‬‎)",
43003         "ly",
43004         "218"
43005       ],
43006       [
43007         "Liechtenstein",
43008         "li",
43009         "423"
43010       ],
43011       [
43012         "Lithuania (Lietuva)",
43013         "lt",
43014         "370"
43015       ],
43016       [
43017         "Luxembourg",
43018         "lu",
43019         "352"
43020       ],
43021       [
43022         "Macau (澳門)",
43023         "mo",
43024         "853"
43025       ],
43026       [
43027         "Macedonia (FYROM) (Македонија)",
43028         "mk",
43029         "389"
43030       ],
43031       [
43032         "Madagascar (Madagasikara)",
43033         "mg",
43034         "261"
43035       ],
43036       [
43037         "Malawi",
43038         "mw",
43039         "265"
43040       ],
43041       [
43042         "Malaysia",
43043         "my",
43044         "60"
43045       ],
43046       [
43047         "Maldives",
43048         "mv",
43049         "960"
43050       ],
43051       [
43052         "Mali",
43053         "ml",
43054         "223"
43055       ],
43056       [
43057         "Malta",
43058         "mt",
43059         "356"
43060       ],
43061       [
43062         "Marshall Islands",
43063         "mh",
43064         "692"
43065       ],
43066       [
43067         "Martinique",
43068         "mq",
43069         "596"
43070       ],
43071       [
43072         "Mauritania (‫موريتانيا‬‎)",
43073         "mr",
43074         "222"
43075       ],
43076       [
43077         "Mauritius (Moris)",
43078         "mu",
43079         "230"
43080       ],
43081       [
43082         "Mayotte",
43083         "yt",
43084         "262",
43085         1
43086       ],
43087       [
43088         "Mexico (México)",
43089         "mx",
43090         "52"
43091       ],
43092       [
43093         "Micronesia",
43094         "fm",
43095         "691"
43096       ],
43097       [
43098         "Moldova (Republica Moldova)",
43099         "md",
43100         "373"
43101       ],
43102       [
43103         "Monaco",
43104         "mc",
43105         "377"
43106       ],
43107       [
43108         "Mongolia (Монгол)",
43109         "mn",
43110         "976"
43111       ],
43112       [
43113         "Montenegro (Crna Gora)",
43114         "me",
43115         "382"
43116       ],
43117       [
43118         "Montserrat",
43119         "ms",
43120         "1664"
43121       ],
43122       [
43123         "Morocco (‫المغرب‬‎)",
43124         "ma",
43125         "212",
43126         0
43127       ],
43128       [
43129         "Mozambique (Moçambique)",
43130         "mz",
43131         "258"
43132       ],
43133       [
43134         "Myanmar (Burma) (မြန်မာ)",
43135         "mm",
43136         "95"
43137       ],
43138       [
43139         "Namibia (Namibië)",
43140         "na",
43141         "264"
43142       ],
43143       [
43144         "Nauru",
43145         "nr",
43146         "674"
43147       ],
43148       [
43149         "Nepal (नेपाल)",
43150         "np",
43151         "977"
43152       ],
43153       [
43154         "Netherlands (Nederland)",
43155         "nl",
43156         "31"
43157       ],
43158       [
43159         "New Caledonia (Nouvelle-Calédonie)",
43160         "nc",
43161         "687"
43162       ],
43163       [
43164         "New Zealand",
43165         "nz",
43166         "64"
43167       ],
43168       [
43169         "Nicaragua",
43170         "ni",
43171         "505"
43172       ],
43173       [
43174         "Niger (Nijar)",
43175         "ne",
43176         "227"
43177       ],
43178       [
43179         "Nigeria",
43180         "ng",
43181         "234"
43182       ],
43183       [
43184         "Niue",
43185         "nu",
43186         "683"
43187       ],
43188       [
43189         "Norfolk Island",
43190         "nf",
43191         "672"
43192       ],
43193       [
43194         "North Korea (조선 민주주의 인민 공화국)",
43195         "kp",
43196         "850"
43197       ],
43198       [
43199         "Northern Mariana Islands",
43200         "mp",
43201         "1670"
43202       ],
43203       [
43204         "Norway (Norge)",
43205         "no",
43206         "47",
43207         0
43208       ],
43209       [
43210         "Oman (‫عُمان‬‎)",
43211         "om",
43212         "968"
43213       ],
43214       [
43215         "Pakistan (‫پاکستان‬‎)",
43216         "pk",
43217         "92"
43218       ],
43219       [
43220         "Palau",
43221         "pw",
43222         "680"
43223       ],
43224       [
43225         "Palestine (‫فلسطين‬‎)",
43226         "ps",
43227         "970"
43228       ],
43229       [
43230         "Panama (Panamá)",
43231         "pa",
43232         "507"
43233       ],
43234       [
43235         "Papua New Guinea",
43236         "pg",
43237         "675"
43238       ],
43239       [
43240         "Paraguay",
43241         "py",
43242         "595"
43243       ],
43244       [
43245         "Peru (Perú)",
43246         "pe",
43247         "51"
43248       ],
43249       [
43250         "Philippines",
43251         "ph",
43252         "63"
43253       ],
43254       [
43255         "Poland (Polska)",
43256         "pl",
43257         "48"
43258       ],
43259       [
43260         "Portugal",
43261         "pt",
43262         "351"
43263       ],
43264       [
43265         "Puerto Rico",
43266         "pr",
43267         "1",
43268         3,
43269         ["787", "939"]
43270       ],
43271       [
43272         "Qatar (‫قطر‬‎)",
43273         "qa",
43274         "974"
43275       ],
43276       [
43277         "Réunion (La Réunion)",
43278         "re",
43279         "262",
43280         0
43281       ],
43282       [
43283         "Romania (România)",
43284         "ro",
43285         "40"
43286       ],
43287       [
43288         "Russia (Россия)",
43289         "ru",
43290         "7",
43291         0
43292       ],
43293       [
43294         "Rwanda",
43295         "rw",
43296         "250"
43297       ],
43298       [
43299         "Saint Barthélemy",
43300         "bl",
43301         "590",
43302         1
43303       ],
43304       [
43305         "Saint Helena",
43306         "sh",
43307         "290"
43308       ],
43309       [
43310         "Saint Kitts and Nevis",
43311         "kn",
43312         "1869"
43313       ],
43314       [
43315         "Saint Lucia",
43316         "lc",
43317         "1758"
43318       ],
43319       [
43320         "Saint Martin (Saint-Martin (partie française))",
43321         "mf",
43322         "590",
43323         2
43324       ],
43325       [
43326         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43327         "pm",
43328         "508"
43329       ],
43330       [
43331         "Saint Vincent and the Grenadines",
43332         "vc",
43333         "1784"
43334       ],
43335       [
43336         "Samoa",
43337         "ws",
43338         "685"
43339       ],
43340       [
43341         "San Marino",
43342         "sm",
43343         "378"
43344       ],
43345       [
43346         "São Tomé and Príncipe (São Tomé e Príncipe)",
43347         "st",
43348         "239"
43349       ],
43350       [
43351         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43352         "sa",
43353         "966"
43354       ],
43355       [
43356         "Senegal (Sénégal)",
43357         "sn",
43358         "221"
43359       ],
43360       [
43361         "Serbia (Србија)",
43362         "rs",
43363         "381"
43364       ],
43365       [
43366         "Seychelles",
43367         "sc",
43368         "248"
43369       ],
43370       [
43371         "Sierra Leone",
43372         "sl",
43373         "232"
43374       ],
43375       [
43376         "Singapore",
43377         "sg",
43378         "65"
43379       ],
43380       [
43381         "Sint Maarten",
43382         "sx",
43383         "1721"
43384       ],
43385       [
43386         "Slovakia (Slovensko)",
43387         "sk",
43388         "421"
43389       ],
43390       [
43391         "Slovenia (Slovenija)",
43392         "si",
43393         "386"
43394       ],
43395       [
43396         "Solomon Islands",
43397         "sb",
43398         "677"
43399       ],
43400       [
43401         "Somalia (Soomaaliya)",
43402         "so",
43403         "252"
43404       ],
43405       [
43406         "South Africa",
43407         "za",
43408         "27"
43409       ],
43410       [
43411         "South Korea (대한민국)",
43412         "kr",
43413         "82"
43414       ],
43415       [
43416         "South Sudan (‫جنوب السودان‬‎)",
43417         "ss",
43418         "211"
43419       ],
43420       [
43421         "Spain (España)",
43422         "es",
43423         "34"
43424       ],
43425       [
43426         "Sri Lanka (ශ්‍රී ලංකාව)",
43427         "lk",
43428         "94"
43429       ],
43430       [
43431         "Sudan (‫السودان‬‎)",
43432         "sd",
43433         "249"
43434       ],
43435       [
43436         "Suriname",
43437         "sr",
43438         "597"
43439       ],
43440       [
43441         "Svalbard and Jan Mayen",
43442         "sj",
43443         "47",
43444         1
43445       ],
43446       [
43447         "Swaziland",
43448         "sz",
43449         "268"
43450       ],
43451       [
43452         "Sweden (Sverige)",
43453         "se",
43454         "46"
43455       ],
43456       [
43457         "Switzerland (Schweiz)",
43458         "ch",
43459         "41"
43460       ],
43461       [
43462         "Syria (‫سوريا‬‎)",
43463         "sy",
43464         "963"
43465       ],
43466       [
43467         "Taiwan (台灣)",
43468         "tw",
43469         "886"
43470       ],
43471       [
43472         "Tajikistan",
43473         "tj",
43474         "992"
43475       ],
43476       [
43477         "Tanzania",
43478         "tz",
43479         "255"
43480       ],
43481       [
43482         "Thailand (ไทย)",
43483         "th",
43484         "66"
43485       ],
43486       [
43487         "Timor-Leste",
43488         "tl",
43489         "670"
43490       ],
43491       [
43492         "Togo",
43493         "tg",
43494         "228"
43495       ],
43496       [
43497         "Tokelau",
43498         "tk",
43499         "690"
43500       ],
43501       [
43502         "Tonga",
43503         "to",
43504         "676"
43505       ],
43506       [
43507         "Trinidad and Tobago",
43508         "tt",
43509         "1868"
43510       ],
43511       [
43512         "Tunisia (‫تونس‬‎)",
43513         "tn",
43514         "216"
43515       ],
43516       [
43517         "Turkey (Türkiye)",
43518         "tr",
43519         "90"
43520       ],
43521       [
43522         "Turkmenistan",
43523         "tm",
43524         "993"
43525       ],
43526       [
43527         "Turks and Caicos Islands",
43528         "tc",
43529         "1649"
43530       ],
43531       [
43532         "Tuvalu",
43533         "tv",
43534         "688"
43535       ],
43536       [
43537         "U.S. Virgin Islands",
43538         "vi",
43539         "1340"
43540       ],
43541       [
43542         "Uganda",
43543         "ug",
43544         "256"
43545       ],
43546       [
43547         "Ukraine (Україна)",
43548         "ua",
43549         "380"
43550       ],
43551       [
43552         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43553         "ae",
43554         "971"
43555       ],
43556       [
43557         "United Kingdom",
43558         "gb",
43559         "44",
43560         0
43561       ],
43562       [
43563         "United States",
43564         "us",
43565         "1",
43566         0
43567       ],
43568       [
43569         "Uruguay",
43570         "uy",
43571         "598"
43572       ],
43573       [
43574         "Uzbekistan (Oʻzbekiston)",
43575         "uz",
43576         "998"
43577       ],
43578       [
43579         "Vanuatu",
43580         "vu",
43581         "678"
43582       ],
43583       [
43584         "Vatican City (Città del Vaticano)",
43585         "va",
43586         "39",
43587         1
43588       ],
43589       [
43590         "Venezuela",
43591         "ve",
43592         "58"
43593       ],
43594       [
43595         "Vietnam (Việt Nam)",
43596         "vn",
43597         "84"
43598       ],
43599       [
43600         "Wallis and Futuna (Wallis-et-Futuna)",
43601         "wf",
43602         "681"
43603       ],
43604       [
43605         "Western Sahara (‫الصحراء الغربية‬‎)",
43606         "eh",
43607         "212",
43608         1
43609       ],
43610       [
43611         "Yemen (‫اليمن‬‎)",
43612         "ye",
43613         "967"
43614       ],
43615       [
43616         "Zambia",
43617         "zm",
43618         "260"
43619       ],
43620       [
43621         "Zimbabwe",
43622         "zw",
43623         "263"
43624       ],
43625       [
43626         "Åland Islands",
43627         "ax",
43628         "358",
43629         1
43630       ]
43631   ];
43632   
43633   return d;
43634 }/**
43635 *    This script refer to:
43636 *    Title: International Telephone Input
43637 *    Author: Jack O'Connor
43638 *    Code version:  v12.1.12
43639 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43640 **/
43641
43642 /**
43643  * @class Roo.bootstrap.PhoneInput
43644  * @extends Roo.bootstrap.TriggerField
43645  * An input with International dial-code selection
43646  
43647  * @cfg {String} defaultDialCode default '+852'
43648  * @cfg {Array} preferedCountries default []
43649   
43650  * @constructor
43651  * Create a new PhoneInput.
43652  * @param {Object} config Configuration options
43653  */
43654
43655 Roo.bootstrap.PhoneInput = function(config) {
43656     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43657 };
43658
43659 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43660         /**
43661         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43662         */
43663         listWidth: undefined,
43664         
43665         selectedClass: 'active',
43666         
43667         invalidClass : "has-warning",
43668         
43669         validClass: 'has-success',
43670         
43671         allowed: '0123456789',
43672         
43673         max_length: 15,
43674         
43675         /**
43676          * @cfg {String} defaultDialCode The default dial code when initializing the input
43677          */
43678         defaultDialCode: '+852',
43679         
43680         /**
43681          * @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
43682          */
43683         preferedCountries: false,
43684         
43685         getAutoCreate : function()
43686         {
43687             var data = Roo.bootstrap.PhoneInputData();
43688             var align = this.labelAlign || this.parentLabelAlign();
43689             var id = Roo.id();
43690             
43691             this.allCountries = [];
43692             this.dialCodeMapping = [];
43693             
43694             for (var i = 0; i < data.length; i++) {
43695               var c = data[i];
43696               this.allCountries[i] = {
43697                 name: c[0],
43698                 iso2: c[1],
43699                 dialCode: c[2],
43700                 priority: c[3] || 0,
43701                 areaCodes: c[4] || null
43702               };
43703               this.dialCodeMapping[c[2]] = {
43704                   name: c[0],
43705                   iso2: c[1],
43706                   priority: c[3] || 0,
43707                   areaCodes: c[4] || null
43708               };
43709             }
43710             
43711             var cfg = {
43712                 cls: 'form-group',
43713                 cn: []
43714             };
43715             
43716             var input =  {
43717                 tag: 'input',
43718                 id : id,
43719                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43720                 maxlength: this.max_length,
43721                 cls : 'form-control tel-input',
43722                 autocomplete: 'new-password'
43723             };
43724             
43725             var hiddenInput = {
43726                 tag: 'input',
43727                 type: 'hidden',
43728                 cls: 'hidden-tel-input'
43729             };
43730             
43731             if (this.name) {
43732                 hiddenInput.name = this.name;
43733             }
43734             
43735             if (this.disabled) {
43736                 input.disabled = true;
43737             }
43738             
43739             var flag_container = {
43740                 tag: 'div',
43741                 cls: 'flag-box',
43742                 cn: [
43743                     {
43744                         tag: 'div',
43745                         cls: 'flag'
43746                     },
43747                     {
43748                         tag: 'div',
43749                         cls: 'caret'
43750                     }
43751                 ]
43752             };
43753             
43754             var box = {
43755                 tag: 'div',
43756                 cls: this.hasFeedback ? 'has-feedback' : '',
43757                 cn: [
43758                     hiddenInput,
43759                     input,
43760                     {
43761                         tag: 'input',
43762                         cls: 'dial-code-holder',
43763                         disabled: true
43764                     }
43765                 ]
43766             };
43767             
43768             var container = {
43769                 cls: 'roo-select2-container input-group',
43770                 cn: [
43771                     flag_container,
43772                     box
43773                 ]
43774             };
43775             
43776             if (this.fieldLabel.length) {
43777                 var indicator = {
43778                     tag: 'i',
43779                     tooltip: 'This field is required'
43780                 };
43781                 
43782                 var label = {
43783                     tag: 'label',
43784                     'for':  id,
43785                     cls: 'control-label',
43786                     cn: []
43787                 };
43788                 
43789                 var label_text = {
43790                     tag: 'span',
43791                     html: this.fieldLabel
43792                 };
43793                 
43794                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43795                 label.cn = [
43796                     indicator,
43797                     label_text
43798                 ];
43799                 
43800                 if(this.indicatorpos == 'right') {
43801                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43802                     label.cn = [
43803                         label_text,
43804                         indicator
43805                     ];
43806                 }
43807                 
43808                 if(align == 'left') {
43809                     container = {
43810                         tag: 'div',
43811                         cn: [
43812                             container
43813                         ]
43814                     };
43815                     
43816                     if(this.labelWidth > 12){
43817                         label.style = "width: " + this.labelWidth + 'px';
43818                     }
43819                     if(this.labelWidth < 13 && this.labelmd == 0){
43820                         this.labelmd = this.labelWidth;
43821                     }
43822                     if(this.labellg > 0){
43823                         label.cls += ' col-lg-' + this.labellg;
43824                         input.cls += ' col-lg-' + (12 - this.labellg);
43825                     }
43826                     if(this.labelmd > 0){
43827                         label.cls += ' col-md-' + this.labelmd;
43828                         container.cls += ' col-md-' + (12 - this.labelmd);
43829                     }
43830                     if(this.labelsm > 0){
43831                         label.cls += ' col-sm-' + this.labelsm;
43832                         container.cls += ' col-sm-' + (12 - this.labelsm);
43833                     }
43834                     if(this.labelxs > 0){
43835                         label.cls += ' col-xs-' + this.labelxs;
43836                         container.cls += ' col-xs-' + (12 - this.labelxs);
43837                     }
43838                 }
43839             }
43840             
43841             cfg.cn = [
43842                 label,
43843                 container
43844             ];
43845             
43846             var settings = this;
43847             
43848             ['xs','sm','md','lg'].map(function(size){
43849                 if (settings[size]) {
43850                     cfg.cls += ' col-' + size + '-' + settings[size];
43851                 }
43852             });
43853             
43854             this.store = new Roo.data.Store({
43855                 proxy : new Roo.data.MemoryProxy({}),
43856                 reader : new Roo.data.JsonReader({
43857                     fields : [
43858                         {
43859                             'name' : 'name',
43860                             'type' : 'string'
43861                         },
43862                         {
43863                             'name' : 'iso2',
43864                             'type' : 'string'
43865                         },
43866                         {
43867                             'name' : 'dialCode',
43868                             'type' : 'string'
43869                         },
43870                         {
43871                             'name' : 'priority',
43872                             'type' : 'string'
43873                         },
43874                         {
43875                             'name' : 'areaCodes',
43876                             'type' : 'string'
43877                         }
43878                     ]
43879                 })
43880             });
43881             
43882             if(!this.preferedCountries) {
43883                 this.preferedCountries = [
43884                     'hk',
43885                     'gb',
43886                     'us'
43887                 ];
43888             }
43889             
43890             var p = this.preferedCountries.reverse();
43891             
43892             if(p) {
43893                 for (var i = 0; i < p.length; i++) {
43894                     for (var j = 0; j < this.allCountries.length; j++) {
43895                         if(this.allCountries[j].iso2 == p[i]) {
43896                             var t = this.allCountries[j];
43897                             this.allCountries.splice(j,1);
43898                             this.allCountries.unshift(t);
43899                         }
43900                     } 
43901                 }
43902             }
43903             
43904             this.store.proxy.data = {
43905                 success: true,
43906                 data: this.allCountries
43907             };
43908             
43909             return cfg;
43910         },
43911         
43912         initEvents : function()
43913         {
43914             this.createList();
43915             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43916             
43917             this.indicator = this.indicatorEl();
43918             this.flag = this.flagEl();
43919             this.dialCodeHolder = this.dialCodeHolderEl();
43920             
43921             this.trigger = this.el.select('div.flag-box',true).first();
43922             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43923             
43924             var _this = this;
43925             
43926             (function(){
43927                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43928                 _this.list.setWidth(lw);
43929             }).defer(100);
43930             
43931             this.list.on('mouseover', this.onViewOver, this);
43932             this.list.on('mousemove', this.onViewMove, this);
43933             this.inputEl().on("keyup", this.onKeyUp, this);
43934             this.inputEl().on("keypress", this.onKeyPress, this);
43935             
43936             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43937
43938             this.view = new Roo.View(this.list, this.tpl, {
43939                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43940             });
43941             
43942             this.view.on('click', this.onViewClick, this);
43943             this.setValue(this.defaultDialCode);
43944         },
43945         
43946         onTriggerClick : function(e)
43947         {
43948             Roo.log('trigger click');
43949             if(this.disabled){
43950                 return;
43951             }
43952             
43953             if(this.isExpanded()){
43954                 this.collapse();
43955                 this.hasFocus = false;
43956             }else {
43957                 this.store.load({});
43958                 this.hasFocus = true;
43959                 this.expand();
43960             }
43961         },
43962         
43963         isExpanded : function()
43964         {
43965             return this.list.isVisible();
43966         },
43967         
43968         collapse : function()
43969         {
43970             if(!this.isExpanded()){
43971                 return;
43972             }
43973             this.list.hide();
43974             Roo.get(document).un('mousedown', this.collapseIf, this);
43975             Roo.get(document).un('mousewheel', this.collapseIf, this);
43976             this.fireEvent('collapse', this);
43977             this.validate();
43978         },
43979         
43980         expand : function()
43981         {
43982             Roo.log('expand');
43983
43984             if(this.isExpanded() || !this.hasFocus){
43985                 return;
43986             }
43987             
43988             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43989             this.list.setWidth(lw);
43990             
43991             this.list.show();
43992             this.restrictHeight();
43993             
43994             Roo.get(document).on('mousedown', this.collapseIf, this);
43995             Roo.get(document).on('mousewheel', this.collapseIf, this);
43996             
43997             this.fireEvent('expand', this);
43998         },
43999         
44000         restrictHeight : function()
44001         {
44002             this.list.alignTo(this.inputEl(), this.listAlign);
44003             this.list.alignTo(this.inputEl(), this.listAlign);
44004         },
44005         
44006         onViewOver : function(e, t)
44007         {
44008             if(this.inKeyMode){
44009                 return;
44010             }
44011             var item = this.view.findItemFromChild(t);
44012             
44013             if(item){
44014                 var index = this.view.indexOf(item);
44015                 this.select(index, false);
44016             }
44017         },
44018
44019         // private
44020         onViewClick : function(view, doFocus, el, e)
44021         {
44022             var index = this.view.getSelectedIndexes()[0];
44023             
44024             var r = this.store.getAt(index);
44025             
44026             if(r){
44027                 this.onSelect(r, index);
44028             }
44029             if(doFocus !== false && !this.blockFocus){
44030                 this.inputEl().focus();
44031             }
44032         },
44033         
44034         onViewMove : function(e, t)
44035         {
44036             this.inKeyMode = false;
44037         },
44038         
44039         select : function(index, scrollIntoView)
44040         {
44041             this.selectedIndex = index;
44042             this.view.select(index);
44043             if(scrollIntoView !== false){
44044                 var el = this.view.getNode(index);
44045                 if(el){
44046                     this.list.scrollChildIntoView(el, false);
44047                 }
44048             }
44049         },
44050         
44051         createList : function()
44052         {
44053             this.list = Roo.get(document.body).createChild({
44054                 tag: 'ul',
44055                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44056                 style: 'display:none'
44057             });
44058             
44059             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44060         },
44061         
44062         collapseIf : function(e)
44063         {
44064             var in_combo  = e.within(this.el);
44065             var in_list =  e.within(this.list);
44066             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44067             
44068             if (in_combo || in_list || is_list) {
44069                 return;
44070             }
44071             this.collapse();
44072         },
44073         
44074         onSelect : function(record, index)
44075         {
44076             if(this.fireEvent('beforeselect', this, record, index) !== false){
44077                 
44078                 this.setFlagClass(record.data.iso2);
44079                 this.setDialCode(record.data.dialCode);
44080                 this.hasFocus = false;
44081                 this.collapse();
44082                 this.fireEvent('select', this, record, index);
44083             }
44084         },
44085         
44086         flagEl : function()
44087         {
44088             var flag = this.el.select('div.flag',true).first();
44089             if(!flag){
44090                 return false;
44091             }
44092             return flag;
44093         },
44094         
44095         dialCodeHolderEl : function()
44096         {
44097             var d = this.el.select('input.dial-code-holder',true).first();
44098             if(!d){
44099                 return false;
44100             }
44101             return d;
44102         },
44103         
44104         setDialCode : function(v)
44105         {
44106             this.dialCodeHolder.dom.value = '+'+v;
44107         },
44108         
44109         setFlagClass : function(n)
44110         {
44111             this.flag.dom.className = 'flag '+n;
44112         },
44113         
44114         getValue : function()
44115         {
44116             var v = this.inputEl().getValue();
44117             if(this.dialCodeHolder) {
44118                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44119             }
44120             return v;
44121         },
44122         
44123         setValue : function(v)
44124         {
44125             var d = this.getDialCode(v);
44126             
44127             //invalid dial code
44128             if(v.length == 0 || !d || d.length == 0) {
44129                 if(this.rendered){
44130                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44131                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44132                 }
44133                 return;
44134             }
44135             
44136             //valid dial code
44137             this.setFlagClass(this.dialCodeMapping[d].iso2);
44138             this.setDialCode(d);
44139             this.inputEl().dom.value = v.replace('+'+d,'');
44140             this.hiddenEl().dom.value = this.getValue();
44141             
44142             this.validate();
44143         },
44144         
44145         getDialCode : function(v)
44146         {
44147             v = v ||  '';
44148             
44149             if (v.length == 0) {
44150                 return this.dialCodeHolder.dom.value;
44151             }
44152             
44153             var dialCode = "";
44154             if (v.charAt(0) != "+") {
44155                 return false;
44156             }
44157             var numericChars = "";
44158             for (var i = 1; i < v.length; i++) {
44159               var c = v.charAt(i);
44160               if (!isNaN(c)) {
44161                 numericChars += c;
44162                 if (this.dialCodeMapping[numericChars]) {
44163                   dialCode = v.substr(1, i);
44164                 }
44165                 if (numericChars.length == 4) {
44166                   break;
44167                 }
44168               }
44169             }
44170             return dialCode;
44171         },
44172         
44173         reset : function()
44174         {
44175             this.setValue(this.defaultDialCode);
44176             this.validate();
44177         },
44178         
44179         hiddenEl : function()
44180         {
44181             return this.el.select('input.hidden-tel-input',true).first();
44182         },
44183         
44184         // after setting val
44185         onKeyUp : function(e){
44186             this.setValue(this.getValue());
44187         },
44188         
44189         onKeyPress : function(e){
44190             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44191                 e.stopEvent();
44192             }
44193         }
44194         
44195 });
44196 /**
44197  * @class Roo.bootstrap.MoneyField
44198  * @extends Roo.bootstrap.ComboBox
44199  * Bootstrap MoneyField class
44200  * 
44201  * @constructor
44202  * Create a new MoneyField.
44203  * @param {Object} config Configuration options
44204  */
44205
44206 Roo.bootstrap.MoneyField = function(config) {
44207     
44208     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44209     
44210 };
44211
44212 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44213     
44214     /**
44215      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44216      */
44217     allowDecimals : true,
44218     /**
44219      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44220      */
44221     decimalSeparator : ".",
44222     /**
44223      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44224      */
44225     decimalPrecision : 0,
44226     /**
44227      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44228      */
44229     allowNegative : true,
44230     /**
44231      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44232      */
44233     allowZero: true,
44234     /**
44235      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44236      */
44237     minValue : Number.NEGATIVE_INFINITY,
44238     /**
44239      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44240      */
44241     maxValue : Number.MAX_VALUE,
44242     /**
44243      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44244      */
44245     minText : "The minimum value for this field is {0}",
44246     /**
44247      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44248      */
44249     maxText : "The maximum value for this field is {0}",
44250     /**
44251      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44252      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44253      */
44254     nanText : "{0} is not a valid number",
44255     /**
44256      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44257      */
44258     castInt : true,
44259     /**
44260      * @cfg {String} defaults currency of the MoneyField
44261      * value should be in lkey
44262      */
44263     defaultCurrency : false,
44264     /**
44265      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44266      */
44267     thousandsDelimiter : false,
44268     /**
44269      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44270      */
44271     max_length: false,
44272     
44273     inputlg : 9,
44274     inputmd : 9,
44275     inputsm : 9,
44276     inputxs : 6,
44277     
44278     store : false,
44279     
44280     getAutoCreate : function()
44281     {
44282         var align = this.labelAlign || this.parentLabelAlign();
44283         
44284         var id = Roo.id();
44285
44286         var cfg = {
44287             cls: 'form-group',
44288             cn: []
44289         };
44290
44291         var input =  {
44292             tag: 'input',
44293             id : id,
44294             cls : 'form-control roo-money-amount-input',
44295             autocomplete: 'new-password'
44296         };
44297         
44298         var hiddenInput = {
44299             tag: 'input',
44300             type: 'hidden',
44301             id: Roo.id(),
44302             cls: 'hidden-number-input'
44303         };
44304         
44305         if(this.max_length) {
44306             input.maxlength = this.max_length; 
44307         }
44308         
44309         if (this.name) {
44310             hiddenInput.name = this.name;
44311         }
44312
44313         if (this.disabled) {
44314             input.disabled = true;
44315         }
44316
44317         var clg = 12 - this.inputlg;
44318         var cmd = 12 - this.inputmd;
44319         var csm = 12 - this.inputsm;
44320         var cxs = 12 - this.inputxs;
44321         
44322         var container = {
44323             tag : 'div',
44324             cls : 'row roo-money-field',
44325             cn : [
44326                 {
44327                     tag : 'div',
44328                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44329                     cn : [
44330                         {
44331                             tag : 'div',
44332                             cls: 'roo-select2-container input-group',
44333                             cn: [
44334                                 {
44335                                     tag : 'input',
44336                                     cls : 'form-control roo-money-currency-input',
44337                                     autocomplete: 'new-password',
44338                                     readOnly : 1,
44339                                     name : this.currencyName
44340                                 },
44341                                 {
44342                                     tag :'span',
44343                                     cls : 'input-group-addon',
44344                                     cn : [
44345                                         {
44346                                             tag: 'span',
44347                                             cls: 'caret'
44348                                         }
44349                                     ]
44350                                 }
44351                             ]
44352                         }
44353                     ]
44354                 },
44355                 {
44356                     tag : 'div',
44357                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44358                     cn : [
44359                         {
44360                             tag: 'div',
44361                             cls: this.hasFeedback ? 'has-feedback' : '',
44362                             cn: [
44363                                 input
44364                             ]
44365                         }
44366                     ]
44367                 }
44368             ]
44369             
44370         };
44371         
44372         if (this.fieldLabel.length) {
44373             var indicator = {
44374                 tag: 'i',
44375                 tooltip: 'This field is required'
44376             };
44377
44378             var label = {
44379                 tag: 'label',
44380                 'for':  id,
44381                 cls: 'control-label',
44382                 cn: []
44383             };
44384
44385             var label_text = {
44386                 tag: 'span',
44387                 html: this.fieldLabel
44388             };
44389
44390             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44391             label.cn = [
44392                 indicator,
44393                 label_text
44394             ];
44395
44396             if(this.indicatorpos == 'right') {
44397                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44398                 label.cn = [
44399                     label_text,
44400                     indicator
44401                 ];
44402             }
44403
44404             if(align == 'left') {
44405                 container = {
44406                     tag: 'div',
44407                     cn: [
44408                         container
44409                     ]
44410                 };
44411
44412                 if(this.labelWidth > 12){
44413                     label.style = "width: " + this.labelWidth + 'px';
44414                 }
44415                 if(this.labelWidth < 13 && this.labelmd == 0){
44416                     this.labelmd = this.labelWidth;
44417                 }
44418                 if(this.labellg > 0){
44419                     label.cls += ' col-lg-' + this.labellg;
44420                     input.cls += ' col-lg-' + (12 - this.labellg);
44421                 }
44422                 if(this.labelmd > 0){
44423                     label.cls += ' col-md-' + this.labelmd;
44424                     container.cls += ' col-md-' + (12 - this.labelmd);
44425                 }
44426                 if(this.labelsm > 0){
44427                     label.cls += ' col-sm-' + this.labelsm;
44428                     container.cls += ' col-sm-' + (12 - this.labelsm);
44429                 }
44430                 if(this.labelxs > 0){
44431                     label.cls += ' col-xs-' + this.labelxs;
44432                     container.cls += ' col-xs-' + (12 - this.labelxs);
44433                 }
44434             }
44435         }
44436
44437         cfg.cn = [
44438             label,
44439             container,
44440             hiddenInput
44441         ];
44442         
44443         var settings = this;
44444
44445         ['xs','sm','md','lg'].map(function(size){
44446             if (settings[size]) {
44447                 cfg.cls += ' col-' + size + '-' + settings[size];
44448             }
44449         });
44450         
44451         return cfg;
44452     },
44453     
44454     initEvents : function()
44455     {
44456         this.indicator = this.indicatorEl();
44457         
44458         this.initCurrencyEvent();
44459         
44460         this.initNumberEvent();
44461     },
44462     
44463     initCurrencyEvent : function()
44464     {
44465         if (!this.store) {
44466             throw "can not find store for combo";
44467         }
44468         
44469         this.store = Roo.factory(this.store, Roo.data);
44470         this.store.parent = this;
44471         
44472         this.createList();
44473         
44474         this.triggerEl = this.el.select('.input-group-addon', true).first();
44475         
44476         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44477         
44478         var _this = this;
44479         
44480         (function(){
44481             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44482             _this.list.setWidth(lw);
44483         }).defer(100);
44484         
44485         this.list.on('mouseover', this.onViewOver, this);
44486         this.list.on('mousemove', this.onViewMove, this);
44487         this.list.on('scroll', this.onViewScroll, this);
44488         
44489         if(!this.tpl){
44490             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44491         }
44492         
44493         this.view = new Roo.View(this.list, this.tpl, {
44494             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44495         });
44496         
44497         this.view.on('click', this.onViewClick, this);
44498         
44499         this.store.on('beforeload', this.onBeforeLoad, this);
44500         this.store.on('load', this.onLoad, this);
44501         this.store.on('loadexception', this.onLoadException, this);
44502         
44503         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44504             "up" : function(e){
44505                 this.inKeyMode = true;
44506                 this.selectPrev();
44507             },
44508
44509             "down" : function(e){
44510                 if(!this.isExpanded()){
44511                     this.onTriggerClick();
44512                 }else{
44513                     this.inKeyMode = true;
44514                     this.selectNext();
44515                 }
44516             },
44517
44518             "enter" : function(e){
44519                 this.collapse();
44520                 
44521                 if(this.fireEvent("specialkey", this, e)){
44522                     this.onViewClick(false);
44523                 }
44524                 
44525                 return true;
44526             },
44527
44528             "esc" : function(e){
44529                 this.collapse();
44530             },
44531
44532             "tab" : function(e){
44533                 this.collapse();
44534                 
44535                 if(this.fireEvent("specialkey", this, e)){
44536                     this.onViewClick(false);
44537                 }
44538                 
44539                 return true;
44540             },
44541
44542             scope : this,
44543
44544             doRelay : function(foo, bar, hname){
44545                 if(hname == 'down' || this.scope.isExpanded()){
44546                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44547                 }
44548                 return true;
44549             },
44550
44551             forceKeyDown: true
44552         });
44553         
44554         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44555         
44556     },
44557     
44558     initNumberEvent : function(e)
44559     {
44560         this.inputEl().on("keydown" , this.fireKey,  this);
44561         this.inputEl().on("focus", this.onFocus,  this);
44562         this.inputEl().on("blur", this.onBlur,  this);
44563         
44564         this.inputEl().relayEvent('keyup', this);
44565         
44566         if(this.indicator){
44567             this.indicator.addClass('invisible');
44568         }
44569  
44570         this.originalValue = this.getValue();
44571         
44572         if(this.validationEvent == 'keyup'){
44573             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44574             this.inputEl().on('keyup', this.filterValidation, this);
44575         }
44576         else if(this.validationEvent !== false){
44577             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44578         }
44579         
44580         if(this.selectOnFocus){
44581             this.on("focus", this.preFocus, this);
44582             
44583         }
44584         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44585             this.inputEl().on("keypress", this.filterKeys, this);
44586         } else {
44587             this.inputEl().relayEvent('keypress', this);
44588         }
44589         
44590         var allowed = "0123456789";
44591         
44592         if(this.allowDecimals){
44593             allowed += this.decimalSeparator;
44594         }
44595         
44596         if(this.allowNegative){
44597             allowed += "-";
44598         }
44599         
44600         if(this.thousandsDelimiter) {
44601             allowed += ",";
44602         }
44603         
44604         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44605         
44606         var keyPress = function(e){
44607             
44608             var k = e.getKey();
44609             
44610             var c = e.getCharCode();
44611             
44612             if(
44613                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44614                     allowed.indexOf(String.fromCharCode(c)) === -1
44615             ){
44616                 e.stopEvent();
44617                 return;
44618             }
44619             
44620             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44621                 return;
44622             }
44623             
44624             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44625                 e.stopEvent();
44626             }
44627         };
44628         
44629         this.inputEl().on("keypress", keyPress, this);
44630         
44631     },
44632     
44633     onTriggerClick : function(e)
44634     {   
44635         if(this.disabled){
44636             return;
44637         }
44638         
44639         this.page = 0;
44640         this.loadNext = false;
44641         
44642         if(this.isExpanded()){
44643             this.collapse();
44644             return;
44645         }
44646         
44647         this.hasFocus = true;
44648         
44649         if(this.triggerAction == 'all') {
44650             this.doQuery(this.allQuery, true);
44651             return;
44652         }
44653         
44654         this.doQuery(this.getRawValue());
44655     },
44656     
44657     getCurrency : function()
44658     {   
44659         var v = this.currencyEl().getValue();
44660         
44661         return v;
44662     },
44663     
44664     restrictHeight : function()
44665     {
44666         this.list.alignTo(this.currencyEl(), this.listAlign);
44667         this.list.alignTo(this.currencyEl(), this.listAlign);
44668     },
44669     
44670     onViewClick : function(view, doFocus, el, e)
44671     {
44672         var index = this.view.getSelectedIndexes()[0];
44673         
44674         var r = this.store.getAt(index);
44675         
44676         if(r){
44677             this.onSelect(r, index);
44678         }
44679     },
44680     
44681     onSelect : function(record, index){
44682         
44683         if(this.fireEvent('beforeselect', this, record, index) !== false){
44684         
44685             this.setFromCurrencyData(index > -1 ? record.data : false);
44686             
44687             this.collapse();
44688             
44689             this.fireEvent('select', this, record, index);
44690         }
44691     },
44692     
44693     setFromCurrencyData : function(o)
44694     {
44695         var currency = '';
44696         
44697         this.lastCurrency = o;
44698         
44699         if (this.currencyField) {
44700             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44701         } else {
44702             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44703         }
44704         
44705         this.lastSelectionText = currency;
44706         
44707         //setting default currency
44708         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44709             this.setCurrency(this.defaultCurrency);
44710             return;
44711         }
44712         
44713         this.setCurrency(currency);
44714     },
44715     
44716     setFromData : function(o)
44717     {
44718         var c = {};
44719         
44720         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44721         
44722         this.setFromCurrencyData(c);
44723         
44724         var value = '';
44725         
44726         if (this.name) {
44727             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44728         } else {
44729             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44730         }
44731         
44732         this.setValue(value);
44733         
44734     },
44735     
44736     setCurrency : function(v)
44737     {   
44738         this.currencyValue = v;
44739         
44740         if(this.rendered){
44741             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44742             this.validate();
44743         }
44744     },
44745     
44746     setValue : function(v)
44747     {
44748         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44749         
44750         this.value = v;
44751         
44752         if(this.rendered){
44753             
44754             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44755             
44756             this.inputEl().dom.value = (v == '') ? '' :
44757                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44758             
44759             if(!this.allowZero && v === '0') {
44760                 this.hiddenEl().dom.value = '';
44761                 this.inputEl().dom.value = '';
44762             }
44763             
44764             this.validate();
44765         }
44766     },
44767     
44768     getRawValue : function()
44769     {
44770         var v = this.inputEl().getValue();
44771         
44772         return v;
44773     },
44774     
44775     getValue : function()
44776     {
44777         return this.fixPrecision(this.parseValue(this.getRawValue()));
44778     },
44779     
44780     parseValue : function(value)
44781     {
44782         if(this.thousandsDelimiter) {
44783             value += "";
44784             r = new RegExp(",", "g");
44785             value = value.replace(r, "");
44786         }
44787         
44788         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44789         return isNaN(value) ? '' : value;
44790         
44791     },
44792     
44793     fixPrecision : function(value)
44794     {
44795         if(this.thousandsDelimiter) {
44796             value += "";
44797             r = new RegExp(",", "g");
44798             value = value.replace(r, "");
44799         }
44800         
44801         var nan = isNaN(value);
44802         
44803         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44804             return nan ? '' : value;
44805         }
44806         return parseFloat(value).toFixed(this.decimalPrecision);
44807     },
44808     
44809     decimalPrecisionFcn : function(v)
44810     {
44811         return Math.floor(v);
44812     },
44813     
44814     validateValue : function(value)
44815     {
44816         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44817             return false;
44818         }
44819         
44820         var num = this.parseValue(value);
44821         
44822         if(isNaN(num)){
44823             this.markInvalid(String.format(this.nanText, value));
44824             return false;
44825         }
44826         
44827         if(num < this.minValue){
44828             this.markInvalid(String.format(this.minText, this.minValue));
44829             return false;
44830         }
44831         
44832         if(num > this.maxValue){
44833             this.markInvalid(String.format(this.maxText, this.maxValue));
44834             return false;
44835         }
44836         
44837         return true;
44838     },
44839     
44840     validate : function()
44841     {
44842         if(this.disabled || this.allowBlank){
44843             this.markValid();
44844             return true;
44845         }
44846         
44847         var currency = this.getCurrency();
44848         
44849         if(this.validateValue(this.getRawValue()) && currency.length){
44850             this.markValid();
44851             return true;
44852         }
44853         
44854         this.markInvalid();
44855         return false;
44856     },
44857     
44858     getName: function()
44859     {
44860         return this.name;
44861     },
44862     
44863     beforeBlur : function()
44864     {
44865         if(!this.castInt){
44866             return;
44867         }
44868         
44869         var v = this.parseValue(this.getRawValue());
44870         
44871         if(v || v == 0){
44872             this.setValue(v);
44873         }
44874     },
44875     
44876     onBlur : function()
44877     {
44878         this.beforeBlur();
44879         
44880         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44881             //this.el.removeClass(this.focusClass);
44882         }
44883         
44884         this.hasFocus = false;
44885         
44886         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44887             this.validate();
44888         }
44889         
44890         var v = this.getValue();
44891         
44892         if(String(v) !== String(this.startValue)){
44893             this.fireEvent('change', this, v, this.startValue);
44894         }
44895         
44896         this.fireEvent("blur", this);
44897     },
44898     
44899     inputEl : function()
44900     {
44901         return this.el.select('.roo-money-amount-input', true).first();
44902     },
44903     
44904     currencyEl : function()
44905     {
44906         return this.el.select('.roo-money-currency-input', true).first();
44907     },
44908     
44909     hiddenEl : function()
44910     {
44911         return this.el.select('input.hidden-number-input',true).first();
44912     }
44913     
44914 });/**
44915  * @class Roo.bootstrap.BezierSignature
44916  * @extends Roo.bootstrap.Component
44917  * Bootstrap BezierSignature class
44918  * This script refer to:
44919  *    Title: Signature Pad
44920  *    Author: szimek
44921  *    Availability: https://github.com/szimek/signature_pad
44922  *
44923  * @constructor
44924  * Create a new BezierSignature
44925  * @param {Object} config The config object
44926  */
44927
44928 Roo.bootstrap.BezierSignature = function(config){
44929     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44930     this.addEvents({
44931         "resize" : true
44932     });
44933 };
44934
44935 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44936 {
44937      
44938     curve_data: [],
44939     
44940     is_empty: true,
44941     
44942     mouse_btn_down: true,
44943     
44944     /**
44945      * @cfg {int} canvas height
44946      */
44947     canvas_height: '200px',
44948     
44949     /**
44950      * @cfg {float|function} Radius of a single dot.
44951      */ 
44952     dot_size: false,
44953     
44954     /**
44955      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44956      */
44957     min_width: 0.5,
44958     
44959     /**
44960      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44961      */
44962     max_width: 2.5,
44963     
44964     /**
44965      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44966      */
44967     throttle: 16,
44968     
44969     /**
44970      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44971      */
44972     min_distance: 5,
44973     
44974     /**
44975      * @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.
44976      */
44977     bg_color: 'rgba(0, 0, 0, 0)',
44978     
44979     /**
44980      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44981      */
44982     dot_color: 'black',
44983     
44984     /**
44985      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44986      */ 
44987     velocity_filter_weight: 0.7,
44988     
44989     /**
44990      * @cfg {function} Callback when stroke begin. 
44991      */
44992     onBegin: false,
44993     
44994     /**
44995      * @cfg {function} Callback when stroke end.
44996      */
44997     onEnd: false,
44998     
44999     getAutoCreate : function()
45000     {
45001         var cls = 'roo-signature column';
45002         
45003         if(this.cls){
45004             cls += ' ' + this.cls;
45005         }
45006         
45007         var col_sizes = [
45008             'lg',
45009             'md',
45010             'sm',
45011             'xs'
45012         ];
45013         
45014         for(var i = 0; i < col_sizes.length; i++) {
45015             if(this[col_sizes[i]]) {
45016                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45017             }
45018         }
45019         
45020         var cfg = {
45021             tag: 'div',
45022             cls: cls,
45023             cn: [
45024                 {
45025                     tag: 'div',
45026                     cls: 'roo-signature-body',
45027                     cn: [
45028                         {
45029                             tag: 'canvas',
45030                             cls: 'roo-signature-body-canvas',
45031                             height: this.canvas_height,
45032                             width: this.canvas_width
45033                         }
45034                     ]
45035                 },
45036                 {
45037                     tag: 'input',
45038                     type: 'file',
45039                     style: 'display: none'
45040                 }
45041             ]
45042         };
45043         
45044         return cfg;
45045     },
45046     
45047     initEvents: function() 
45048     {
45049         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45050         
45051         var canvas = this.canvasEl();
45052         
45053         // mouse && touch event swapping...
45054         canvas.dom.style.touchAction = 'none';
45055         canvas.dom.style.msTouchAction = 'none';
45056         
45057         this.mouse_btn_down = false;
45058         canvas.on('mousedown', this._handleMouseDown, this);
45059         canvas.on('mousemove', this._handleMouseMove, this);
45060         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45061         
45062         if (window.PointerEvent) {
45063             canvas.on('pointerdown', this._handleMouseDown, this);
45064             canvas.on('pointermove', this._handleMouseMove, this);
45065             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45066         }
45067         
45068         if ('ontouchstart' in window) {
45069             canvas.on('touchstart', this._handleTouchStart, this);
45070             canvas.on('touchmove', this._handleTouchMove, this);
45071             canvas.on('touchend', this._handleTouchEnd, this);
45072         }
45073         
45074         Roo.EventManager.onWindowResize(this.resize, this, true);
45075         
45076         // file input event
45077         this.fileEl().on('change', this.uploadImage, this);
45078         
45079         this.clear();
45080         
45081         this.resize();
45082     },
45083     
45084     resize: function(){
45085         
45086         var canvas = this.canvasEl().dom;
45087         var ctx = this.canvasElCtx();
45088         var img_data = false;
45089         
45090         if(canvas.width > 0) {
45091             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45092         }
45093         // setting canvas width will clean img data
45094         canvas.width = 0;
45095         
45096         var style = window.getComputedStyle ? 
45097             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45098             
45099         var padding_left = parseInt(style.paddingLeft) || 0;
45100         var padding_right = parseInt(style.paddingRight) || 0;
45101         
45102         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45103         
45104         if(img_data) {
45105             ctx.putImageData(img_data, 0, 0);
45106         }
45107     },
45108     
45109     _handleMouseDown: function(e)
45110     {
45111         if (e.browserEvent.which === 1) {
45112             this.mouse_btn_down = true;
45113             this.strokeBegin(e);
45114         }
45115     },
45116     
45117     _handleMouseMove: function (e)
45118     {
45119         if (this.mouse_btn_down) {
45120             this.strokeMoveUpdate(e);
45121         }
45122     },
45123     
45124     _handleMouseUp: function (e)
45125     {
45126         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45127             this.mouse_btn_down = false;
45128             this.strokeEnd(e);
45129         }
45130     },
45131     
45132     _handleTouchStart: function (e) {
45133         
45134         e.preventDefault();
45135         if (e.browserEvent.targetTouches.length === 1) {
45136             // var touch = e.browserEvent.changedTouches[0];
45137             // this.strokeBegin(touch);
45138             
45139              this.strokeBegin(e); // assume e catching the correct xy...
45140         }
45141     },
45142     
45143     _handleTouchMove: function (e) {
45144         e.preventDefault();
45145         // var touch = event.targetTouches[0];
45146         // _this._strokeMoveUpdate(touch);
45147         this.strokeMoveUpdate(e);
45148     },
45149     
45150     _handleTouchEnd: function (e) {
45151         var wasCanvasTouched = e.target === this.canvasEl().dom;
45152         if (wasCanvasTouched) {
45153             e.preventDefault();
45154             // var touch = event.changedTouches[0];
45155             // _this._strokeEnd(touch);
45156             this.strokeEnd(e);
45157         }
45158     },
45159     
45160     reset: function () {
45161         this._lastPoints = [];
45162         this._lastVelocity = 0;
45163         this._lastWidth = (this.min_width + this.max_width) / 2;
45164         this.canvasElCtx().fillStyle = this.dot_color;
45165     },
45166     
45167     strokeMoveUpdate: function(e)
45168     {
45169         this.strokeUpdate(e);
45170         
45171         if (this.throttle) {
45172             this.throttleStroke(this.strokeUpdate, this.throttle);
45173         }
45174         else {
45175             this.strokeUpdate(e);
45176         }
45177     },
45178     
45179     strokeBegin: function(e)
45180     {
45181         var newPointGroup = {
45182             color: this.dot_color,
45183             points: []
45184         };
45185         
45186         if (typeof this.onBegin === 'function') {
45187             this.onBegin(e);
45188         }
45189         
45190         this.curve_data.push(newPointGroup);
45191         this.reset();
45192         this.strokeUpdate(e);
45193     },
45194     
45195     strokeUpdate: function(e)
45196     {
45197         var rect = this.canvasEl().dom.getBoundingClientRect();
45198         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45199         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45200         var lastPoints = lastPointGroup.points;
45201         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45202         var isLastPointTooClose = lastPoint
45203             ? point.distanceTo(lastPoint) <= this.min_distance
45204             : false;
45205         var color = lastPointGroup.color;
45206         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45207             var curve = this.addPoint(point);
45208             if (!lastPoint) {
45209                 this.drawDot({color: color, point: point});
45210             }
45211             else if (curve) {
45212                 this.drawCurve({color: color, curve: curve});
45213             }
45214             lastPoints.push({
45215                 time: point.time,
45216                 x: point.x,
45217                 y: point.y
45218             });
45219         }
45220     },
45221     
45222     strokeEnd: function(e)
45223     {
45224         this.strokeUpdate(e);
45225         if (typeof this.onEnd === 'function') {
45226             this.onEnd(e);
45227         }
45228     },
45229     
45230     addPoint:  function (point) {
45231         var _lastPoints = this._lastPoints;
45232         _lastPoints.push(point);
45233         if (_lastPoints.length > 2) {
45234             if (_lastPoints.length === 3) {
45235                 _lastPoints.unshift(_lastPoints[0]);
45236             }
45237             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45238             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45239             _lastPoints.shift();
45240             return curve;
45241         }
45242         return null;
45243     },
45244     
45245     calculateCurveWidths: function (startPoint, endPoint) {
45246         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45247             (1 - this.velocity_filter_weight) * this._lastVelocity;
45248
45249         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45250         var widths = {
45251             end: newWidth,
45252             start: this._lastWidth
45253         };
45254         
45255         this._lastVelocity = velocity;
45256         this._lastWidth = newWidth;
45257         return widths;
45258     },
45259     
45260     drawDot: function (_a) {
45261         var color = _a.color, point = _a.point;
45262         var ctx = this.canvasElCtx();
45263         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45264         ctx.beginPath();
45265         this.drawCurveSegment(point.x, point.y, width);
45266         ctx.closePath();
45267         ctx.fillStyle = color;
45268         ctx.fill();
45269     },
45270     
45271     drawCurve: function (_a) {
45272         var color = _a.color, curve = _a.curve;
45273         var ctx = this.canvasElCtx();
45274         var widthDelta = curve.endWidth - curve.startWidth;
45275         var drawSteps = Math.floor(curve.length()) * 2;
45276         ctx.beginPath();
45277         ctx.fillStyle = color;
45278         for (var i = 0; i < drawSteps; i += 1) {
45279         var t = i / drawSteps;
45280         var tt = t * t;
45281         var ttt = tt * t;
45282         var u = 1 - t;
45283         var uu = u * u;
45284         var uuu = uu * u;
45285         var x = uuu * curve.startPoint.x;
45286         x += 3 * uu * t * curve.control1.x;
45287         x += 3 * u * tt * curve.control2.x;
45288         x += ttt * curve.endPoint.x;
45289         var y = uuu * curve.startPoint.y;
45290         y += 3 * uu * t * curve.control1.y;
45291         y += 3 * u * tt * curve.control2.y;
45292         y += ttt * curve.endPoint.y;
45293         var width = curve.startWidth + ttt * widthDelta;
45294         this.drawCurveSegment(x, y, width);
45295         }
45296         ctx.closePath();
45297         ctx.fill();
45298     },
45299     
45300     drawCurveSegment: function (x, y, width) {
45301         var ctx = this.canvasElCtx();
45302         ctx.moveTo(x, y);
45303         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45304         this.is_empty = false;
45305     },
45306     
45307     clear: function()
45308     {
45309         var ctx = this.canvasElCtx();
45310         var canvas = this.canvasEl().dom;
45311         ctx.fillStyle = this.bg_color;
45312         ctx.clearRect(0, 0, canvas.width, canvas.height);
45313         ctx.fillRect(0, 0, canvas.width, canvas.height);
45314         this.curve_data = [];
45315         this.reset();
45316         this.is_empty = true;
45317     },
45318     
45319     fileEl: function()
45320     {
45321         return  this.el.select('input',true).first();
45322     },
45323     
45324     canvasEl: function()
45325     {
45326         return this.el.select('canvas',true).first();
45327     },
45328     
45329     canvasElCtx: function()
45330     {
45331         return this.el.select('canvas',true).first().dom.getContext('2d');
45332     },
45333     
45334     getImage: function(type)
45335     {
45336         if(this.is_empty) {
45337             return false;
45338         }
45339         
45340         // encryption ?
45341         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45342     },
45343     
45344     drawFromImage: function(img_src)
45345     {
45346         var img = new Image();
45347         
45348         img.onload = function(){
45349             this.canvasElCtx().drawImage(img, 0, 0);
45350         }.bind(this);
45351         
45352         img.src = img_src;
45353         
45354         this.is_empty = false;
45355     },
45356     
45357     selectImage: function()
45358     {
45359         this.fileEl().dom.click();
45360     },
45361     
45362     uploadImage: function(e)
45363     {
45364         var reader = new FileReader();
45365         
45366         reader.onload = function(e){
45367             var img = new Image();
45368             img.onload = function(){
45369                 this.reset();
45370                 this.canvasElCtx().drawImage(img, 0, 0);
45371             }.bind(this);
45372             img.src = e.target.result;
45373         }.bind(this);
45374         
45375         reader.readAsDataURL(e.target.files[0]);
45376     },
45377     
45378     // Bezier Point Constructor
45379     Point: (function () {
45380         function Point(x, y, time) {
45381             this.x = x;
45382             this.y = y;
45383             this.time = time || Date.now();
45384         }
45385         Point.prototype.distanceTo = function (start) {
45386             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45387         };
45388         Point.prototype.equals = function (other) {
45389             return this.x === other.x && this.y === other.y && this.time === other.time;
45390         };
45391         Point.prototype.velocityFrom = function (start) {
45392             return this.time !== start.time
45393             ? this.distanceTo(start) / (this.time - start.time)
45394             : 0;
45395         };
45396         return Point;
45397     }()),
45398     
45399     
45400     // Bezier Constructor
45401     Bezier: (function () {
45402         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45403             this.startPoint = startPoint;
45404             this.control2 = control2;
45405             this.control1 = control1;
45406             this.endPoint = endPoint;
45407             this.startWidth = startWidth;
45408             this.endWidth = endWidth;
45409         }
45410         Bezier.fromPoints = function (points, widths, scope) {
45411             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45412             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45413             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45414         };
45415         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45416             var dx1 = s1.x - s2.x;
45417             var dy1 = s1.y - s2.y;
45418             var dx2 = s2.x - s3.x;
45419             var dy2 = s2.y - s3.y;
45420             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45421             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45422             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45423             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45424             var dxm = m1.x - m2.x;
45425             var dym = m1.y - m2.y;
45426             var k = l2 / (l1 + l2);
45427             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45428             var tx = s2.x - cm.x;
45429             var ty = s2.y - cm.y;
45430             return {
45431                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45432                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45433             };
45434         };
45435         Bezier.prototype.length = function () {
45436             var steps = 10;
45437             var length = 0;
45438             var px;
45439             var py;
45440             for (var i = 0; i <= steps; i += 1) {
45441                 var t = i / steps;
45442                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45443                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45444                 if (i > 0) {
45445                     var xdiff = cx - px;
45446                     var ydiff = cy - py;
45447                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45448                 }
45449                 px = cx;
45450                 py = cy;
45451             }
45452             return length;
45453         };
45454         Bezier.prototype.point = function (t, start, c1, c2, end) {
45455             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45456             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45457             + (3.0 * c2 * (1.0 - t) * t * t)
45458             + (end * t * t * t);
45459         };
45460         return Bezier;
45461     }()),
45462     
45463     throttleStroke: function(fn, wait) {
45464       if (wait === void 0) { wait = 250; }
45465       var previous = 0;
45466       var timeout = null;
45467       var result;
45468       var storedContext;
45469       var storedArgs;
45470       var later = function () {
45471           previous = Date.now();
45472           timeout = null;
45473           result = fn.apply(storedContext, storedArgs);
45474           if (!timeout) {
45475               storedContext = null;
45476               storedArgs = [];
45477           }
45478       };
45479       return function wrapper() {
45480           var args = [];
45481           for (var _i = 0; _i < arguments.length; _i++) {
45482               args[_i] = arguments[_i];
45483           }
45484           var now = Date.now();
45485           var remaining = wait - (now - previous);
45486           storedContext = this;
45487           storedArgs = args;
45488           if (remaining <= 0 || remaining > wait) {
45489               if (timeout) {
45490                   clearTimeout(timeout);
45491                   timeout = null;
45492               }
45493               previous = now;
45494               result = fn.apply(storedContext, storedArgs);
45495               if (!timeout) {
45496                   storedContext = null;
45497                   storedArgs = [];
45498               }
45499           }
45500           else if (!timeout) {
45501               timeout = window.setTimeout(later, remaining);
45502           }
45503           return result;
45504       };
45505   }
45506   
45507 });
45508
45509  
45510
45511