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  * Bootstrap Element class
656  * @cfg {String} html contents of the element
657  * @cfg {String} tag tag of the element
658  * @cfg {String} cls class of the element
659  * @cfg {Boolean} preventDefault (true|false) default false
660  * @cfg {Boolean} clickable (true|false) default false
661  * @cfg {String} role default blank - set to button to force cursor pointer
662  
663  * 
664  * @constructor
665  * Create a new Element
666  * @param {Object} config The config object
667  */
668
669 Roo.bootstrap.Element = function(config){
670     Roo.bootstrap.Element.superclass.constructor.call(this, config);
671     
672     this.addEvents({
673         // raw events
674         /**
675          * @event click
676          * When a element is chick
677          * @param {Roo.bootstrap.Element} this
678          * @param {Roo.EventObject} e
679          */
680         "click" : true 
681         
682       
683     });
684 };
685
686 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
687     
688     tag: 'div',
689     cls: '',
690     html: '',
691     preventDefault: false, 
692     clickable: false,
693     tapedTwice : false,
694     role : false,
695     
696     getAutoCreate : function(){
697         
698         var cfg = {
699             tag: this.tag,
700             // cls: this.cls, double assign in parent class Component.js :: onRender
701             html: this.html
702         };
703         if (this.role !== false) {
704             cfg.role = this.role;
705         }
706         
707         return cfg;
708     },
709     
710     initEvents: function() 
711     {
712         Roo.bootstrap.Element.superclass.initEvents.call(this);
713         
714         if(this.clickable){
715             this.el.on('click', this.onClick, this);
716         }
717         
718         
719     },
720     
721     onClick : function(e)
722     {
723         if(this.preventDefault){
724             e.preventDefault();
725         }
726         
727         this.fireEvent('click', this, e); // why was this double click before?
728     },
729     
730     
731     
732
733     
734     
735     getValue : function()
736     {
737         return this.el.dom.innerHTML;
738     },
739     
740     setValue : function(value)
741     {
742         this.el.dom.innerHTML = value;
743     }
744    
745 });
746
747  
748
749  /*
750  * - LGPL
751  *
752  * dropable area
753  * 
754  */
755
756 /**
757  * @class Roo.bootstrap.DropTarget
758  * @extends Roo.bootstrap.Element
759  * Bootstrap DropTarget class
760  
761  * @cfg {string} name dropable name
762  * 
763  * @constructor
764  * Create a new Dropable Area
765  * @param {Object} config The config object
766  */
767
768 Roo.bootstrap.DropTarget = function(config){
769     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
770     
771     this.addEvents({
772         // raw events
773         /**
774          * @event click
775          * When a element is chick
776          * @param {Roo.bootstrap.Element} this
777          * @param {Roo.EventObject} e
778          */
779         "drop" : true
780     });
781 };
782
783 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
784     
785     
786     getAutoCreate : function(){
787         
788          
789     },
790     
791     initEvents: function() 
792     {
793         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
794         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
795             ddGroup: this.name,
796             listeners : {
797                 drop : this.dragDrop.createDelegate(this),
798                 enter : this.dragEnter.createDelegate(this),
799                 out : this.dragOut.createDelegate(this),
800                 over : this.dragOver.createDelegate(this)
801             }
802             
803         });
804         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
805     },
806     
807     dragDrop : function(source,e,data)
808     {
809         // user has to decide how to impliment this.
810         Roo.log('drop');
811         Roo.log(this);
812         //this.fireEvent('drop', this, source, e ,data);
813         return false;
814     },
815     
816     dragEnter : function(n, dd, e, data)
817     {
818         // probably want to resize the element to match the dropped element..
819         Roo.log("enter");
820         this.originalSize = this.el.getSize();
821         this.el.setSize( n.el.getSize());
822         this.dropZone.DDM.refreshCache(this.name);
823         Roo.log([n, dd, e, data]);
824     },
825     
826     dragOut : function(value)
827     {
828         // resize back to normal
829         Roo.log("out");
830         this.el.setSize(this.originalSize);
831         this.dropZone.resetConstraints();
832     },
833     
834     dragOver : function()
835     {
836         // ??? do nothing?
837     }
838    
839 });
840
841  
842
843  /*
844  * - LGPL
845  *
846  * Body
847  *
848  */
849
850 /**
851  * @class Roo.bootstrap.Body
852  * @extends Roo.bootstrap.Component
853  * @builder-top
854  * @children Roo.bootstrap.Component
855  * @parent none
856  * Bootstrap Body class
857  *
858  * @constructor
859  * Create a new body
860  * @param {Object} config The config object
861  */
862
863 Roo.bootstrap.Body = function(config){
864
865     config = config || {};
866
867     Roo.bootstrap.Body.superclass.constructor.call(this, config);
868     this.el = Roo.get(config.el ? config.el : document.body );
869     if (this.cls && this.cls.length) {
870         Roo.get(document.body).addClass(this.cls);
871     }
872 };
873
874 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
875
876     is_body : true,// just to make sure it's constructed?
877
878         autoCreate : {
879         cls: 'container'
880     },
881     onRender : function(ct, position)
882     {
883        /* Roo.log("Roo.bootstrap.Body - onRender");
884         if (this.cls && this.cls.length) {
885             Roo.get(document.body).addClass(this.cls);
886         }
887         // style??? xttr???
888         */
889     }
890
891
892
893
894 });
895 /*
896  * - LGPL
897  *
898  * button group
899  * 
900  */
901
902
903 /**
904  * @class Roo.bootstrap.ButtonGroup
905  * @extends Roo.bootstrap.Component
906  * Bootstrap ButtonGroup class
907  * @cfg {String} size lg | sm | xs (default empty normal)
908  * @cfg {String} align vertical | justified  (default none)
909  * @cfg {String} direction up | down (default down)
910  * @cfg {Boolean} toolbar false | true
911  * @cfg {Boolean} btn true | false
912  * 
913  * 
914  * @constructor
915  * Create a new Input
916  * @param {Object} config The config object
917  */
918
919 Roo.bootstrap.ButtonGroup = function(config){
920     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
921 };
922
923 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
924     
925     size: '',
926     align: '',
927     direction: '',
928     toolbar: false,
929     btn: true,
930
931     getAutoCreate : function(){
932         var cfg = {
933             cls: 'btn-group',
934             html : null
935         };
936         
937         cfg.html = this.html || cfg.html;
938         
939         if (this.toolbar) {
940             cfg = {
941                 cls: 'btn-toolbar',
942                 html: null
943             };
944             
945             return cfg;
946         }
947         
948         if (['vertical','justified'].indexOf(this.align)!==-1) {
949             cfg.cls = 'btn-group-' + this.align;
950             
951             if (this.align == 'justified') {
952                 console.log(this.items);
953             }
954         }
955         
956         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
957             cfg.cls += ' btn-group-' + this.size;
958         }
959         
960         if (this.direction == 'up') {
961             cfg.cls += ' dropup' ;
962         }
963         
964         return cfg;
965     },
966     /**
967      * Add a button to the group (similar to NavItem API.)
968      */
969     addItem : function(cfg)
970     {
971         var cn = new Roo.bootstrap.Button(cfg);
972         //this.register(cn);
973         cn.parentId = this.id;
974         cn.onRender(this.el, null);
975         return cn;
976     }
977    
978 });
979
980  /*
981  * - LGPL
982  *
983  * button
984  * 
985  */
986
987 /**
988  * @class Roo.bootstrap.Button
989  * @extends Roo.bootstrap.Component
990  * Bootstrap Button class
991  * @cfg {String} html The button content
992  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
993  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
994  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
995  * @cfg {String} size (lg|sm|xs)
996  * @cfg {String} tag (a|input|submit)
997  * @cfg {String} href empty or href
998  * @cfg {Boolean} disabled default false;
999  * @cfg {Boolean} isClose default false;
1000  * @cfg {String} glyphicon depricated - use fa
1001  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1002  * @cfg {String} badge text for badge
1003  * @cfg {String} theme (default|glow)  
1004  * @cfg {Boolean} inverse dark themed version
1005  * @cfg {Boolean} toggle is it a slidy toggle button
1006  * @cfg {Boolean} pressed   default null - if the button ahs active state
1007  * @cfg {String} ontext text for on slidy toggle state
1008  * @cfg {String} offtext text for off slidy toggle state
1009  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1010  * @cfg {Boolean} removeClass remove the standard class..
1011  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1012  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1013  * 
1014  * @constructor
1015  * Create a new button
1016  * @param {Object} config The config object
1017  */
1018
1019
1020 Roo.bootstrap.Button = function(config){
1021     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1022     
1023     this.addEvents({
1024         // raw events
1025         /**
1026          * @event click
1027          * When a button is pressed
1028          * @param {Roo.bootstrap.Button} btn
1029          * @param {Roo.EventObject} e
1030          */
1031         "click" : true,
1032         /**
1033          * @event dblclick
1034          * When a button is double clicked
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "dblclick" : true,
1039          /**
1040          * @event toggle
1041          * After the button has been toggles
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          * @param {boolean} pressed (also available as button.pressed)
1045          */
1046         "toggle" : true
1047     });
1048 };
1049
1050 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1051     html: false,
1052     active: false,
1053     weight: '',
1054     badge_weight: '',
1055     outline : false,
1056     size: '',
1057     tag: 'button',
1058     href: '',
1059     disabled: false,
1060     isClose: false,
1061     glyphicon: '',
1062     fa: '',
1063     badge: '',
1064     theme: 'default',
1065     inverse: false,
1066     
1067     toggle: false,
1068     ontext: 'ON',
1069     offtext: 'OFF',
1070     defaulton: true,
1071     preventDefault: true,
1072     removeClass: false,
1073     name: false,
1074     target: false,
1075     group : false,
1076      
1077     pressed : null,
1078      
1079     
1080     getAutoCreate : function(){
1081         
1082         var cfg = {
1083             tag : 'button',
1084             cls : 'roo-button',
1085             html: ''
1086         };
1087         
1088         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1089             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1090             this.tag = 'button';
1091         } else {
1092             cfg.tag = this.tag;
1093         }
1094         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1095         
1096         if (this.toggle == true) {
1097             cfg={
1098                 tag: 'div',
1099                 cls: 'slider-frame roo-button',
1100                 cn: [
1101                     {
1102                         tag: 'span',
1103                         'data-on-text':'ON',
1104                         'data-off-text':'OFF',
1105                         cls: 'slider-button',
1106                         html: this.offtext
1107                     }
1108                 ]
1109             };
1110             // why are we validating the weights?
1111             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1112                 cfg.cls +=  ' ' + this.weight;
1113             }
1114             
1115             return cfg;
1116         }
1117         
1118         if (this.isClose) {
1119             cfg.cls += ' close';
1120             
1121             cfg["aria-hidden"] = true;
1122             
1123             cfg.html = "&times;";
1124             
1125             return cfg;
1126         }
1127              
1128         
1129         if (this.theme==='default') {
1130             cfg.cls = 'btn roo-button';
1131             
1132             //if (this.parentType != 'Navbar') {
1133             this.weight = this.weight.length ?  this.weight : 'default';
1134             //}
1135             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1136                 
1137                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1138                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1139                 cfg.cls += ' btn-' + outline + weight;
1140                 if (this.weight == 'default') {
1141                     // BC
1142                     cfg.cls += ' btn-' + this.weight;
1143                 }
1144             }
1145         } else if (this.theme==='glow') {
1146             
1147             cfg.tag = 'a';
1148             cfg.cls = 'btn-glow roo-button';
1149             
1150             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1151                 
1152                 cfg.cls += ' ' + this.weight;
1153             }
1154         }
1155    
1156         
1157         if (this.inverse) {
1158             this.cls += ' inverse';
1159         }
1160         
1161         
1162         if (this.active || this.pressed === true) {
1163             cfg.cls += ' active';
1164         }
1165         
1166         if (this.disabled) {
1167             cfg.disabled = 'disabled';
1168         }
1169         
1170         if (this.items) {
1171             Roo.log('changing to ul' );
1172             cfg.tag = 'ul';
1173             this.glyphicon = 'caret';
1174             if (Roo.bootstrap.version == 4) {
1175                 this.fa = 'caret-down';
1176             }
1177             
1178         }
1179         
1180         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1181          
1182         //gsRoo.log(this.parentType);
1183         if (this.parentType === 'Navbar' && !this.parent().bar) {
1184             Roo.log('changing to li?');
1185             
1186             cfg.tag = 'li';
1187             
1188             cfg.cls = '';
1189             cfg.cn =  [{
1190                 tag : 'a',
1191                 cls : 'roo-button',
1192                 html : this.html,
1193                 href : this.href || '#'
1194             }];
1195             if (this.menu) {
1196                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1197                 cfg.cls += ' dropdown';
1198             }   
1199             
1200             delete cfg.html;
1201             
1202         }
1203         
1204        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1205         
1206         if (this.glyphicon) {
1207             cfg.html = ' ' + cfg.html;
1208             
1209             cfg.cn = [
1210                 {
1211                     tag: 'span',
1212                     cls: 'glyphicon glyphicon-' + this.glyphicon
1213                 }
1214             ];
1215         }
1216         if (this.fa) {
1217             cfg.html = ' ' + cfg.html;
1218             
1219             cfg.cn = [
1220                 {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa
1223                 }
1224             ];
1225         }
1226         
1227         if (this.badge) {
1228             cfg.html += ' ';
1229             
1230             cfg.tag = 'a';
1231             
1232 //            cfg.cls='btn roo-button';
1233             
1234             cfg.href=this.href;
1235             
1236             var value = cfg.html;
1237             
1238             if(this.glyphicon){
1239                 value = {
1240                     tag: 'span',
1241                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1242                     html: this.html
1243                 };
1244             }
1245             if(this.fa){
1246                 value = {
1247                     tag: 'i',
1248                     cls: 'fa fas fa-' + this.fa,
1249                     html: this.html
1250                 };
1251             }
1252             
1253             var bw = this.badge_weight.length ? this.badge_weight :
1254                 (this.weight.length ? this.weight : 'secondary');
1255             bw = bw == 'default' ? 'secondary' : bw;
1256             
1257             cfg.cn = [
1258                 value,
1259                 {
1260                     tag: 'span',
1261                     cls: 'badge badge-' + bw,
1262                     html: this.badge
1263                 }
1264             ];
1265             
1266             cfg.html='';
1267         }
1268         
1269         if (this.menu) {
1270             cfg.cls += ' dropdown';
1271             cfg.html = typeof(cfg.html) != 'undefined' ?
1272                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1273         }
1274         
1275         if (cfg.tag !== 'a' && this.href !== '') {
1276             throw "Tag must be a to set href.";
1277         } else if (this.href.length > 0) {
1278             cfg.href = this.href;
1279         }
1280         
1281         if(this.removeClass){
1282             cfg.cls = '';
1283         }
1284         
1285         if(this.target){
1286             cfg.target = this.target;
1287         }
1288         
1289         return cfg;
1290     },
1291     initEvents: function() {
1292        // Roo.log('init events?');
1293 //        Roo.log(this.el.dom);
1294         // add the menu...
1295         
1296         if (typeof (this.menu) != 'undefined') {
1297             this.menu.parentType = this.xtype;
1298             this.menu.triggerEl = this.el;
1299             this.addxtype(Roo.apply({}, this.menu));
1300         }
1301
1302
1303         if (this.el.hasClass('roo-button')) {
1304              this.el.on('click', this.onClick, this);
1305              this.el.on('dblclick', this.onDblClick, this);
1306         } else {
1307              this.el.select('.roo-button').on('click', this.onClick, this);
1308              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1309              
1310         }
1311         // why?
1312         if(this.removeClass){
1313             this.el.on('click', this.onClick, this);
1314         }
1315         
1316         if (this.group === true) {
1317              if (this.pressed === false || this.pressed === true) {
1318                 // nothing
1319             } else {
1320                 this.pressed = false;
1321                 this.setActive(this.pressed);
1322             }
1323             
1324         }
1325         
1326         this.el.enableDisplayMode();
1327         
1328     },
1329     onClick : function(e)
1330     {
1331         if (this.disabled) {
1332             return;
1333         }
1334         
1335         Roo.log('button on click ');
1336         if(this.preventDefault){
1337             e.preventDefault();
1338         }
1339         
1340         if (this.group) {
1341             if (this.pressed) {
1342                 // do nothing -
1343                 return;
1344             }
1345             this.setActive(true);
1346             var pi = this.parent().items;
1347             for (var i = 0;i < pi.length;i++) {
1348                 if (this == pi[i]) {
1349                     continue;
1350                 }
1351                 if (pi[i].el.hasClass('roo-button')) {
1352                     pi[i].setActive(false);
1353                 }
1354             }
1355             this.fireEvent('click', this, e);            
1356             return;
1357         }
1358         
1359         if (this.pressed === true || this.pressed === false) {
1360             this.toggleActive(e);
1361         }
1362         
1363         
1364         this.fireEvent('click', this, e);
1365     },
1366     onDblClick: function(e)
1367     {
1368         if (this.disabled) {
1369             return;
1370         }
1371         if(this.preventDefault){
1372             e.preventDefault();
1373         }
1374         this.fireEvent('dblclick', this, e);
1375     },
1376     /**
1377      * Enables this button
1378      */
1379     enable : function()
1380     {
1381         this.disabled = false;
1382         this.el.removeClass('disabled');
1383         this.el.dom.removeAttribute("disabled");
1384     },
1385     
1386     /**
1387      * Disable this button
1388      */
1389     disable : function()
1390     {
1391         this.disabled = true;
1392         this.el.addClass('disabled');
1393         this.el.attr("disabled", "disabled")
1394     },
1395      /**
1396      * sets the active state on/off, 
1397      * @param {Boolean} state (optional) Force a particular state
1398      */
1399     setActive : function(v) {
1400         
1401         this.el[v ? 'addClass' : 'removeClass']('active');
1402         this.pressed = v;
1403     },
1404      /**
1405      * toggles the current active state 
1406      */
1407     toggleActive : function(e)
1408     {
1409         this.setActive(!this.pressed); // this modifies pressed...
1410         this.fireEvent('toggle', this, e, this.pressed);
1411     },
1412      /**
1413      * get the current active state
1414      * @return {boolean} true if it's active
1415      */
1416     isActive : function()
1417     {
1418         return this.el.hasClass('active');
1419     },
1420     /**
1421      * set the text of the first selected button
1422      */
1423     setText : function(str)
1424     {
1425         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1426     },
1427     /**
1428      * get the text of the first selected button
1429      */
1430     getText : function()
1431     {
1432         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1433     },
1434     
1435     setWeight : function(str)
1436     {
1437         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1438         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1439         this.weight = str;
1440         var outline = this.outline ? 'outline-' : '';
1441         if (str == 'default') {
1442             this.el.addClass('btn-default btn-outline-secondary');        
1443             return;
1444         }
1445         this.el.addClass('btn-' + outline + str);        
1446     }
1447     
1448     
1449 });
1450 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1451
1452 Roo.bootstrap.Button.weights = [
1453     'default',
1454     'secondary' ,
1455     'primary',
1456     'success',
1457     'info',
1458     'warning',
1459     'danger',
1460     'link',
1461     'light',
1462     'dark'              
1463    
1464 ];/*
1465  * - LGPL
1466  *
1467  * column
1468  * 
1469  */
1470
1471 /**
1472  * @class Roo.bootstrap.Column
1473  * @extends Roo.bootstrap.Component
1474  * @children Roo.bootstrap.Component
1475  * Bootstrap Column class
1476  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1477  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1478  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1479  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1480  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1481  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1482  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1483  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1484  *
1485  * 
1486  * @cfg {Boolean} hidden (true|false) hide the element
1487  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1488  * @cfg {String} fa (ban|check|...) font awesome icon
1489  * @cfg {Number} fasize (1|2|....) font awsome size
1490
1491  * @cfg {String} icon (info-sign|check|...) glyphicon name
1492
1493  * @cfg {String} html content of column.
1494  * 
1495  * @constructor
1496  * Create a new Column
1497  * @param {Object} config The config object
1498  */
1499
1500 Roo.bootstrap.Column = function(config){
1501     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1502 };
1503
1504 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1505     
1506     xs: false,
1507     sm: false,
1508     md: false,
1509     lg: false,
1510     xsoff: false,
1511     smoff: false,
1512     mdoff: false,
1513     lgoff: false,
1514     html: '',
1515     offset: 0,
1516     alert: false,
1517     fa: false,
1518     icon : false,
1519     hidden : false,
1520     fasize : 1,
1521     
1522     getAutoCreate : function(){
1523         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524         
1525         cfg = {
1526             tag: 'div',
1527             cls: 'column'
1528         };
1529         
1530         var settings=this;
1531         var sizes =   ['xs','sm','md','lg'];
1532         sizes.map(function(size ,ix){
1533             //Roo.log( size + ':' + settings[size]);
1534             
1535             if (settings[size+'off'] !== false) {
1536                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1537             }
1538             
1539             if (settings[size] === false) {
1540                 return;
1541             }
1542             
1543             if (!settings[size]) { // 0 = hidden
1544                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1545                 // bootsrap4
1546                 for (var i = ix; i > -1; i--) {
1547                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1548                 }
1549                 
1550                 
1551                 return;
1552             }
1553             cfg.cls += ' col-' + size + '-' + settings[size] + (
1554                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1555             );
1556             
1557         });
1558         
1559         if (this.hidden) {
1560             cfg.cls += ' hidden';
1561         }
1562         
1563         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1564             cfg.cls +=' alert alert-' + this.alert;
1565         }
1566         
1567         
1568         if (this.html.length) {
1569             cfg.html = this.html;
1570         }
1571         if (this.fa) {
1572             var fasize = '';
1573             if (this.fasize > 1) {
1574                 fasize = ' fa-' + this.fasize + 'x';
1575             }
1576             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1577             
1578             
1579         }
1580         if (this.icon) {
1581             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1582         }
1583         
1584         return cfg;
1585     }
1586    
1587 });
1588
1589  
1590
1591  /*
1592  * - LGPL
1593  *
1594  * page container.
1595  * 
1596  */
1597
1598
1599 /**
1600  * @class Roo.bootstrap.Container
1601  * @extends Roo.bootstrap.Component
1602  * @builder-top
1603  * @children Roo.bootstrap.Component
1604  * Bootstrap Container class
1605  * @cfg {Boolean} jumbotron is it a jumbotron element
1606  * @cfg {String} html content of element
1607  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1608  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1609  * @cfg {String} header content of header (for panel)
1610  * @cfg {String} footer content of footer (for panel)
1611  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1612  * @cfg {String} tag (header|aside|section) type of HTML tag.
1613  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1614  * @cfg {String} fa font awesome icon
1615  * @cfg {String} icon (info-sign|check|...) glyphicon name
1616  * @cfg {Boolean} hidden (true|false) hide the element
1617  * @cfg {Boolean} expandable (true|false) default false
1618  * @cfg {Boolean} expanded (true|false) default true
1619  * @cfg {String} rheader contet on the right of header
1620  * @cfg {Boolean} clickable (true|false) default false
1621
1622  *     
1623  * @constructor
1624  * Create a new Container
1625  * @param {Object} config The config object
1626  */
1627
1628 Roo.bootstrap.Container = function(config){
1629     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1630     
1631     this.addEvents({
1632         // raw events
1633          /**
1634          * @event expand
1635          * After the panel has been expand
1636          * 
1637          * @param {Roo.bootstrap.Container} this
1638          */
1639         "expand" : true,
1640         /**
1641          * @event collapse
1642          * After the panel has been collapsed
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "collapse" : true,
1647         /**
1648          * @event click
1649          * When a element is chick
1650          * @param {Roo.bootstrap.Container} this
1651          * @param {Roo.EventObject} e
1652          */
1653         "click" : true
1654     });
1655 };
1656
1657 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1658     
1659     jumbotron : false,
1660     well: '',
1661     panel : '',
1662     header: '',
1663     footer : '',
1664     sticky: '',
1665     tag : false,
1666     alert : false,
1667     fa: false,
1668     icon : false,
1669     expandable : false,
1670     rheader : '',
1671     expanded : true,
1672     clickable: false,
1673   
1674      
1675     getChildContainer : function() {
1676         
1677         if(!this.el){
1678             return false;
1679         }
1680         
1681         if (this.panel.length) {
1682             return this.el.select('.panel-body',true).first();
1683         }
1684         
1685         return this.el;
1686     },
1687     
1688     
1689     getAutoCreate : function(){
1690         
1691         var cfg = {
1692             tag : this.tag || 'div',
1693             html : '',
1694             cls : ''
1695         };
1696         if (this.jumbotron) {
1697             cfg.cls = 'jumbotron';
1698         }
1699         
1700         
1701         
1702         // - this is applied by the parent..
1703         //if (this.cls) {
1704         //    cfg.cls = this.cls + '';
1705         //}
1706         
1707         if (this.sticky.length) {
1708             
1709             var bd = Roo.get(document.body);
1710             if (!bd.hasClass('bootstrap-sticky')) {
1711                 bd.addClass('bootstrap-sticky');
1712                 Roo.select('html',true).setStyle('height', '100%');
1713             }
1714              
1715             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1716         }
1717         
1718         
1719         if (this.well.length) {
1720             switch (this.well) {
1721                 case 'lg':
1722                 case 'sm':
1723                     cfg.cls +=' well well-' +this.well;
1724                     break;
1725                 default:
1726                     cfg.cls +=' well';
1727                     break;
1728             }
1729         }
1730         
1731         if (this.hidden) {
1732             cfg.cls += ' hidden';
1733         }
1734         
1735         
1736         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1737             cfg.cls +=' alert alert-' + this.alert;
1738         }
1739         
1740         var body = cfg;
1741         
1742         if (this.panel.length) {
1743             cfg.cls += ' panel panel-' + this.panel;
1744             cfg.cn = [];
1745             if (this.header.length) {
1746                 
1747                 var h = [];
1748                 
1749                 if(this.expandable){
1750                     
1751                     cfg.cls = cfg.cls + ' expandable';
1752                     
1753                     h.push({
1754                         tag: 'i',
1755                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1756                     });
1757                     
1758                 }
1759                 
1760                 h.push(
1761                     {
1762                         tag: 'span',
1763                         cls : 'panel-title',
1764                         html : (this.expandable ? '&nbsp;' : '') + this.header
1765                     },
1766                     {
1767                         tag: 'span',
1768                         cls: 'panel-header-right',
1769                         html: this.rheader
1770                     }
1771                 );
1772                 
1773                 cfg.cn.push({
1774                     cls : 'panel-heading',
1775                     style : this.expandable ? 'cursor: pointer' : '',
1776                     cn : h
1777                 });
1778                 
1779             }
1780             
1781             body = false;
1782             cfg.cn.push({
1783                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1784                 html : this.html
1785             });
1786             
1787             
1788             if (this.footer.length) {
1789                 cfg.cn.push({
1790                     cls : 'panel-footer',
1791                     html : this.footer
1792                     
1793                 });
1794             }
1795             
1796         }
1797         
1798         if (body) {
1799             body.html = this.html || cfg.html;
1800             // prefix with the icons..
1801             if (this.fa) {
1802                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1803             }
1804             if (this.icon) {
1805                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1806             }
1807             
1808             
1809         }
1810         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1811             cfg.cls =  'container';
1812         }
1813         
1814         return cfg;
1815     },
1816     
1817     initEvents: function() 
1818     {
1819         if(this.expandable){
1820             var headerEl = this.headerEl();
1821         
1822             if(headerEl){
1823                 headerEl.on('click', this.onToggleClick, this);
1824             }
1825         }
1826         
1827         if(this.clickable){
1828             this.el.on('click', this.onClick, this);
1829         }
1830         
1831     },
1832     
1833     onToggleClick : function()
1834     {
1835         var headerEl = this.headerEl();
1836         
1837         if(!headerEl){
1838             return;
1839         }
1840         
1841         if(this.expanded){
1842             this.collapse();
1843             return;
1844         }
1845         
1846         this.expand();
1847     },
1848     
1849     expand : function()
1850     {
1851         if(this.fireEvent('expand', this)) {
1852             
1853             this.expanded = true;
1854             
1855             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1856             
1857             this.el.select('.panel-body',true).first().removeClass('hide');
1858             
1859             var toggleEl = this.toggleEl();
1860
1861             if(!toggleEl){
1862                 return;
1863             }
1864
1865             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1866         }
1867         
1868     },
1869     
1870     collapse : function()
1871     {
1872         if(this.fireEvent('collapse', this)) {
1873             
1874             this.expanded = false;
1875             
1876             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1877             this.el.select('.panel-body',true).first().addClass('hide');
1878         
1879             var toggleEl = this.toggleEl();
1880
1881             if(!toggleEl){
1882                 return;
1883             }
1884
1885             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1886         }
1887     },
1888     
1889     toggleEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading .fa',true).first();
1896     },
1897     
1898     headerEl : function()
1899     {
1900         if(!this.el || !this.panel.length || !this.header.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-heading',true).first()
1905     },
1906     
1907     bodyEl : function()
1908     {
1909         if(!this.el || !this.panel.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-body',true).first()
1914     },
1915     
1916     titleEl : function()
1917     {
1918         if(!this.el || !this.panel.length || !this.header.length){
1919             return;
1920         }
1921         
1922         return this.el.select('.panel-title',true).first();
1923     },
1924     
1925     setTitle : function(v)
1926     {
1927         var titleEl = this.titleEl();
1928         
1929         if(!titleEl){
1930             return;
1931         }
1932         
1933         titleEl.dom.innerHTML = v;
1934     },
1935     
1936     getTitle : function()
1937     {
1938         
1939         var titleEl = this.titleEl();
1940         
1941         if(!titleEl){
1942             return '';
1943         }
1944         
1945         return titleEl.dom.innerHTML;
1946     },
1947     
1948     setRightTitle : function(v)
1949     {
1950         var t = this.el.select('.panel-header-right',true).first();
1951         
1952         if(!t){
1953             return;
1954         }
1955         
1956         t.dom.innerHTML = v;
1957     },
1958     
1959     onClick : function(e)
1960     {
1961         e.preventDefault();
1962         
1963         this.fireEvent('click', this, e);
1964     }
1965 });
1966
1967  /*
1968  *  - LGPL
1969  *
1970  *  This is BS4's Card element.. - similar to our containers probably..
1971  * 
1972  */
1973 /**
1974  * @class Roo.bootstrap.Card
1975  * @extends Roo.bootstrap.Component
1976  * @children Roo.bootstrap.Component
1977  * Bootstrap Card class
1978  *
1979  *
1980  * possible... may not be implemented..
1981  * @cfg {String} header_image  src url of image.
1982  * @cfg {String|Object} header
1983  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1985  * 
1986  * @cfg {String} title
1987  * @cfg {String} subtitle
1988  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989  * @cfg {String} footer
1990  
1991  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1992  * 
1993  * @cfg {String} margin (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2000  *
2001  * @cfg {String} padding (0|1|2|3|4|5)
2002  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004  * @cfg {String} padding_left (0|1|2|3|4|5)
2005  * @cfg {String} padding_right (0|1|2|3|4|5)
2006  * @cfg {String} padding_x (0|1|2|3|4|5)
2007  * @cfg {String} padding_y (0|1|2|3|4|5)
2008  *
2009  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  
2015  * @config {Boolean} dragable  if this card can be dragged.
2016  * @config {String} drag_group  group for drag
2017  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2018  * @config {String} drop_group  group for drag
2019  * 
2020  * @config {Boolean} collapsable can the body be collapsed.
2021  * @config {Boolean} collapsed is the body collapsed when rendered...
2022  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023  * @config {Boolean} rotated is the body rotated when rendered...
2024  * 
2025  * @constructor
2026  * Create a new Container
2027  * @param {Object} config The config object
2028  */
2029
2030 Roo.bootstrap.Card = function(config){
2031     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2032     
2033     this.addEvents({
2034          // raw events
2035         /**
2036          * @event drop
2037          * When a element a card is dropped
2038          * @param {Roo.bootstrap.Card} this
2039          *
2040          * 
2041          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042          * @param {String} position 'above' or 'below'
2043          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2044         
2045          */
2046         'drop' : true,
2047          /**
2048          * @event rotate
2049          * When a element a card is rotate
2050          * @param {Roo.bootstrap.Card} this
2051          * @param {Roo.Element} n the node being dropped?
2052          * @param {Boolean} rotate status
2053          */
2054         'rotate' : true,
2055         /**
2056          * @event cardover
2057          * When a card element is dragged over ready to drop (return false to block dropable)
2058          * @param {Roo.bootstrap.Card} this
2059          * @param {Object} data from dragdrop 
2060          */
2061          'cardover' : true
2062          
2063     });
2064 };
2065
2066
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2068     
2069     
2070     weight : '',
2071     
2072     margin: '', /// may be better in component?
2073     margin_top: '', 
2074     margin_bottom: '', 
2075     margin_left: '',
2076     margin_right: '',
2077     margin_x: '',
2078     margin_y: '',
2079     
2080     padding : '',
2081     padding_top: '', 
2082     padding_bottom: '', 
2083     padding_left: '',
2084     padding_right: '',
2085     padding_x: '',
2086     padding_y: '',
2087     
2088     display: '', 
2089     display_xs: '', 
2090     display_sm: '', 
2091     display_lg: '',
2092     display_xl: '',
2093  
2094     header_image  : '',
2095     header : '',
2096     header_size : 0,
2097     title : '',
2098     subtitle : '',
2099     html : '',
2100     footer: '',
2101
2102     collapsable : false,
2103     collapsed : false,
2104     rotateable : false,
2105     rotated : false,
2106     
2107     dragable : false,
2108     drag_group : false,
2109     dropable : false,
2110     drop_group : false,
2111     childContainer : false,
2112     dropEl : false, /// the dom placeholde element that indicates drop location.
2113     containerEl: false, // body container
2114     bodyEl: false, // card-body
2115     headerContainerEl : false, //
2116     headerEl : false,
2117     header_imageEl : false,
2118     
2119     
2120     layoutCls : function()
2121     {
2122         var cls = '';
2123         var t = this;
2124         Roo.log(this.margin_bottom.length);
2125         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2127             
2128             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2130             }
2131             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2133             }
2134         });
2135         
2136         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2139             }
2140         });
2141         
2142         // more generic support?
2143         if (this.hidden) {
2144             cls += ' d-none';
2145         }
2146         
2147         return cls;
2148     },
2149  
2150        // Roo.log("Call onRender: " + this.xtype);
2151         /*  We are looking at something like this.
2152 <div class="card">
2153     <img src="..." class="card-img-top" alt="...">
2154     <div class="card-body">
2155         <h5 class="card-title">Card title</h5>
2156          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2157
2158         >> this bit is really the body...
2159         <div> << we will ad dthis in hopefully it will not break shit.
2160         
2161         ** card text does not actually have any styling...
2162         
2163             <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>
2164         
2165         </div> <<
2166           <a href="#" class="card-link">Card link</a>
2167           
2168     </div>
2169     <div class="card-footer">
2170         <small class="text-muted">Last updated 3 mins ago</small>
2171     </div>
2172 </div>
2173          */
2174     getAutoCreate : function(){
2175         
2176         var cfg = {
2177             tag : 'div',
2178             cls : 'card',
2179             cn : [ ]
2180         };
2181         
2182         if (this.weight.length && this.weight != 'light') {
2183             cfg.cls += ' text-white';
2184         } else {
2185             cfg.cls += ' text-dark'; // need as it's nested..
2186         }
2187         if (this.weight.length) {
2188             cfg.cls += ' bg-' + this.weight;
2189         }
2190         
2191         cfg.cls += ' ' + this.layoutCls(); 
2192         
2193         var hdr = false;
2194         var hdr_ctr = false;
2195         if (this.header.length) {
2196             hdr = {
2197                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199                 cn : []
2200             };
2201             cfg.cn.push(hdr);
2202             hdr_ctr = hdr;
2203         } else {
2204             hdr = {
2205                 tag : 'div',
2206                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2207                 cn : []
2208             };
2209             cfg.cn.push(hdr);
2210             hdr_ctr = hdr;
2211         }
2212         if (this.collapsable) {
2213             hdr_ctr = {
2214             tag : 'a',
2215             cls : 'd-block user-select-none',
2216             cn: [
2217                     {
2218                         tag: 'i',
2219                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2220                     }
2221                    
2222                 ]
2223             };
2224             hdr.cn.push(hdr_ctr);
2225         }
2226         
2227         hdr_ctr.cn.push(        {
2228             tag: 'span',
2229             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2230             html : this.header
2231         });
2232         
2233         
2234         if (this.header_image.length) {
2235             cfg.cn.push({
2236                 tag : 'img',
2237                 cls : 'card-img-top',
2238                 src: this.header_image // escape?
2239             });
2240         } else {
2241             cfg.cn.push({
2242                     tag : 'div',
2243                     cls : 'card-img-top d-none' 
2244                 });
2245         }
2246             
2247         var body = {
2248             tag : 'div',
2249             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2250             cn : []
2251         };
2252         var obody = body;
2253         if (this.collapsable || this.rotateable) {
2254             obody = {
2255                 tag: 'div',
2256                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2257                 cn : [  body ]
2258             };
2259         }
2260         
2261         cfg.cn.push(obody);
2262         
2263         if (this.title.length) {
2264             body.cn.push({
2265                 tag : 'div',
2266                 cls : 'card-title',
2267                 src: this.title // escape?
2268             });
2269         }  
2270         
2271         if (this.subtitle.length) {
2272             body.cn.push({
2273                 tag : 'div',
2274                 cls : 'card-title',
2275                 src: this.subtitle // escape?
2276             });
2277         }
2278         
2279         body.cn.push({
2280             tag : 'div',
2281             cls : 'roo-card-body-ctr'
2282         });
2283         
2284         if (this.html.length) {
2285             body.cn.push({
2286                 tag: 'div',
2287                 html : this.html
2288             });
2289         }
2290         // fixme ? handle objects?
2291         
2292         if (this.footer.length) {
2293            
2294             cfg.cn.push({
2295                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2296                 html : this.footer
2297             });
2298             
2299         } else {
2300             cfg.cn.push({cls : 'card-footer d-none'});
2301         }
2302         
2303         // footer...
2304         
2305         return cfg;
2306     },
2307     
2308     
2309     getCardHeader : function()
2310     {
2311         var  ret = this.el.select('.card-header',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardFooter : function()
2319     {
2320         var  ret = this.el.select('.card-footer',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardImageTop : function()
2328     {
2329         var  ret = this.header_imageEl;
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333             
2334         return ret;
2335     },
2336     
2337     getChildContainer : function()
2338     {
2339         
2340         if(!this.el){
2341             return false;
2342         }
2343         return this.el.select('.roo-card-body-ctr',true).first();    
2344     },
2345     
2346     initEvents: function() 
2347     {
2348         this.bodyEl = this.el.select('.card-body',true).first(); 
2349         this.containerEl = this.getChildContainer();
2350         if(this.dragable){
2351             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352                     containerScroll: true,
2353                     ddGroup: this.drag_group || 'default_card_drag_group'
2354             });
2355             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2356         }
2357         if (this.dropable) {
2358             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359                 containerScroll: true,
2360                 ddGroup: this.drop_group || 'default_card_drag_group'
2361             });
2362             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2367         }
2368         
2369         if (this.collapsable) {
2370             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2371         }
2372         if (this.rotateable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2374         }
2375         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2376          
2377         this.footerEl = this.el.select('.card-footer',true).first();
2378         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380         this.headerEl = this.el.select('.card-header',true).first();
2381         
2382         if (this.rotated) {
2383             this.el.addClass('roo-card-rotated');
2384             this.fireEvent('rotate', this, true);
2385         }
2386         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2387         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2388         
2389     },
2390     getDragData : function(e)
2391     {
2392         var target = this.getEl();
2393         if (target) {
2394             //this.handleSelection(e);
2395             
2396             var dragData = {
2397                 source: this,
2398                 copy: false,
2399                 nodes: this.getEl(),
2400                 records: []
2401             };
2402             
2403             
2404             dragData.ddel = target.dom ;    // the div element
2405             Roo.log(target.getWidth( ));
2406             dragData.ddel.style.width = target.getWidth() + 'px';
2407             
2408             return dragData;
2409         }
2410         return false;
2411     },
2412     /**
2413     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2414     *    whole Element becomes the target, and this causes the drop gesture to append.
2415     *
2416     *    Returns an object:
2417     *     {
2418            
2419            position : 'below' or 'above'
2420            card  : relateive to card OBJECT (or true for no cards listed)
2421            items_n : relative to nth item in list
2422            card_n : relative to  nth card in list
2423     }
2424     *
2425     *    
2426     */
2427     getTargetFromEvent : function(e, dragged_card_el)
2428     {
2429         var target = e.getTarget();
2430         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431             target = target.parentNode;
2432         }
2433         
2434         var ret = {
2435             position: '',
2436             cards : [],
2437             card_n : -1,
2438             items_n : -1,
2439             card : false 
2440         };
2441         
2442         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443         // see if target is one of the 'cards'...
2444         
2445         
2446         //Roo.log(this.items.length);
2447         var pos = false;
2448         
2449         var last_card_n = 0;
2450         var cards_len  = 0;
2451         for (var i = 0;i< this.items.length;i++) {
2452             
2453             if (!this.items[i].el.hasClass('card')) {
2454                  continue;
2455             }
2456             pos = this.getDropPoint(e, this.items[i].el.dom);
2457             
2458             cards_len = ret.cards.length;
2459             //Roo.log(this.items[i].el.dom.id);
2460             ret.cards.push(this.items[i]);
2461             last_card_n  = i;
2462             if (ret.card_n < 0 && pos == 'above') {
2463                 ret.position = cards_len > 0 ? 'below' : pos;
2464                 ret.items_n = i > 0 ? i - 1 : 0;
2465                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2466                 ret.card = ret.cards[ret.card_n];
2467             }
2468         }
2469         if (!ret.cards.length) {
2470             ret.card = true;
2471             ret.position = 'below';
2472             ret.items_n;
2473             return ret;
2474         }
2475         // could not find a card.. stick it at the end..
2476         if (ret.card_n < 0) {
2477             ret.card_n = last_card_n;
2478             ret.card = ret.cards[last_card_n];
2479             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480             ret.position = 'below';
2481         }
2482         
2483         if (this.items[ret.items_n].el == dragged_card_el) {
2484             return false;
2485         }
2486         
2487         if (ret.position == 'below') {
2488             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2489             
2490             if (card_after  && card_after.el == dragged_card_el) {
2491                 return false;
2492             }
2493             return ret;
2494         }
2495         
2496         // its's after ..
2497         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2498         
2499         if (card_before  && card_before.el == dragged_card_el) {
2500             return false;
2501         }
2502         
2503         return ret;
2504     },
2505     
2506     onNodeEnter : function(n, dd, e, data){
2507         return false;
2508     },
2509     onNodeOver : function(n, dd, e, data)
2510     {
2511        
2512         var target_info = this.getTargetFromEvent(e,data.source.el);
2513         if (target_info === false) {
2514             this.dropPlaceHolder('hide');
2515             return false;
2516         }
2517         Roo.log(['getTargetFromEvent', target_info ]);
2518         
2519         
2520         if (this.fireEvent('cardover', this, [ data ]) === false) {
2521             return false;
2522         }
2523         
2524         this.dropPlaceHolder('show', target_info,data);
2525         
2526         return false; 
2527     },
2528     onNodeOut : function(n, dd, e, data){
2529         this.dropPlaceHolder('hide');
2530      
2531     },
2532     onNodeDrop : function(n, dd, e, data)
2533     {
2534         
2535         // call drop - return false if
2536         
2537         // this could actually fail - if the Network drops..
2538         // we will ignore this at present..- client should probably reload
2539         // the whole set of cards if stuff like that fails.
2540         
2541         
2542         var info = this.getTargetFromEvent(e,data.source.el);
2543         if (info === false) {
2544             return false;
2545         }
2546         this.dropPlaceHolder('hide');
2547   
2548           
2549     
2550         this.acceptCard(data.source, info.position, info.card, info.items_n);
2551         return true;
2552          
2553     },
2554     firstChildCard : function()
2555     {
2556         for (var i = 0;i< this.items.length;i++) {
2557             
2558             if (!this.items[i].el.hasClass('card')) {
2559                  continue;
2560             }
2561             return this.items[i];
2562         }
2563         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2564     },
2565     /**
2566      * accept card
2567      *
2568      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2569      */
2570     acceptCard : function(move_card,  position, next_to_card )
2571     {
2572         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2573             return false;
2574         }
2575         
2576         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2577         
2578         move_card.parent().removeCard(move_card);
2579         
2580         
2581         var dom = move_card.el.dom;
2582         dom.style.width = ''; // clear with - which is set by drag.
2583         
2584         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585             var cardel = next_to_card.el.dom;
2586             
2587             if (position == 'above' ) {
2588                 cardel.parentNode.insertBefore(dom, cardel);
2589             } else if (cardel.nextSibling) {
2590                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2591             } else {
2592                 cardel.parentNode.append(dom);
2593             }
2594         } else {
2595             // card container???
2596             this.containerEl.dom.append(dom);
2597         }
2598         
2599         //FIXME HANDLE card = true 
2600         
2601         // add this to the correct place in items.
2602         
2603         // remove Card from items.
2604         
2605        
2606         if (this.items.length) {
2607             var nitems = [];
2608             //Roo.log([info.items_n, info.position, this.items.length]);
2609             for (var i =0; i < this.items.length; i++) {
2610                 if (i == to_items_n && position == 'above') {
2611                     nitems.push(move_card);
2612                 }
2613                 nitems.push(this.items[i]);
2614                 if (i == to_items_n && position == 'below') {
2615                     nitems.push(move_card);
2616                 }
2617             }
2618             this.items = nitems;
2619             Roo.log(this.items);
2620         } else {
2621             this.items.push(move_card);
2622         }
2623         
2624         move_card.parentId = this.id;
2625         
2626         return true;
2627         
2628         
2629     },
2630     removeCard : function(c)
2631     {
2632         this.items = this.items.filter(function(e) { return e != c });
2633  
2634         var dom = c.el.dom;
2635         dom.parentNode.removeChild(dom);
2636         dom.style.width = ''; // clear with - which is set by drag.
2637         c.parentId = false;
2638         
2639     },
2640     
2641     /**    Decide whether to drop above or below a View node. */
2642     getDropPoint : function(e, n, dd)
2643     {
2644         if (dd) {
2645              return false;
2646         }
2647         if (n == this.containerEl.dom) {
2648             return "above";
2649         }
2650         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651         var c = t + (b - t) / 2;
2652         var y = Roo.lib.Event.getPageY(e);
2653         if(y <= c) {
2654             return "above";
2655         }else{
2656             return "below";
2657         }
2658     },
2659     onToggleCollapse : function(e)
2660         {
2661         if (this.collapsed) {
2662             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663             this.collapsableEl.addClass('show');
2664             this.collapsed = false;
2665             return;
2666         }
2667         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668         this.collapsableEl.removeClass('show');
2669         this.collapsed = true;
2670         
2671     
2672     },
2673     
2674     onToggleRotate : function(e)
2675     {
2676         this.collapsableEl.removeClass('show');
2677         this.footerEl.removeClass('d-none');
2678         this.el.removeClass('roo-card-rotated');
2679         this.el.removeClass('d-none');
2680         if (this.rotated) {
2681             
2682             this.collapsableEl.addClass('show');
2683             this.rotated = false;
2684             this.fireEvent('rotate', this, this.rotated);
2685             return;
2686         }
2687         this.el.addClass('roo-card-rotated');
2688         this.footerEl.addClass('d-none');
2689         this.el.select('.roo-collapsable').removeClass('show');
2690         
2691         this.rotated = true;
2692         this.fireEvent('rotate', this, this.rotated);
2693     
2694     },
2695     
2696     dropPlaceHolder: function (action, info, data)
2697     {
2698         if (this.dropEl === false) {
2699             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2700             cls : 'd-none'
2701             },true);
2702         }
2703         this.dropEl.removeClass(['d-none', 'd-block']);        
2704         if (action == 'hide') {
2705             
2706             this.dropEl.addClass('d-none');
2707             return;
2708         }
2709         // FIXME - info.card == true!!!
2710         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2711         
2712         if (info.card !== true) {
2713             var cardel = info.card.el.dom;
2714             
2715             if (info.position == 'above') {
2716                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717             } else if (cardel.nextSibling) {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2719             } else {
2720                 cardel.parentNode.append(this.dropEl.dom);
2721             }
2722         } else {
2723             // card container???
2724             this.containerEl.dom.append(this.dropEl.dom);
2725         }
2726         
2727         this.dropEl.addClass('d-block roo-card-dropzone');
2728         
2729         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2730         
2731         
2732     
2733     
2734     
2735     },
2736     setHeaderText: function(html)
2737     {
2738         this.header = html;
2739         if (this.headerContainerEl) {
2740             this.headerContainerEl.dom.innerHTML = html;
2741         }
2742     },
2743     onHeaderImageLoad : function(ev, he)
2744     {
2745         if (!this.header_image_fit_square) {
2746             return;
2747         }
2748         
2749         var hw = he.naturalHeight / he.naturalWidth;
2750         // wide image = < 0
2751         // tall image = > 1
2752         //var w = he.dom.naturalWidth;
2753         var ww = he.width;
2754         he.style.left =  0;
2755         he.style.position =  'relative';
2756         if (hw > 1) {
2757             var nw = (ww * (1/hw));
2758             Roo.get(he).setSize( ww * (1/hw),  ww);
2759             he.style.left =  ((ww - nw)/ 2) + 'px';
2760             he.style.position =  'relative';
2761         }
2762
2763     }
2764
2765     
2766 });
2767
2768 /*
2769  * - LGPL
2770  *
2771  * Card header - holder for the card header elements.
2772  * 
2773  */
2774
2775 /**
2776  * @class Roo.bootstrap.CardHeader
2777  * @extends Roo.bootstrap.Element
2778  * Bootstrap CardHeader class
2779  * @constructor
2780  * Create a new Card Header - that you can embed children into
2781  * @param {Object} config The config object
2782  */
2783
2784 Roo.bootstrap.CardHeader = function(config){
2785     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2786 };
2787
2788 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2789     
2790     
2791     container_method : 'getCardHeader' 
2792     
2793      
2794     
2795     
2796    
2797 });
2798
2799  
2800
2801  /*
2802  * - LGPL
2803  *
2804  * Card footer - holder for the card footer elements.
2805  * 
2806  */
2807
2808 /**
2809  * @class Roo.bootstrap.CardFooter
2810  * @extends Roo.bootstrap.Element
2811  * Bootstrap CardFooter class
2812  * @constructor
2813  * Create a new Card Footer - that you can embed children into
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.CardFooter = function(config){
2818     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2819 };
2820
2821 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2822     
2823     
2824     container_method : 'getCardFooter' 
2825     
2826      
2827     
2828     
2829    
2830 });
2831
2832  
2833
2834  /*
2835  * - LGPL
2836  *
2837  * Card header - holder for the card header elements.
2838  * 
2839  */
2840
2841 /**
2842  * @class Roo.bootstrap.CardImageTop
2843  * @extends Roo.bootstrap.Element
2844  * Bootstrap CardImageTop class
2845  * @constructor
2846  * Create a new Card Image Top container
2847  * @param {Object} config The config object
2848  */
2849
2850 Roo.bootstrap.CardImageTop = function(config){
2851     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2852 };
2853
2854 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2855     
2856    
2857     container_method : 'getCardImageTop' 
2858     
2859      
2860     
2861    
2862 });
2863
2864  
2865
2866  
2867 /*
2868 * Licence: LGPL
2869 */
2870
2871 /**
2872  * @class Roo.bootstrap.ButtonUploader
2873  * @extends Roo.bootstrap.Button
2874  * Bootstrap Button Uploader class - it's a button which when you add files to it
2875  *
2876  * 
2877  * @cfg {Number} errorTimeout default 3000
2878  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2879  * @cfg {Array}  html The button text.
2880  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2881  *
2882  * @constructor
2883  * Create a new CardUploader
2884  * @param {Object} config The config object
2885  */
2886
2887 Roo.bootstrap.ButtonUploader = function(config){
2888     
2889  
2890     
2891     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2892     
2893      
2894      this.addEvents({
2895          // raw events
2896         /**
2897          * @event beforeselect
2898          * When button is pressed, before show upload files dialog is shown
2899          * @param {Roo.bootstrap.UploaderButton} this
2900          *
2901          */
2902         'beforeselect' : true,
2903          /**
2904          * @event fired when files have been selected, 
2905          * When a the download link is clicked
2906          * @param {Roo.bootstrap.UploaderButton} this
2907          * @param {Array} Array of files that have been uploaded
2908          */
2909         'uploaded' : true
2910         
2911     });
2912 };
2913  
2914 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2915     
2916      
2917     errorTimeout : 3000,
2918      
2919     images : false,
2920    
2921     fileCollection : false,
2922     allowBlank : true,
2923     
2924     multiple : true,
2925     
2926     getAutoCreate : function()
2927     {
2928         var im = {
2929             tag: 'input',
2930             type : 'file',
2931             cls : 'd-none  roo-card-upload-selector' 
2932           
2933         };
2934         if (this.multiple) {
2935             im.multiple = 'multiple';
2936         }
2937         
2938         return  {
2939             cls :'div' ,
2940             cn : [
2941                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2942                 im
2943
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964          
2965          
2966          
2967         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2968         
2969         this.selectorEl.on('change', this.onFileSelected, this);
2970          
2971          
2972        
2973     },
2974     
2975    
2976     onClick : function(e)
2977     {
2978         e.preventDefault();
2979         
2980         if ( this.fireEvent('beforeselect', this) === false) {
2981             return;
2982         }
2983          
2984         this.selectorEl.dom.click();
2985          
2986     },
2987     
2988     onFileSelected : function(e)
2989     {
2990         e.preventDefault();
2991         
2992         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2993             return;
2994         }
2995         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2996         this.selectorEl.dom.value  = '';// hopefully reset..
2997         
2998         this.fireEvent('uploaded', this,  files );
2999         
3000     },
3001     
3002        
3003    
3004     
3005     /**
3006      * addCard - add an Attachment to the uploader
3007      * @param data - the data about the image to upload
3008      *
3009      * {
3010           id : 123
3011           title : "Title of file",
3012           is_uploaded : false,
3013           src : "http://.....",
3014           srcfile : { the File upload object },
3015           mimetype : file.type,
3016           preview : false,
3017           is_deleted : 0
3018           .. any other data...
3019         }
3020      *
3021      * 
3022     */
3023      
3024     reset: function()
3025     {
3026          
3027          this.selectorEl
3028     } 
3029     
3030     
3031     
3032     
3033 });
3034  /*
3035  * - LGPL
3036  *
3037  * image
3038  * 
3039  */
3040
3041
3042 /**
3043  * @class Roo.bootstrap.Img
3044  * @extends Roo.bootstrap.Component
3045  * Bootstrap Img class
3046  * @cfg {Boolean} imgResponsive false | true
3047  * @cfg {String} border rounded | circle | thumbnail
3048  * @cfg {String} src image source
3049  * @cfg {String} alt image alternative text
3050  * @cfg {String} href a tag href
3051  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3052  * @cfg {String} xsUrl xs image source
3053  * @cfg {String} smUrl sm image source
3054  * @cfg {String} mdUrl md image source
3055  * @cfg {String} lgUrl lg image source
3056  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3057  * 
3058  * @constructor
3059  * Create a new Input
3060  * @param {Object} config The config object
3061  */
3062
3063 Roo.bootstrap.Img = function(config){
3064     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3065     
3066     this.addEvents({
3067         // img events
3068         /**
3069          * @event click
3070          * The img click event for the img.
3071          * @param {Roo.EventObject} e
3072          */
3073         "click" : true,
3074         /**
3075          * @event load
3076          * The when any image loads
3077          * @param {Roo.EventObject} e
3078          */
3079         "load" : true
3080     });
3081 };
3082
3083 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3084     
3085     imgResponsive: true,
3086     border: '',
3087     src: 'about:blank',
3088     href: false,
3089     target: false,
3090     xsUrl: '',
3091     smUrl: '',
3092     mdUrl: '',
3093     lgUrl: '',
3094     backgroundContain : false,
3095
3096     getAutoCreate : function()
3097     {   
3098         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3099             return this.createSingleImg();
3100         }
3101         
3102         var cfg = {
3103             tag: 'div',
3104             cls: 'roo-image-responsive-group',
3105             cn: []
3106         };
3107         var _this = this;
3108         
3109         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3110             
3111             if(!_this[size + 'Url']){
3112                 return;
3113             }
3114             
3115             var img = {
3116                 tag: 'img',
3117                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3118                 html: _this.html || cfg.html,
3119                 src: _this[size + 'Url']
3120             };
3121             
3122             img.cls += ' roo-image-responsive-' + size;
3123             
3124             var s = ['xs', 'sm', 'md', 'lg'];
3125             
3126             s.splice(s.indexOf(size), 1);
3127             
3128             Roo.each(s, function(ss){
3129                 img.cls += ' hidden-' + ss;
3130             });
3131             
3132             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3133                 cfg.cls += ' img-' + _this.border;
3134             }
3135             
3136             if(_this.alt){
3137                 cfg.alt = _this.alt;
3138             }
3139             
3140             if(_this.href){
3141                 var a = {
3142                     tag: 'a',
3143                     href: _this.href,
3144                     cn: [
3145                         img
3146                     ]
3147                 };
3148
3149                 if(this.target){
3150                     a.target = _this.target;
3151                 }
3152             }
3153             
3154             cfg.cn.push((_this.href) ? a : img);
3155             
3156         });
3157         
3158         return cfg;
3159     },
3160     
3161     createSingleImg : function()
3162     {
3163         var cfg = {
3164             tag: 'img',
3165             cls: (this.imgResponsive) ? 'img-responsive' : '',
3166             html : null,
3167             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3168         };
3169         
3170         if (this.backgroundContain) {
3171             cfg.cls += ' background-contain';
3172         }
3173         
3174         cfg.html = this.html || cfg.html;
3175         
3176         if (this.backgroundContain) {
3177             cfg.style="background-image: url(" + this.src + ')';
3178         } else {
3179             cfg.src = this.src || cfg.src;
3180         }
3181         
3182         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3183             cfg.cls += ' img-' + this.border;
3184         }
3185         
3186         if(this.alt){
3187             cfg.alt = this.alt;
3188         }
3189         
3190         if(this.href){
3191             var a = {
3192                 tag: 'a',
3193                 href: this.href,
3194                 cn: [
3195                     cfg
3196                 ]
3197             };
3198             
3199             if(this.target){
3200                 a.target = this.target;
3201             }
3202             
3203         }
3204         
3205         return (this.href) ? a : cfg;
3206     },
3207     
3208     initEvents: function() 
3209     {
3210         if(!this.href){
3211             this.el.on('click', this.onClick, this);
3212         }
3213         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3214             this.el.on('load', this.onImageLoad, this);
3215         } else {
3216             // not sure if this works.. not tested
3217             this.el.select('img', true).on('load', this.onImageLoad, this);
3218         }
3219         
3220     },
3221     
3222     onClick : function(e)
3223     {
3224         Roo.log('img onclick');
3225         this.fireEvent('click', this, e);
3226     },
3227     onImageLoad: function(e)
3228     {
3229         Roo.log('img load');
3230         this.fireEvent('load', this, e);
3231     },
3232     
3233     /**
3234      * Sets the url of the image - used to update it
3235      * @param {String} url the url of the image
3236      */
3237     
3238     setSrc : function(url)
3239     {
3240         this.src =  url;
3241         
3242         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3243             if (this.backgroundContain) {
3244                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3245             } else {
3246                 this.el.dom.src =  url;
3247             }
3248             return;
3249         }
3250         
3251         this.el.select('img', true).first().dom.src =  url;
3252     }
3253     
3254     
3255    
3256 });
3257
3258  /*
3259  * - LGPL
3260  *
3261  * image
3262  * 
3263  */
3264
3265
3266 /**
3267  * @class Roo.bootstrap.Link
3268  * @extends Roo.bootstrap.Component
3269  * Bootstrap Link Class
3270  * @cfg {String} alt image alternative text
3271  * @cfg {String} href a tag href
3272  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3273  * @cfg {String} html the content of the link.
3274  * @cfg {String} anchor name for the anchor link
3275  * @cfg {String} fa - favicon
3276
3277  * @cfg {Boolean} preventDefault (true | false) default false
3278
3279  * 
3280  * @constructor
3281  * Create a new Input
3282  * @param {Object} config The config object
3283  */
3284
3285 Roo.bootstrap.Link = function(config){
3286     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3287     
3288     this.addEvents({
3289         // img events
3290         /**
3291          * @event click
3292          * The img click event for the img.
3293          * @param {Roo.EventObject} e
3294          */
3295         "click" : true
3296     });
3297 };
3298
3299 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3300     
3301     href: false,
3302     target: false,
3303     preventDefault: false,
3304     anchor : false,
3305     alt : false,
3306     fa: false,
3307
3308
3309     getAutoCreate : function()
3310     {
3311         var html = this.html || '';
3312         
3313         if (this.fa !== false) {
3314             html = '<i class="fa fa-' + this.fa + '"></i>';
3315         }
3316         var cfg = {
3317             tag: 'a'
3318         };
3319         // anchor's do not require html/href...
3320         if (this.anchor === false) {
3321             cfg.html = html;
3322             cfg.href = this.href || '#';
3323         } else {
3324             cfg.name = this.anchor;
3325             if (this.html !== false || this.fa !== false) {
3326                 cfg.html = html;
3327             }
3328             if (this.href !== false) {
3329                 cfg.href = this.href;
3330             }
3331         }
3332         
3333         if(this.alt !== false){
3334             cfg.alt = this.alt;
3335         }
3336         
3337         
3338         if(this.target !== false) {
3339             cfg.target = this.target;
3340         }
3341         
3342         return cfg;
3343     },
3344     
3345     initEvents: function() {
3346         
3347         if(!this.href || this.preventDefault){
3348             this.el.on('click', this.onClick, this);
3349         }
3350     },
3351     
3352     onClick : function(e)
3353     {
3354         if(this.preventDefault){
3355             e.preventDefault();
3356         }
3357         //Roo.log('img onclick');
3358         this.fireEvent('click', this, e);
3359     }
3360    
3361 });
3362
3363  /*
3364  * - LGPL
3365  *
3366  * header
3367  * 
3368  */
3369
3370 /**
3371  * @class Roo.bootstrap.Header
3372  * @extends Roo.bootstrap.Component
3373  * Bootstrap Header class
3374  * @cfg {String} html content of header
3375  * @cfg {Number} level (1|2|3|4|5|6) default 1
3376  * 
3377  * @constructor
3378  * Create a new Header
3379  * @param {Object} config The config object
3380  */
3381
3382
3383 Roo.bootstrap.Header  = function(config){
3384     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3385 };
3386
3387 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3388     
3389     //href : false,
3390     html : false,
3391     level : 1,
3392     
3393     
3394     
3395     getAutoCreate : function(){
3396         
3397         
3398         
3399         var cfg = {
3400             tag: 'h' + (1 *this.level),
3401             html: this.html || ''
3402         } ;
3403         
3404         return cfg;
3405     }
3406    
3407 });
3408
3409  
3410
3411  /*
3412  * Based on:
3413  * Ext JS Library 1.1.1
3414  * Copyright(c) 2006-2007, Ext JS, LLC.
3415  *
3416  * Originally Released Under LGPL - original licence link has changed is not relivant.
3417  *
3418  * Fork - LGPL
3419  * <script type="text/javascript">
3420  */
3421  
3422 /**
3423  * @class Roo.bootstrap.MenuMgr
3424  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3425  * @singleton
3426  */
3427 Roo.bootstrap.MenuMgr = function(){
3428    var menus, active, groups = {}, attached = false, lastShow = new Date();
3429
3430    // private - called when first menu is created
3431    function init(){
3432        menus = {};
3433        active = new Roo.util.MixedCollection();
3434        Roo.get(document).addKeyListener(27, function(){
3435            if(active.length > 0){
3436                hideAll();
3437            }
3438        });
3439    }
3440
3441    // private
3442    function hideAll(){
3443        if(active && active.length > 0){
3444            var c = active.clone();
3445            c.each(function(m){
3446                m.hide();
3447            });
3448        }
3449    }
3450
3451    // private
3452    function onHide(m){
3453        active.remove(m);
3454        if(active.length < 1){
3455            Roo.get(document).un("mouseup", onMouseDown);
3456             
3457            attached = false;
3458        }
3459    }
3460
3461    // private
3462    function onShow(m){
3463        var last = active.last();
3464        lastShow = new Date();
3465        active.add(m);
3466        if(!attached){
3467           Roo.get(document).on("mouseup", onMouseDown);
3468            
3469            attached = true;
3470        }
3471        if(m.parentMenu){
3472           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3473           m.parentMenu.activeChild = m;
3474        }else if(last && last.isVisible()){
3475           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3476        }
3477    }
3478
3479    // private
3480    function onBeforeHide(m){
3481        if(m.activeChild){
3482            m.activeChild.hide();
3483        }
3484        if(m.autoHideTimer){
3485            clearTimeout(m.autoHideTimer);
3486            delete m.autoHideTimer;
3487        }
3488    }
3489
3490    // private
3491    function onBeforeShow(m){
3492        var pm = m.parentMenu;
3493        if(!pm && !m.allowOtherMenus){
3494            hideAll();
3495        }else if(pm && pm.activeChild && active != m){
3496            pm.activeChild.hide();
3497        }
3498    }
3499
3500    // private this should really trigger on mouseup..
3501    function onMouseDown(e){
3502         Roo.log("on Mouse Up");
3503         
3504         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3505             Roo.log("MenuManager hideAll");
3506             hideAll();
3507             e.stopEvent();
3508         }
3509         
3510         
3511    }
3512
3513    // private
3514    function onBeforeCheck(mi, state){
3515        if(state){
3516            var g = groups[mi.group];
3517            for(var i = 0, l = g.length; i < l; i++){
3518                if(g[i] != mi){
3519                    g[i].setChecked(false);
3520                }
3521            }
3522        }
3523    }
3524
3525    return {
3526
3527        /**
3528         * Hides all menus that are currently visible
3529         */
3530        hideAll : function(){
3531             hideAll();  
3532        },
3533
3534        // private
3535        register : function(menu){
3536            if(!menus){
3537                init();
3538            }
3539            menus[menu.id] = menu;
3540            menu.on("beforehide", onBeforeHide);
3541            menu.on("hide", onHide);
3542            menu.on("beforeshow", onBeforeShow);
3543            menu.on("show", onShow);
3544            var g = menu.group;
3545            if(g && menu.events["checkchange"]){
3546                if(!groups[g]){
3547                    groups[g] = [];
3548                }
3549                groups[g].push(menu);
3550                menu.on("checkchange", onCheck);
3551            }
3552        },
3553
3554         /**
3555          * Returns a {@link Roo.menu.Menu} object
3556          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3557          * be used to generate and return a new Menu instance.
3558          */
3559        get : function(menu){
3560            if(typeof menu == "string"){ // menu id
3561                return menus[menu];
3562            }else if(menu.events){  // menu instance
3563                return menu;
3564            }
3565            /*else if(typeof menu.length == 'number'){ // array of menu items?
3566                return new Roo.bootstrap.Menu({items:menu});
3567            }else{ // otherwise, must be a config
3568                return new Roo.bootstrap.Menu(menu);
3569            }
3570            */
3571            return false;
3572        },
3573
3574        // private
3575        unregister : function(menu){
3576            delete menus[menu.id];
3577            menu.un("beforehide", onBeforeHide);
3578            menu.un("hide", onHide);
3579            menu.un("beforeshow", onBeforeShow);
3580            menu.un("show", onShow);
3581            var g = menu.group;
3582            if(g && menu.events["checkchange"]){
3583                groups[g].remove(menu);
3584                menu.un("checkchange", onCheck);
3585            }
3586        },
3587
3588        // private
3589        registerCheckable : function(menuItem){
3590            var g = menuItem.group;
3591            if(g){
3592                if(!groups[g]){
3593                    groups[g] = [];
3594                }
3595                groups[g].push(menuItem);
3596                menuItem.on("beforecheckchange", onBeforeCheck);
3597            }
3598        },
3599
3600        // private
3601        unregisterCheckable : function(menuItem){
3602            var g = menuItem.group;
3603            if(g){
3604                groups[g].remove(menuItem);
3605                menuItem.un("beforecheckchange", onBeforeCheck);
3606            }
3607        }
3608    };
3609 }();/*
3610  * - LGPL
3611  *
3612  * menu
3613  * 
3614  */
3615
3616 /**
3617  * @class Roo.bootstrap.Menu
3618  * @extends Roo.bootstrap.Component
3619  * Bootstrap Menu class - container for MenuItems
3620  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3621  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3622  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3623  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3624   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3625   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3626  
3627  * @constructor
3628  * Create a new Menu
3629  * @param {Object} config The config object
3630  */
3631
3632
3633 Roo.bootstrap.Menu = function(config){
3634     
3635     if (config.type == 'treeview') {
3636         // normally menu's are drawn attached to the document to handle layering etc..
3637         // however treeview (used by the docs menu is drawn into the parent element)
3638         this.container_method = 'getChildContainer'; 
3639     }
3640     
3641     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3642     if (this.registerMenu && this.type != 'treeview')  {
3643         Roo.bootstrap.MenuMgr.register(this);
3644     }
3645     
3646     
3647     this.addEvents({
3648         /**
3649          * @event beforeshow
3650          * Fires before this menu is displayed (return false to block)
3651          * @param {Roo.menu.Menu} this
3652          */
3653         beforeshow : true,
3654         /**
3655          * @event beforehide
3656          * Fires before this menu is hidden (return false to block)
3657          * @param {Roo.menu.Menu} this
3658          */
3659         beforehide : true,
3660         /**
3661          * @event show
3662          * Fires after this menu is displayed
3663          * @param {Roo.menu.Menu} this
3664          */
3665         show : true,
3666         /**
3667          * @event hide
3668          * Fires after this menu is hidden
3669          * @param {Roo.menu.Menu} this
3670          */
3671         hide : true,
3672         /**
3673          * @event click
3674          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3675          * @param {Roo.menu.Menu} this
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          * @param {Roo.EventObject} e
3678          */
3679         click : true,
3680         /**
3681          * @event mouseover
3682          * Fires when the mouse is hovering over this menu
3683          * @param {Roo.menu.Menu} this
3684          * @param {Roo.EventObject} e
3685          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3686          */
3687         mouseover : true,
3688         /**
3689          * @event mouseout
3690          * Fires when the mouse exits this menu
3691          * @param {Roo.menu.Menu} this
3692          * @param {Roo.EventObject} e
3693          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3694          */
3695         mouseout : true,
3696         /**
3697          * @event itemclick
3698          * Fires when a menu item contained in this menu is clicked
3699          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3700          * @param {Roo.EventObject} e
3701          */
3702         itemclick: true
3703     });
3704     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3705 };
3706
3707 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3708     
3709    /// html : false,
3710    
3711     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3712     type: false,
3713     /**
3714      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3715      */
3716     registerMenu : true,
3717     
3718     menuItems :false, // stores the menu items..
3719     
3720     hidden:true,
3721         
3722     parentMenu : false,
3723     
3724     stopEvent : true,
3725     
3726     isLink : false,
3727     
3728     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3729     
3730     hideTrigger : false,
3731     
3732     align : 'tl-bl?',
3733     
3734     
3735     getChildContainer : function() {
3736         return this.el;  
3737     },
3738     
3739     getAutoCreate : function(){
3740          
3741         //if (['right'].indexOf(this.align)!==-1) {
3742         //    cfg.cn[1].cls += ' pull-right'
3743         //}
3744          
3745         var cfg = {
3746             tag : 'ul',
3747             cls : 'dropdown-menu shadow' ,
3748             style : 'z-index:1000'
3749             
3750         };
3751         
3752         if (this.type === 'submenu') {
3753             cfg.cls = 'submenu active';
3754         }
3755         if (this.type === 'treeview') {
3756             cfg.cls = 'treeview-menu';
3757         }
3758         
3759         return cfg;
3760     },
3761     initEvents : function() {
3762         
3763        // Roo.log("ADD event");
3764        // Roo.log(this.triggerEl.dom);
3765         if (this.triggerEl) {
3766             
3767             this.triggerEl.on('click', this.onTriggerClick, this);
3768             
3769             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3770             
3771             if (!this.hideTrigger) {
3772                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3773                     // dropdown toggle on the 'a' in BS4?
3774                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3775                 } else {
3776                     this.triggerEl.addClass('dropdown-toggle');
3777                 }
3778             }
3779         }
3780         
3781         if (Roo.isTouch) {
3782             this.el.on('touchstart'  , this.onTouch, this);
3783         }
3784         this.el.on('click' , this.onClick, this);
3785
3786         this.el.on("mouseover", this.onMouseOver, this);
3787         this.el.on("mouseout", this.onMouseOut, this);
3788         
3789     },
3790     
3791     findTargetItem : function(e)
3792     {
3793         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3794         if(!t){
3795             return false;
3796         }
3797         //Roo.log(t);         Roo.log(t.id);
3798         if(t && t.id){
3799             //Roo.log(this.menuitems);
3800             return this.menuitems.get(t.id);
3801             
3802             //return this.items.get(t.menuItemId);
3803         }
3804         
3805         return false;
3806     },
3807     
3808     onTouch : function(e) 
3809     {
3810         Roo.log("menu.onTouch");
3811         //e.stopEvent(); this make the user popdown broken
3812         this.onClick(e);
3813     },
3814     
3815     onClick : function(e)
3816     {
3817         Roo.log("menu.onClick");
3818         
3819         var t = this.findTargetItem(e);
3820         if(!t || t.isContainer){
3821             return;
3822         }
3823         Roo.log(e);
3824         /*
3825         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3826             if(t == this.activeItem && t.shouldDeactivate(e)){
3827                 this.activeItem.deactivate();
3828                 delete this.activeItem;
3829                 return;
3830             }
3831             if(t.canActivate){
3832                 this.setActiveItem(t, true);
3833             }
3834             return;
3835             
3836             
3837         }
3838         */
3839        
3840         Roo.log('pass click event');
3841         
3842         t.onClick(e);
3843         
3844         this.fireEvent("click", this, t, e);
3845         
3846         var _this = this;
3847         
3848         if(!t.href.length || t.href == '#'){
3849             (function() { _this.hide(); }).defer(100);
3850         }
3851         
3852     },
3853     
3854     onMouseOver : function(e){
3855         var t  = this.findTargetItem(e);
3856         //Roo.log(t);
3857         //if(t){
3858         //    if(t.canActivate && !t.disabled){
3859         //        this.setActiveItem(t, true);
3860         //    }
3861         //}
3862         
3863         this.fireEvent("mouseover", this, e, t);
3864     },
3865     isVisible : function(){
3866         return !this.hidden;
3867     },
3868     onMouseOut : function(e){
3869         var t  = this.findTargetItem(e);
3870         
3871         //if(t ){
3872         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3873         //        this.activeItem.deactivate();
3874         //        delete this.activeItem;
3875         //    }
3876         //}
3877         this.fireEvent("mouseout", this, e, t);
3878     },
3879     
3880     
3881     /**
3882      * Displays this menu relative to another element
3883      * @param {String/HTMLElement/Roo.Element} element The element to align to
3884      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3885      * the element (defaults to this.defaultAlign)
3886      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3887      */
3888     show : function(el, pos, parentMenu)
3889     {
3890         if (false === this.fireEvent("beforeshow", this)) {
3891             Roo.log("show canceled");
3892             return;
3893         }
3894         this.parentMenu = parentMenu;
3895         if(!this.el){
3896             this.render();
3897         }
3898         this.el.addClass('show'); // show otherwise we do not know how big we are..
3899          
3900         var xy = this.el.getAlignToXY(el, pos);
3901         
3902         // bl-tl << left align  below
3903         // tl-bl << left align 
3904         
3905         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3906             // if it goes to far to the right.. -> align left.
3907             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3908         }
3909         if(xy[0] < 0){
3910             // was left align - go right?
3911             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3912         }
3913         
3914         // goes down the bottom
3915         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3916            xy[1]  < 0 ){
3917             var a = this.align.replace('?', '').split('-');
3918             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3919             
3920         }
3921         
3922         this.showAt(  xy , parentMenu, false);
3923     },
3924      /**
3925      * Displays this menu at a specific xy position
3926      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3927      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3928      */
3929     showAt : function(xy, parentMenu, /* private: */_e){
3930         this.parentMenu = parentMenu;
3931         if(!this.el){
3932             this.render();
3933         }
3934         if(_e !== false){
3935             this.fireEvent("beforeshow", this);
3936             //xy = this.el.adjustForConstraints(xy);
3937         }
3938         
3939         //this.el.show();
3940         this.hideMenuItems();
3941         this.hidden = false;
3942         if (this.triggerEl) {
3943             this.triggerEl.addClass('open');
3944         }
3945         
3946         this.el.addClass('show');
3947         
3948         
3949         
3950         // reassign x when hitting right
3951         
3952         // reassign y when hitting bottom
3953         
3954         // but the list may align on trigger left or trigger top... should it be a properity?
3955         
3956         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3957             this.el.setXY(xy);
3958         }
3959         
3960         this.focus();
3961         this.fireEvent("show", this);
3962     },
3963     
3964     focus : function(){
3965         return;
3966         if(!this.hidden){
3967             this.doFocus.defer(50, this);
3968         }
3969     },
3970
3971     doFocus : function(){
3972         if(!this.hidden){
3973             this.focusEl.focus();
3974         }
3975     },
3976
3977     /**
3978      * Hides this menu and optionally all parent menus
3979      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3980      */
3981     hide : function(deep)
3982     {
3983         if (false === this.fireEvent("beforehide", this)) {
3984             Roo.log("hide canceled");
3985             return;
3986         }
3987         this.hideMenuItems();
3988         if(this.el && this.isVisible()){
3989            
3990             if(this.activeItem){
3991                 this.activeItem.deactivate();
3992                 this.activeItem = null;
3993             }
3994             if (this.triggerEl) {
3995                 this.triggerEl.removeClass('open');
3996             }
3997             
3998             this.el.removeClass('show');
3999             this.hidden = true;
4000             this.fireEvent("hide", this);
4001         }
4002         if(deep === true && this.parentMenu){
4003             this.parentMenu.hide(true);
4004         }
4005     },
4006     
4007     onTriggerClick : function(e)
4008     {
4009         Roo.log('trigger click');
4010         
4011         var target = e.getTarget();
4012         
4013         Roo.log(target.nodeName.toLowerCase());
4014         
4015         if(target.nodeName.toLowerCase() === 'i'){
4016             e.preventDefault();
4017         }
4018         
4019     },
4020     
4021     onTriggerPress  : function(e)
4022     {
4023         Roo.log('trigger press');
4024         //Roo.log(e.getTarget());
4025        // Roo.log(this.triggerEl.dom);
4026        
4027         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4028         var pel = Roo.get(e.getTarget());
4029         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4030             Roo.log('is treeview or dropdown?');
4031             return;
4032         }
4033         
4034         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4035             return;
4036         }
4037         
4038         if (this.isVisible()) {
4039             Roo.log('hide');
4040             this.hide();
4041         } else {
4042             Roo.log('show');
4043             
4044             this.show(this.triggerEl, this.align, false);
4045         }
4046         
4047         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4048             e.stopEvent();
4049         }
4050         
4051     },
4052        
4053     
4054     hideMenuItems : function()
4055     {
4056         Roo.log("hide Menu Items");
4057         if (!this.el) { 
4058             return;
4059         }
4060         
4061         this.el.select('.open',true).each(function(aa) {
4062             
4063             aa.removeClass('open');
4064          
4065         });
4066     },
4067     addxtypeChild : function (tree, cntr) {
4068         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4069           
4070         this.menuitems.add(comp);
4071         return comp;
4072
4073     },
4074     getEl : function()
4075     {
4076         Roo.log(this.el);
4077         return this.el;
4078     },
4079     
4080     clear : function()
4081     {
4082         this.getEl().dom.innerHTML = '';
4083         this.menuitems.clear();
4084     }
4085 });
4086
4087  
4088  /*
4089  * - LGPL
4090  *
4091  * menu item
4092  * 
4093  */
4094
4095
4096 /**
4097  * @class Roo.bootstrap.MenuItem
4098  * @extends Roo.bootstrap.Component
4099  * Bootstrap MenuItem class
4100  * @cfg {String} html the menu label
4101  * @cfg {String} href the link
4102  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4105  * @cfg {String} fa favicon to show on left of menu item.
4106  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107  * 
4108  * 
4109  * @constructor
4110  * Create a new MenuItem
4111  * @param {Object} config The config object
4112  */
4113
4114
4115 Roo.bootstrap.MenuItem = function(config){
4116     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4117     this.addEvents({
4118         // raw events
4119         /**
4120          * @event click
4121          * The raw click event for the entire grid.
4122          * @param {Roo.bootstrap.MenuItem} this
4123          * @param {Roo.EventObject} e
4124          */
4125         "click" : true
4126     });
4127 };
4128
4129 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4130     
4131     href : false,
4132     html : false,
4133     preventDefault: false,
4134     isContainer : false,
4135     active : false,
4136     fa: false,
4137     
4138     getAutoCreate : function(){
4139         
4140         if(this.isContainer){
4141             return {
4142                 tag: 'li',
4143                 cls: 'dropdown-menu-item '
4144             };
4145         }
4146         var ctag = {
4147             tag: 'span',
4148             html: 'Link'
4149         };
4150         
4151         var anc = {
4152             tag : 'a',
4153             cls : 'dropdown-item',
4154             href : '#',
4155             cn : [  ]
4156         };
4157         
4158         if (this.fa !== false) {
4159             anc.cn.push({
4160                 tag : 'i',
4161                 cls : 'fa fa-' + this.fa
4162             });
4163         }
4164         
4165         anc.cn.push(ctag);
4166         
4167         
4168         var cfg= {
4169             tag: 'li',
4170             cls: 'dropdown-menu-item',
4171             cn: [ anc ]
4172         };
4173         if (this.parent().type == 'treeview') {
4174             cfg.cls = 'treeview-menu';
4175         }
4176         if (this.active) {
4177             cfg.cls += ' active';
4178         }
4179         
4180         
4181         
4182         anc.href = this.href || cfg.cn[0].href ;
4183         ctag.html = this.html || cfg.cn[0].html ;
4184         return cfg;
4185     },
4186     
4187     initEvents: function()
4188     {
4189         if (this.parent().type == 'treeview') {
4190             this.el.select('a').on('click', this.onClick, this);
4191         }
4192         
4193         if (this.menu) {
4194             this.menu.parentType = this.xtype;
4195             this.menu.triggerEl = this.el;
4196             this.menu = this.addxtype(Roo.apply({}, this.menu));
4197         }
4198         
4199     },
4200     onClick : function(e)
4201     {
4202         Roo.log('item on click ');
4203         
4204         if(this.preventDefault){
4205             e.preventDefault();
4206         }
4207         //this.parent().hideMenuItems();
4208         
4209         this.fireEvent('click', this, e);
4210     },
4211     getEl : function()
4212     {
4213         return this.el;
4214     } 
4215 });
4216
4217  
4218
4219  /*
4220  * - LGPL
4221  *
4222  * menu separator
4223  * 
4224  */
4225
4226
4227 /**
4228  * @class Roo.bootstrap.MenuSeparator
4229  * @extends Roo.bootstrap.Component
4230  * Bootstrap MenuSeparator class
4231  * 
4232  * @constructor
4233  * Create a new MenuItem
4234  * @param {Object} config The config object
4235  */
4236
4237
4238 Roo.bootstrap.MenuSeparator = function(config){
4239     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4240 };
4241
4242 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4243     
4244     getAutoCreate : function(){
4245         var cfg = {
4246             cls: 'divider',
4247             tag : 'li'
4248         };
4249         
4250         return cfg;
4251     }
4252    
4253 });
4254
4255  
4256
4257  
4258 /*
4259 * Licence: LGPL
4260 */
4261
4262 /**
4263  * @class Roo.bootstrap.Modal
4264  * @extends Roo.bootstrap.Component
4265  * @builder-top
4266  * @parent none
4267  * @children Roo.bootstrap.Component
4268  * Bootstrap Modal class
4269  * @cfg {String} title Title of dialog
4270  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4271  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4272  * @cfg {Boolean} specificTitle default false
4273  * @cfg {Array} buttons Array of buttons or standard button set..
4274  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4275  * @cfg {Boolean} animate default true
4276  * @cfg {Boolean} allow_close default true
4277  * @cfg {Boolean} fitwindow default false
4278  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4279  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4280  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4281  * @cfg {String} size (sm|lg|xl) default empty
4282  * @cfg {Number} max_width set the max width of modal
4283  * @cfg {Boolean} editableTitle can the title be edited
4284
4285  *
4286  *
4287  * @constructor
4288  * Create a new Modal Dialog
4289  * @param {Object} config The config object
4290  */
4291
4292 Roo.bootstrap.Modal = function(config){
4293     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4294     this.addEvents({
4295         // raw events
4296         /**
4297          * @event btnclick
4298          * The raw btnclick event for the button
4299          * @param {Roo.EventObject} e
4300          */
4301         "btnclick" : true,
4302         /**
4303          * @event resize
4304          * Fire when dialog resize
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} e
4307          */
4308         "resize" : true,
4309         /**
4310          * @event titlechanged
4311          * Fire when the editable title has been changed
4312          * @param {Roo.bootstrap.Modal} this
4313          * @param {Roo.EventObject} value
4314          */
4315         "titlechanged" : true 
4316         
4317     });
4318     this.buttons = this.buttons || [];
4319
4320     if (this.tmpl) {
4321         this.tmpl = Roo.factory(this.tmpl);
4322     }
4323
4324 };
4325
4326 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4327
4328     title : 'test dialog',
4329
4330     buttons : false,
4331
4332     // set on load...
4333
4334     html: false,
4335
4336     tmp: false,
4337
4338     specificTitle: false,
4339
4340     buttonPosition: 'right',
4341
4342     allow_close : true,
4343
4344     animate : true,
4345
4346     fitwindow: false,
4347     
4348      // private
4349     dialogEl: false,
4350     bodyEl:  false,
4351     footerEl:  false,
4352     titleEl:  false,
4353     closeEl:  false,
4354
4355     size: '',
4356     
4357     max_width: 0,
4358     
4359     max_height: 0,
4360     
4361     fit_content: false,
4362     editableTitle  : false,
4363
4364     onRender : function(ct, position)
4365     {
4366         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4367
4368         if(!this.el){
4369             var cfg = Roo.apply({},  this.getAutoCreate());
4370             cfg.id = Roo.id();
4371             //if(!cfg.name){
4372             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4373             //}
4374             //if (!cfg.name.length) {
4375             //    delete cfg.name;
4376            // }
4377             if (this.cls) {
4378                 cfg.cls += ' ' + this.cls;
4379             }
4380             if (this.style) {
4381                 cfg.style = this.style;
4382             }
4383             this.el = Roo.get(document.body).createChild(cfg, position);
4384         }
4385         //var type = this.el.dom.type;
4386
4387
4388         if(this.tabIndex !== undefined){
4389             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4390         }
4391
4392         this.dialogEl = this.el.select('.modal-dialog',true).first();
4393         this.bodyEl = this.el.select('.modal-body',true).first();
4394         this.closeEl = this.el.select('.modal-header .close', true).first();
4395         this.headerEl = this.el.select('.modal-header',true).first();
4396         this.titleEl = this.el.select('.modal-title',true).first();
4397         this.footerEl = this.el.select('.modal-footer',true).first();
4398
4399         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4400         
4401         //this.el.addClass("x-dlg-modal");
4402
4403         if (this.buttons.length) {
4404             Roo.each(this.buttons, function(bb) {
4405                 var b = Roo.apply({}, bb);
4406                 b.xns = b.xns || Roo.bootstrap;
4407                 b.xtype = b.xtype || 'Button';
4408                 if (typeof(b.listeners) == 'undefined') {
4409                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4410                 }
4411
4412                 var btn = Roo.factory(b);
4413
4414                 btn.render(this.getButtonContainer());
4415
4416             },this);
4417         }
4418         // render the children.
4419         var nitems = [];
4420
4421         if(typeof(this.items) != 'undefined'){
4422             var items = this.items;
4423             delete this.items;
4424
4425             for(var i =0;i < items.length;i++) {
4426                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4427             }
4428         }
4429
4430         this.items = nitems;
4431
4432         // where are these used - they used to be body/close/footer
4433
4434
4435         this.initEvents();
4436         //this.el.addClass([this.fieldClass, this.cls]);
4437
4438     },
4439
4440     getAutoCreate : function()
4441     {
4442         // we will default to modal-body-overflow - might need to remove or make optional later.
4443         var bdy = {
4444                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4445                 html : this.html || ''
4446         };
4447
4448         var title = {
4449             tag: 'h5',
4450             cls : 'modal-title',
4451             html : this.title
4452         };
4453
4454         if(this.specificTitle){ // WTF is this?
4455             title = this.title;
4456         }
4457
4458         var header = [];
4459         if (this.allow_close && Roo.bootstrap.version == 3) {
4460             header.push({
4461                 tag: 'button',
4462                 cls : 'close',
4463                 html : '&times'
4464             });
4465         }
4466
4467         header.push(title);
4468
4469         if (this.editableTitle) {
4470             header.push({
4471                 cls: 'form-control roo-editable-title d-none',
4472                 tag: 'input',
4473                 type: 'text'
4474             });
4475         }
4476         
4477         if (this.allow_close && Roo.bootstrap.version == 4) {
4478             header.push({
4479                 tag: 'button',
4480                 cls : 'close',
4481                 html : '&times'
4482             });
4483         }
4484         
4485         var size = '';
4486
4487         if(this.size.length){
4488             size = 'modal-' + this.size;
4489         }
4490         
4491         var footer = Roo.bootstrap.version == 3 ?
4492             {
4493                 cls : 'modal-footer',
4494                 cn : [
4495                     {
4496                         tag: 'div',
4497                         cls: 'btn-' + this.buttonPosition
4498                     }
4499                 ]
4500
4501             } :
4502             {  // BS4 uses mr-auto on left buttons....
4503                 cls : 'modal-footer'
4504             };
4505
4506             
4507
4508         
4509         
4510         var modal = {
4511             cls: "modal",
4512              cn : [
4513                 {
4514                     cls: "modal-dialog " + size,
4515                     cn : [
4516                         {
4517                             cls : "modal-content",
4518                             cn : [
4519                                 {
4520                                     cls : 'modal-header',
4521                                     cn : header
4522                                 },
4523                                 bdy,
4524                                 footer
4525                             ]
4526
4527                         }
4528                     ]
4529
4530                 }
4531             ]
4532         };
4533
4534         if(this.animate){
4535             modal.cls += ' fade';
4536         }
4537
4538         return modal;
4539
4540     },
4541     getChildContainer : function() {
4542
4543          return this.bodyEl;
4544
4545     },
4546     getButtonContainer : function() {
4547         
4548          return Roo.bootstrap.version == 4 ?
4549             this.el.select('.modal-footer',true).first()
4550             : this.el.select('.modal-footer div',true).first();
4551
4552     },
4553     initEvents : function()
4554     {
4555         if (this.allow_close) {
4556             this.closeEl.on('click', this.hide, this);
4557         }
4558         Roo.EventManager.onWindowResize(this.resize, this, true);
4559         if (this.editableTitle) {
4560             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4561             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4562             this.headerEditEl.on('keyup', function(e) {
4563                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4564                         this.toggleHeaderInput(false)
4565                     }
4566                 }, this);
4567             this.headerEditEl.on('blur', function(e) {
4568                 this.toggleHeaderInput(false)
4569             },this);
4570         }
4571
4572     },
4573   
4574
4575     resize : function()
4576     {
4577         this.maskEl.setSize(
4578             Roo.lib.Dom.getViewWidth(true),
4579             Roo.lib.Dom.getViewHeight(true)
4580         );
4581         
4582         if (this.fitwindow) {
4583             
4584            this.dialogEl.setStyle( { 'max-width' : '100%' });
4585             this.setSize(
4586                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4587                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4588             );
4589             return;
4590         }
4591         
4592         if(this.max_width !== 0) {
4593             
4594             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4595             
4596             if(this.height) {
4597                 this.setSize(w, this.height);
4598                 return;
4599             }
4600             
4601             if(this.max_height) {
4602                 this.setSize(w,Math.min(
4603                     this.max_height,
4604                     Roo.lib.Dom.getViewportHeight(true) - 60
4605                 ));
4606                 
4607                 return;
4608             }
4609             
4610             if(!this.fit_content) {
4611                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4612                 return;
4613             }
4614             
4615             this.setSize(w, Math.min(
4616                 60 +
4617                 this.headerEl.getHeight() + 
4618                 this.footerEl.getHeight() + 
4619                 this.getChildHeight(this.bodyEl.dom.childNodes),
4620                 Roo.lib.Dom.getViewportHeight(true) - 60)
4621             );
4622         }
4623         
4624     },
4625
4626     setSize : function(w,h)
4627     {
4628         if (!w && !h) {
4629             return;
4630         }
4631         
4632         this.resizeTo(w,h);
4633     },
4634
4635     show : function() {
4636
4637         if (!this.rendered) {
4638             this.render();
4639         }
4640         this.toggleHeaderInput(false);
4641         //this.el.setStyle('display', 'block');
4642         this.el.removeClass('hideing');
4643         this.el.dom.style.display='block';
4644         
4645         Roo.get(document.body).addClass('modal-open');
4646  
4647         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4648             
4649             (function(){
4650                 this.el.addClass('show');
4651                 this.el.addClass('in');
4652             }).defer(50, this);
4653         }else{
4654             this.el.addClass('show');
4655             this.el.addClass('in');
4656         }
4657
4658         // not sure how we can show data in here..
4659         //if (this.tmpl) {
4660         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4661         //}
4662
4663         Roo.get(document.body).addClass("x-body-masked");
4664         
4665         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4666         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4667         this.maskEl.dom.style.display = 'block';
4668         this.maskEl.addClass('show');
4669         
4670         
4671         this.resize();
4672         
4673         this.fireEvent('show', this);
4674
4675         // set zindex here - otherwise it appears to be ignored...
4676         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4677
4678         (function () {
4679             this.items.forEach( function(e) {
4680                 e.layout ? e.layout() : false;
4681
4682             });
4683         }).defer(100,this);
4684
4685     },
4686     hide : function()
4687     {
4688         if(this.fireEvent("beforehide", this) !== false){
4689             
4690             this.maskEl.removeClass('show');
4691             
4692             this.maskEl.dom.style.display = '';
4693             Roo.get(document.body).removeClass("x-body-masked");
4694             this.el.removeClass('in');
4695             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4696
4697             if(this.animate){ // why
4698                 this.el.addClass('hideing');
4699                 this.el.removeClass('show');
4700                 (function(){
4701                     if (!this.el.hasClass('hideing')) {
4702                         return; // it's been shown again...
4703                     }
4704                     
4705                     this.el.dom.style.display='';
4706
4707                     Roo.get(document.body).removeClass('modal-open');
4708                     this.el.removeClass('hideing');
4709                 }).defer(150,this);
4710                 
4711             }else{
4712                 this.el.removeClass('show');
4713                 this.el.dom.style.display='';
4714                 Roo.get(document.body).removeClass('modal-open');
4715
4716             }
4717             this.fireEvent('hide', this);
4718         }
4719     },
4720     isVisible : function()
4721     {
4722         
4723         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4724         
4725     },
4726
4727     addButton : function(str, cb)
4728     {
4729
4730
4731         var b = Roo.apply({}, { html : str } );
4732         b.xns = b.xns || Roo.bootstrap;
4733         b.xtype = b.xtype || 'Button';
4734         if (typeof(b.listeners) == 'undefined') {
4735             b.listeners = { click : cb.createDelegate(this)  };
4736         }
4737
4738         var btn = Roo.factory(b);
4739
4740         btn.render(this.getButtonContainer());
4741
4742         return btn;
4743
4744     },
4745
4746     setDefaultButton : function(btn)
4747     {
4748         //this.el.select('.modal-footer').()
4749     },
4750
4751     resizeTo: function(w,h)
4752     {
4753         this.dialogEl.setWidth(w);
4754         
4755         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4756
4757         this.bodyEl.setHeight(h - diff);
4758         
4759         this.fireEvent('resize', this);
4760     },
4761     
4762     setContentSize  : function(w, h)
4763     {
4764
4765     },
4766     onButtonClick: function(btn,e)
4767     {
4768         //Roo.log([a,b,c]);
4769         this.fireEvent('btnclick', btn.name, e);
4770     },
4771      /**
4772      * Set the title of the Dialog
4773      * @param {String} str new Title
4774      */
4775     setTitle: function(str) {
4776         this.titleEl.dom.innerHTML = str;
4777         this.title = str;
4778     },
4779     /**
4780      * Set the body of the Dialog
4781      * @param {String} str new Title
4782      */
4783     setBody: function(str) {
4784         this.bodyEl.dom.innerHTML = str;
4785     },
4786     /**
4787      * Set the body of the Dialog using the template
4788      * @param {Obj} data - apply this data to the template and replace the body contents.
4789      */
4790     applyBody: function(obj)
4791     {
4792         if (!this.tmpl) {
4793             Roo.log("Error - using apply Body without a template");
4794             //code
4795         }
4796         this.tmpl.overwrite(this.bodyEl, obj);
4797     },
4798     
4799     getChildHeight : function(child_nodes)
4800     {
4801         if(
4802             !child_nodes ||
4803             child_nodes.length == 0
4804         ) {
4805             return 0;
4806         }
4807         
4808         var child_height = 0;
4809         
4810         for(var i = 0; i < child_nodes.length; i++) {
4811             
4812             /*
4813             * for modal with tabs...
4814             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4815                 
4816                 var layout_childs = child_nodes[i].childNodes;
4817                 
4818                 for(var j = 0; j < layout_childs.length; j++) {
4819                     
4820                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4821                         
4822                         var layout_body_childs = layout_childs[j].childNodes;
4823                         
4824                         for(var k = 0; k < layout_body_childs.length; k++) {
4825                             
4826                             if(layout_body_childs[k].classList.contains('navbar')) {
4827                                 child_height += layout_body_childs[k].offsetHeight;
4828                                 continue;
4829                             }
4830                             
4831                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4832                                 
4833                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4834                                 
4835                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4836                                     
4837                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4838                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4839                                         continue;
4840                                     }
4841                                     
4842                                 }
4843                                 
4844                             }
4845                             
4846                         }
4847                     }
4848                 }
4849                 continue;
4850             }
4851             */
4852             
4853             child_height += child_nodes[i].offsetHeight;
4854             // Roo.log(child_nodes[i].offsetHeight);
4855         }
4856         
4857         return child_height;
4858     },
4859     toggleHeaderInput : function(is_edit)
4860     {
4861         if (!this.editableTitle) {
4862             return; // not editable.
4863         }
4864         if (is_edit && this.is_header_editing) {
4865             return; // already editing..
4866         }
4867         if (is_edit) {
4868     
4869             this.headerEditEl.dom.value = this.title;
4870             this.headerEditEl.removeClass('d-none');
4871             this.headerEditEl.dom.focus();
4872             this.titleEl.addClass('d-none');
4873             
4874             this.is_header_editing = true;
4875             return
4876         }
4877         // flip back to not editing.
4878         this.title = this.headerEditEl.dom.value;
4879         this.headerEditEl.addClass('d-none');
4880         this.titleEl.removeClass('d-none');
4881         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4882         this.is_header_editing = false;
4883         this.fireEvent('titlechanged', this, this.title);
4884     
4885             
4886         
4887     }
4888
4889 });
4890
4891
4892 Roo.apply(Roo.bootstrap.Modal,  {
4893     /**
4894          * Button config that displays a single OK button
4895          * @type Object
4896          */
4897         OK :  [{
4898             name : 'ok',
4899             weight : 'primary',
4900             html : 'OK'
4901         }],
4902         /**
4903          * Button config that displays Yes and No buttons
4904          * @type Object
4905          */
4906         YESNO : [
4907             {
4908                 name  : 'no',
4909                 html : 'No'
4910             },
4911             {
4912                 name  :'yes',
4913                 weight : 'primary',
4914                 html : 'Yes'
4915             }
4916         ],
4917
4918         /**
4919          * Button config that displays OK and Cancel buttons
4920          * @type Object
4921          */
4922         OKCANCEL : [
4923             {
4924                name : 'cancel',
4925                 html : 'Cancel'
4926             },
4927             {
4928                 name : 'ok',
4929                 weight : 'primary',
4930                 html : 'OK'
4931             }
4932         ],
4933         /**
4934          * Button config that displays Yes, No and Cancel buttons
4935          * @type Object
4936          */
4937         YESNOCANCEL : [
4938             {
4939                 name : 'yes',
4940                 weight : 'primary',
4941                 html : 'Yes'
4942             },
4943             {
4944                 name : 'no',
4945                 html : 'No'
4946             },
4947             {
4948                 name : 'cancel',
4949                 html : 'Cancel'
4950             }
4951         ],
4952         
4953         zIndex : 10001
4954 });
4955
4956 /*
4957  * - LGPL
4958  *
4959  * messagebox - can be used as a replace
4960  * 
4961  */
4962 /**
4963  * @class Roo.MessageBox
4964  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4965  * Example usage:
4966  *<pre><code>
4967 // Basic alert:
4968 Roo.Msg.alert('Status', 'Changes saved successfully.');
4969
4970 // Prompt for user data:
4971 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4972     if (btn == 'ok'){
4973         // process text value...
4974     }
4975 });
4976
4977 // Show a dialog using config options:
4978 Roo.Msg.show({
4979    title:'Save Changes?',
4980    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4981    buttons: Roo.Msg.YESNOCANCEL,
4982    fn: processResult,
4983    animEl: 'elId'
4984 });
4985 </code></pre>
4986  * @singleton
4987  */
4988 Roo.bootstrap.MessageBox = function(){
4989     var dlg, opt, mask, waitTimer;
4990     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4991     var buttons, activeTextEl, bwidth;
4992
4993     
4994     // private
4995     var handleButton = function(button){
4996         dlg.hide();
4997         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4998     };
4999
5000     // private
5001     var handleHide = function(){
5002         if(opt && opt.cls){
5003             dlg.el.removeClass(opt.cls);
5004         }
5005         //if(waitTimer){
5006         //    Roo.TaskMgr.stop(waitTimer);
5007         //    waitTimer = null;
5008         //}
5009     };
5010
5011     // private
5012     var updateButtons = function(b){
5013         var width = 0;
5014         if(!b){
5015             buttons["ok"].hide();
5016             buttons["cancel"].hide();
5017             buttons["yes"].hide();
5018             buttons["no"].hide();
5019             dlg.footerEl.hide();
5020             
5021             return width;
5022         }
5023         dlg.footerEl.show();
5024         for(var k in buttons){
5025             if(typeof buttons[k] != "function"){
5026                 if(b[k]){
5027                     buttons[k].show();
5028                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5029                     width += buttons[k].el.getWidth()+15;
5030                 }else{
5031                     buttons[k].hide();
5032                 }
5033             }
5034         }
5035         return width;
5036     };
5037
5038     // private
5039     var handleEsc = function(d, k, e){
5040         if(opt && opt.closable !== false){
5041             dlg.hide();
5042         }
5043         if(e){
5044             e.stopEvent();
5045         }
5046     };
5047
5048     return {
5049         /**
5050          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5051          * @return {Roo.BasicDialog} The BasicDialog element
5052          */
5053         getDialog : function(){
5054            if(!dlg){
5055                 dlg = new Roo.bootstrap.Modal( {
5056                     //draggable: true,
5057                     //resizable:false,
5058                     //constraintoviewport:false,
5059                     //fixedcenter:true,
5060                     //collapsible : false,
5061                     //shim:true,
5062                     //modal: true,
5063                 //    width: 'auto',
5064                   //  height:100,
5065                     //buttonAlign:"center",
5066                     closeClick : function(){
5067                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5068                             handleButton("no");
5069                         }else{
5070                             handleButton("cancel");
5071                         }
5072                     }
5073                 });
5074                 dlg.render();
5075                 dlg.on("hide", handleHide);
5076                 mask = dlg.mask;
5077                 //dlg.addKeyListener(27, handleEsc);
5078                 buttons = {};
5079                 this.buttons = buttons;
5080                 var bt = this.buttonText;
5081                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5082                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5083                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5084                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5085                 //Roo.log(buttons);
5086                 bodyEl = dlg.bodyEl.createChild({
5087
5088                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5089                         '<textarea class="roo-mb-textarea"></textarea>' +
5090                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5091                 });
5092                 msgEl = bodyEl.dom.firstChild;
5093                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5094                 textboxEl.enableDisplayMode();
5095                 textboxEl.addKeyListener([10,13], function(){
5096                     if(dlg.isVisible() && opt && opt.buttons){
5097                         if(opt.buttons.ok){
5098                             handleButton("ok");
5099                         }else if(opt.buttons.yes){
5100                             handleButton("yes");
5101                         }
5102                     }
5103                 });
5104                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5105                 textareaEl.enableDisplayMode();
5106                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5107                 progressEl.enableDisplayMode();
5108                 
5109                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5110                 var pf = progressEl.dom.firstChild;
5111                 if (pf) {
5112                     pp = Roo.get(pf.firstChild);
5113                     pp.setHeight(pf.offsetHeight);
5114                 }
5115                 
5116             }
5117             return dlg;
5118         },
5119
5120         /**
5121          * Updates the message box body text
5122          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5123          * the XHTML-compliant non-breaking space character '&amp;#160;')
5124          * @return {Roo.MessageBox} This message box
5125          */
5126         updateText : function(text)
5127         {
5128             if(!dlg.isVisible() && !opt.width){
5129                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5130                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5131             }
5132             msgEl.innerHTML = text || '&#160;';
5133       
5134             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5135             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5136             var w = Math.max(
5137                     Math.min(opt.width || cw , this.maxWidth), 
5138                     Math.max(opt.minWidth || this.minWidth, bwidth)
5139             );
5140             if(opt.prompt){
5141                 activeTextEl.setWidth(w);
5142             }
5143             if(dlg.isVisible()){
5144                 dlg.fixedcenter = false;
5145             }
5146             // to big, make it scroll. = But as usual stupid IE does not support
5147             // !important..
5148             
5149             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5150                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5151                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5152             } else {
5153                 bodyEl.dom.style.height = '';
5154                 bodyEl.dom.style.overflowY = '';
5155             }
5156             if (cw > w) {
5157                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5158             } else {
5159                 bodyEl.dom.style.overflowX = '';
5160             }
5161             
5162             dlg.setContentSize(w, bodyEl.getHeight());
5163             if(dlg.isVisible()){
5164                 dlg.fixedcenter = true;
5165             }
5166             return this;
5167         },
5168
5169         /**
5170          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5171          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5172          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5173          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5174          * @return {Roo.MessageBox} This message box
5175          */
5176         updateProgress : function(value, text){
5177             if(text){
5178                 this.updateText(text);
5179             }
5180             
5181             if (pp) { // weird bug on my firefox - for some reason this is not defined
5182                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5183                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5184             }
5185             return this;
5186         },        
5187
5188         /**
5189          * Returns true if the message box is currently displayed
5190          * @return {Boolean} True if the message box is visible, else false
5191          */
5192         isVisible : function(){
5193             return dlg && dlg.isVisible();  
5194         },
5195
5196         /**
5197          * Hides the message box if it is displayed
5198          */
5199         hide : function(){
5200             if(this.isVisible()){
5201                 dlg.hide();
5202             }  
5203         },
5204
5205         /**
5206          * Displays a new message box, or reinitializes an existing message box, based on the config options
5207          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5208          * The following config object properties are supported:
5209          * <pre>
5210 Property    Type             Description
5211 ----------  ---------------  ------------------------------------------------------------------------------------
5212 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5213                                    closes (defaults to undefined)
5214 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5215                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5216 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5217                                    progress and wait dialogs will ignore this property and always hide the
5218                                    close button as they can only be closed programmatically.
5219 cls               String           A custom CSS class to apply to the message box element
5220 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5221                                    displayed (defaults to 75)
5222 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5223                                    function will be btn (the name of the button that was clicked, if applicable,
5224                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5225                                    Progress and wait dialogs will ignore this option since they do not respond to
5226                                    user actions and can only be closed programmatically, so any required function
5227                                    should be called by the same code after it closes the dialog.
5228 icon              String           A CSS class that provides a background image to be used as an icon for
5229                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5230 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5231 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5232 modal             Boolean          False to allow user interaction with the page while the message box is
5233                                    displayed (defaults to true)
5234 msg               String           A string that will replace the existing message box body text (defaults
5235                                    to the XHTML-compliant non-breaking space character '&#160;')
5236 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5237 progress          Boolean          True to display a progress bar (defaults to false)
5238 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5239 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5240 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5241 title             String           The title text
5242 value             String           The string value to set into the active textbox element if displayed
5243 wait              Boolean          True to display a progress bar (defaults to false)
5244 width             Number           The width of the dialog in pixels
5245 </pre>
5246          *
5247          * Example usage:
5248          * <pre><code>
5249 Roo.Msg.show({
5250    title: 'Address',
5251    msg: 'Please enter your address:',
5252    width: 300,
5253    buttons: Roo.MessageBox.OKCANCEL,
5254    multiline: true,
5255    fn: saveAddress,
5256    animEl: 'addAddressBtn'
5257 });
5258 </code></pre>
5259          * @param {Object} config Configuration options
5260          * @return {Roo.MessageBox} This message box
5261          */
5262         show : function(options)
5263         {
5264             
5265             // this causes nightmares if you show one dialog after another
5266             // especially on callbacks..
5267              
5268             if(this.isVisible()){
5269                 
5270                 this.hide();
5271                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5272                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5273                 Roo.log("New Dialog Message:" +  options.msg )
5274                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5275                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5276                 
5277             }
5278             var d = this.getDialog();
5279             opt = options;
5280             d.setTitle(opt.title || "&#160;");
5281             d.closeEl.setDisplayed(opt.closable !== false);
5282             activeTextEl = textboxEl;
5283             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5284             if(opt.prompt){
5285                 if(opt.multiline){
5286                     textboxEl.hide();
5287                     textareaEl.show();
5288                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5289                         opt.multiline : this.defaultTextHeight);
5290                     activeTextEl = textareaEl;
5291                 }else{
5292                     textboxEl.show();
5293                     textareaEl.hide();
5294                 }
5295             }else{
5296                 textboxEl.hide();
5297                 textareaEl.hide();
5298             }
5299             progressEl.setDisplayed(opt.progress === true);
5300             if (opt.progress) {
5301                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5302             }
5303             this.updateProgress(0);
5304             activeTextEl.dom.value = opt.value || "";
5305             if(opt.prompt){
5306                 dlg.setDefaultButton(activeTextEl);
5307             }else{
5308                 var bs = opt.buttons;
5309                 var db = null;
5310                 if(bs && bs.ok){
5311                     db = buttons["ok"];
5312                 }else if(bs && bs.yes){
5313                     db = buttons["yes"];
5314                 }
5315                 dlg.setDefaultButton(db);
5316             }
5317             bwidth = updateButtons(opt.buttons);
5318             this.updateText(opt.msg);
5319             if(opt.cls){
5320                 d.el.addClass(opt.cls);
5321             }
5322             d.proxyDrag = opt.proxyDrag === true;
5323             d.modal = opt.modal !== false;
5324             d.mask = opt.modal !== false ? mask : false;
5325             if(!d.isVisible()){
5326                 // force it to the end of the z-index stack so it gets a cursor in FF
5327                 document.body.appendChild(dlg.el.dom);
5328                 d.animateTarget = null;
5329                 d.show(options.animEl);
5330             }
5331             return this;
5332         },
5333
5334         /**
5335          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5336          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5337          * and closing the message box when the process is complete.
5338          * @param {String} title The title bar text
5339          * @param {String} msg The message box body text
5340          * @return {Roo.MessageBox} This message box
5341          */
5342         progress : function(title, msg){
5343             this.show({
5344                 title : title,
5345                 msg : msg,
5346                 buttons: false,
5347                 progress:true,
5348                 closable:false,
5349                 minWidth: this.minProgressWidth,
5350                 modal : true
5351             });
5352             return this;
5353         },
5354
5355         /**
5356          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5357          * If a callback function is passed it will be called after the user clicks the button, and the
5358          * id of the button that was clicked will be passed as the only parameter to the callback
5359          * (could also be the top-right close button).
5360          * @param {String} title The title bar text
5361          * @param {String} msg The message box body text
5362          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5363          * @param {Object} scope (optional) The scope of the callback function
5364          * @return {Roo.MessageBox} This message box
5365          */
5366         alert : function(title, msg, fn, scope)
5367         {
5368             this.show({
5369                 title : title,
5370                 msg : msg,
5371                 buttons: this.OK,
5372                 fn: fn,
5373                 closable : false,
5374                 scope : scope,
5375                 modal : true
5376             });
5377             return this;
5378         },
5379
5380         /**
5381          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5382          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5383          * You are responsible for closing the message box when the process is complete.
5384          * @param {String} msg The message box body text
5385          * @param {String} title (optional) The title bar text
5386          * @return {Roo.MessageBox} This message box
5387          */
5388         wait : function(msg, title){
5389             this.show({
5390                 title : title,
5391                 msg : msg,
5392                 buttons: false,
5393                 closable:false,
5394                 progress:true,
5395                 modal:true,
5396                 width:300,
5397                 wait:true
5398             });
5399             waitTimer = Roo.TaskMgr.start({
5400                 run: function(i){
5401                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5402                 },
5403                 interval: 1000
5404             });
5405             return this;
5406         },
5407
5408         /**
5409          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5410          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5411          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5412          * @param {String} title The title bar text
5413          * @param {String} msg The message box body text
5414          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5415          * @param {Object} scope (optional) The scope of the callback function
5416          * @return {Roo.MessageBox} This message box
5417          */
5418         confirm : function(title, msg, fn, scope){
5419             this.show({
5420                 title : title,
5421                 msg : msg,
5422                 buttons: this.YESNO,
5423                 fn: fn,
5424                 scope : scope,
5425                 modal : true
5426             });
5427             return this;
5428         },
5429
5430         /**
5431          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5432          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5433          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5434          * (could also be the top-right close button) and the text that was entered will be passed as the two
5435          * parameters to the callback.
5436          * @param {String} title The title bar text
5437          * @param {String} msg The message box body text
5438          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5439          * @param {Object} scope (optional) The scope of the callback function
5440          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5441          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5442          * @return {Roo.MessageBox} This message box
5443          */
5444         prompt : function(title, msg, fn, scope, multiline){
5445             this.show({
5446                 title : title,
5447                 msg : msg,
5448                 buttons: this.OKCANCEL,
5449                 fn: fn,
5450                 minWidth:250,
5451                 scope : scope,
5452                 prompt:true,
5453                 multiline: multiline,
5454                 modal : true
5455             });
5456             return this;
5457         },
5458
5459         /**
5460          * Button config that displays a single OK button
5461          * @type Object
5462          */
5463         OK : {ok:true},
5464         /**
5465          * Button config that displays Yes and No buttons
5466          * @type Object
5467          */
5468         YESNO : {yes:true, no:true},
5469         /**
5470          * Button config that displays OK and Cancel buttons
5471          * @type Object
5472          */
5473         OKCANCEL : {ok:true, cancel:true},
5474         /**
5475          * Button config that displays Yes, No and Cancel buttons
5476          * @type Object
5477          */
5478         YESNOCANCEL : {yes:true, no:true, cancel:true},
5479
5480         /**
5481          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5482          * @type Number
5483          */
5484         defaultTextHeight : 75,
5485         /**
5486          * The maximum width in pixels of the message box (defaults to 600)
5487          * @type Number
5488          */
5489         maxWidth : 600,
5490         /**
5491          * The minimum width in pixels of the message box (defaults to 100)
5492          * @type Number
5493          */
5494         minWidth : 100,
5495         /**
5496          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5497          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5498          * @type Number
5499          */
5500         minProgressWidth : 250,
5501         /**
5502          * An object containing the default button text strings that can be overriden for localized language support.
5503          * Supported properties are: ok, cancel, yes and no.
5504          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5505          * @type Object
5506          */
5507         buttonText : {
5508             ok : "OK",
5509             cancel : "Cancel",
5510             yes : "Yes",
5511             no : "No"
5512         }
5513     };
5514 }();
5515
5516 /**
5517  * Shorthand for {@link Roo.MessageBox}
5518  */
5519 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5520 Roo.Msg = Roo.Msg || Roo.MessageBox;
5521 /*
5522  * - LGPL
5523  *
5524  * navbar
5525  * 
5526  */
5527
5528 /**
5529  * @class Roo.bootstrap.Navbar
5530  * @extends Roo.bootstrap.Component
5531  * Bootstrap Navbar class
5532
5533  * @constructor
5534  * Create a new Navbar
5535  * @param {Object} config The config object
5536  */
5537
5538
5539 Roo.bootstrap.Navbar = function(config){
5540     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5541     this.addEvents({
5542         // raw events
5543         /**
5544          * @event beforetoggle
5545          * Fire before toggle the menu
5546          * @param {Roo.EventObject} e
5547          */
5548         "beforetoggle" : true
5549     });
5550 };
5551
5552 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5553     
5554     
5555    
5556     // private
5557     navItems : false,
5558     loadMask : false,
5559     
5560     
5561     getAutoCreate : function(){
5562         
5563         
5564         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5565         
5566     },
5567     
5568     initEvents :function ()
5569     {
5570         //Roo.log(this.el.select('.navbar-toggle',true));
5571         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5572         
5573         var mark = {
5574             tag: "div",
5575             cls:"x-dlg-mask"
5576         };
5577         
5578         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5579         
5580         var size = this.el.getSize();
5581         this.maskEl.setSize(size.width, size.height);
5582         this.maskEl.enableDisplayMode("block");
5583         this.maskEl.hide();
5584         
5585         if(this.loadMask){
5586             this.maskEl.show();
5587         }
5588     },
5589     
5590     
5591     getChildContainer : function()
5592     {
5593         if (this.el && this.el.select('.collapse').getCount()) {
5594             return this.el.select('.collapse',true).first();
5595         }
5596         
5597         return this.el;
5598     },
5599     
5600     mask : function()
5601     {
5602         this.maskEl.show();
5603     },
5604     
5605     unmask : function()
5606     {
5607         this.maskEl.hide();
5608     },
5609     onToggle : function()
5610     {
5611         
5612         if(this.fireEvent('beforetoggle', this) === false){
5613             return;
5614         }
5615         var ce = this.el.select('.navbar-collapse',true).first();
5616       
5617         if (!ce.hasClass('show')) {
5618            this.expand();
5619         } else {
5620             this.collapse();
5621         }
5622         
5623         
5624     
5625     },
5626     /**
5627      * Expand the navbar pulldown 
5628      */
5629     expand : function ()
5630     {
5631        
5632         var ce = this.el.select('.navbar-collapse',true).first();
5633         if (ce.hasClass('collapsing')) {
5634             return;
5635         }
5636         ce.dom.style.height = '';
5637                // show it...
5638         ce.addClass('in'); // old...
5639         ce.removeClass('collapse');
5640         ce.addClass('show');
5641         var h = ce.getHeight();
5642         Roo.log(h);
5643         ce.removeClass('show');
5644         // at this point we should be able to see it..
5645         ce.addClass('collapsing');
5646         
5647         ce.setHeight(0); // resize it ...
5648         ce.on('transitionend', function() {
5649             //Roo.log('done transition');
5650             ce.removeClass('collapsing');
5651             ce.addClass('show');
5652             ce.removeClass('collapse');
5653
5654             ce.dom.style.height = '';
5655         }, this, { single: true} );
5656         ce.setHeight(h);
5657         ce.dom.scrollTop = 0;
5658     },
5659     /**
5660      * Collapse the navbar pulldown 
5661      */
5662     collapse : function()
5663     {
5664          var ce = this.el.select('.navbar-collapse',true).first();
5665        
5666         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5667             // it's collapsed or collapsing..
5668             return;
5669         }
5670         ce.removeClass('in'); // old...
5671         ce.setHeight(ce.getHeight());
5672         ce.removeClass('show');
5673         ce.addClass('collapsing');
5674         
5675         ce.on('transitionend', function() {
5676             ce.dom.style.height = '';
5677             ce.removeClass('collapsing');
5678             ce.addClass('collapse');
5679         }, this, { single: true} );
5680         ce.setHeight(0);
5681     }
5682     
5683     
5684     
5685 });
5686
5687
5688
5689  
5690
5691  /*
5692  * - LGPL
5693  *
5694  * navbar
5695  * 
5696  */
5697
5698 /**
5699  * @class Roo.bootstrap.NavSimplebar
5700  * @extends Roo.bootstrap.Navbar
5701  * Bootstrap Sidebar class
5702  *
5703  * @cfg {Boolean} inverse is inverted color
5704  * 
5705  * @cfg {String} type (nav | pills | tabs)
5706  * @cfg {Boolean} arrangement stacked | justified
5707  * @cfg {String} align (left | right) alignment
5708  * 
5709  * @cfg {Boolean} main (true|false) main nav bar? default false
5710  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5711  * 
5712  * @cfg {String} tag (header|footer|nav|div) default is nav 
5713
5714  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5715  * 
5716  * 
5717  * @constructor
5718  * Create a new Sidebar
5719  * @param {Object} config The config object
5720  */
5721
5722
5723 Roo.bootstrap.NavSimplebar = function(config){
5724     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5725 };
5726
5727 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5728     
5729     inverse: false,
5730     
5731     type: false,
5732     arrangement: '',
5733     align : false,
5734     
5735     weight : 'light',
5736     
5737     main : false,
5738     
5739     
5740     tag : false,
5741     
5742     
5743     getAutoCreate : function(){
5744         
5745         
5746         var cfg = {
5747             tag : this.tag || 'div',
5748             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5749         };
5750         if (['light','white'].indexOf(this.weight) > -1) {
5751             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5752         }
5753         cfg.cls += ' bg-' + this.weight;
5754         
5755         if (this.inverse) {
5756             cfg.cls += ' navbar-inverse';
5757             
5758         }
5759         
5760         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5761         
5762         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5763             return cfg;
5764         }
5765         
5766         
5767     
5768         
5769         cfg.cn = [
5770             {
5771                 cls: 'nav nav-' + this.xtype,
5772                 tag : 'ul'
5773             }
5774         ];
5775         
5776          
5777         this.type = this.type || 'nav';
5778         if (['tabs','pills'].indexOf(this.type) != -1) {
5779             cfg.cn[0].cls += ' nav-' + this.type
5780         
5781         
5782         } else {
5783             if (this.type!=='nav') {
5784                 Roo.log('nav type must be nav/tabs/pills')
5785             }
5786             cfg.cn[0].cls += ' navbar-nav'
5787         }
5788         
5789         
5790         
5791         
5792         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5793             cfg.cn[0].cls += ' nav-' + this.arrangement;
5794         }
5795         
5796         
5797         if (this.align === 'right') {
5798             cfg.cn[0].cls += ' navbar-right';
5799         }
5800         
5801         
5802         
5803         
5804         return cfg;
5805     
5806         
5807     }
5808     
5809     
5810     
5811 });
5812
5813
5814
5815  
5816
5817  
5818        /*
5819  * - LGPL
5820  *
5821  * navbar
5822  * navbar-fixed-top
5823  * navbar-expand-md  fixed-top 
5824  */
5825
5826 /**
5827  * @class Roo.bootstrap.NavHeaderbar
5828  * @extends Roo.bootstrap.NavSimplebar
5829  * Bootstrap Sidebar class
5830  *
5831  * @cfg {String} brand what is brand
5832  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5833  * @cfg {String} brand_href href of the brand
5834  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5835  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5836  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5837  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838  * 
5839  * @constructor
5840  * Create a new Sidebar
5841  * @param {Object} config The config object
5842  */
5843
5844
5845 Roo.bootstrap.NavHeaderbar = function(config){
5846     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5847       
5848 };
5849
5850 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5851     
5852     position: '',
5853     brand: '',
5854     brand_href: false,
5855     srButton : true,
5856     autohide : false,
5857     desktopCenter : false,
5858    
5859     
5860     getAutoCreate : function(){
5861         
5862         var   cfg = {
5863             tag: this.nav || 'nav',
5864             cls: 'navbar navbar-expand-md',
5865             role: 'navigation',
5866             cn: []
5867         };
5868         
5869         var cn = cfg.cn;
5870         if (this.desktopCenter) {
5871             cn.push({cls : 'container', cn : []});
5872             cn = cn[0].cn;
5873         }
5874         
5875         if(this.srButton){
5876             var btn = {
5877                 tag: 'button',
5878                 type: 'button',
5879                 cls: 'navbar-toggle navbar-toggler',
5880                 'data-toggle': 'collapse',
5881                 cn: [
5882                     {
5883                         tag: 'span',
5884                         cls: 'sr-only',
5885                         html: 'Toggle navigation'
5886                     },
5887                     {
5888                         tag: 'span',
5889                         cls: 'icon-bar navbar-toggler-icon'
5890                     },
5891                     {
5892                         tag: 'span',
5893                         cls: 'icon-bar'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar'
5898                     }
5899                 ]
5900             };
5901             
5902             cn.push( Roo.bootstrap.version == 4 ? btn : {
5903                 tag: 'div',
5904                 cls: 'navbar-header',
5905                 cn: [
5906                     btn
5907                 ]
5908             });
5909         }
5910         
5911         cn.push({
5912             tag: 'div',
5913             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5914             cn : []
5915         });
5916         
5917         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5918         
5919         if (['light','white'].indexOf(this.weight) > -1) {
5920             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5921         }
5922         cfg.cls += ' bg-' + this.weight;
5923         
5924         
5925         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5926             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5927             
5928             // tag can override this..
5929             
5930             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5931         }
5932         
5933         if (this.brand !== '') {
5934             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5935             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5936                 tag: 'a',
5937                 href: this.brand_href ? this.brand_href : '#',
5938                 cls: 'navbar-brand',
5939                 cn: [
5940                 this.brand
5941                 ]
5942             });
5943         }
5944         
5945         if(this.main){
5946             cfg.cls += ' main-nav';
5947         }
5948         
5949         
5950         return cfg;
5951
5952         
5953     },
5954     getHeaderChildContainer : function()
5955     {
5956         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5957             return this.el.select('.navbar-header',true).first();
5958         }
5959         
5960         return this.getChildContainer();
5961     },
5962     
5963     getChildContainer : function()
5964     {
5965          
5966         return this.el.select('.roo-navbar-collapse',true).first();
5967          
5968         
5969     },
5970     
5971     initEvents : function()
5972     {
5973         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5974         
5975         if (this.autohide) {
5976             
5977             var prevScroll = 0;
5978             var ft = this.el;
5979             
5980             Roo.get(document).on('scroll',function(e) {
5981                 var ns = Roo.get(document).getScroll().top;
5982                 var os = prevScroll;
5983                 prevScroll = ns;
5984                 
5985                 if(ns > os){
5986                     ft.removeClass('slideDown');
5987                     ft.addClass('slideUp');
5988                     return;
5989                 }
5990                 ft.removeClass('slideUp');
5991                 ft.addClass('slideDown');
5992                  
5993               
5994           },this);
5995         }
5996     }    
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * navbar
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavSidebar
6013  * @extends Roo.bootstrap.Navbar
6014  * Bootstrap Sidebar class
6015  * 
6016  * @constructor
6017  * Create a new Sidebar
6018  * @param {Object} config The config object
6019  */
6020
6021
6022 Roo.bootstrap.NavSidebar = function(config){
6023     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6024 };
6025
6026 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6027     
6028     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6029     
6030     getAutoCreate : function(){
6031         
6032         
6033         return  {
6034             tag: 'div',
6035             cls: 'sidebar sidebar-nav'
6036         };
6037     
6038         
6039     }
6040     
6041     
6042     
6043 });
6044
6045
6046
6047  
6048
6049  /*
6050  * - LGPL
6051  *
6052  * nav group
6053  * 
6054  */
6055
6056 /**
6057  * @class Roo.bootstrap.NavGroup
6058  * @extends Roo.bootstrap.Component
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.NavGroup = function(config){
6072     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.NavGroup.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.NavGroup} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.NavItem} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.NavItem} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.NavItem} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.NavItem(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.NavItem} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.NavGroup, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /*
6348  * - LGPL
6349  *
6350  * row
6351  * 
6352  */
6353
6354 /**
6355  * @class Roo.bootstrap.NavItem
6356  * @extends Roo.bootstrap.Component
6357  * Bootstrap Navbar.NavItem class
6358  * @cfg {String} href  link to
6359  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6360  * @cfg {Boolean} button_outline show and outlined button
6361  * @cfg {String} html content of button
6362  * @cfg {String} badge text inside badge
6363  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6364  * @cfg {String} glyphicon DEPRICATED - use fa
6365  * @cfg {String} icon DEPRICATED - use fa
6366  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6367  * @cfg {Boolean} active Is item active
6368  * @cfg {Boolean} disabled Is item disabled
6369  * @cfg {String} linkcls  Link Class
6370  * @cfg {Boolean} preventDefault (true | false) default false
6371  * @cfg {String} tabId the tab that this item activates.
6372  * @cfg {String} tagtype (a|span) render as a href or span?
6373  * @cfg {Boolean} animateRef (true|false) link to element default false  
6374   
6375  * @constructor
6376  * Create a new Navbar Item
6377  * @param {Object} config The config object
6378  */
6379 Roo.bootstrap.NavItem = function(config){
6380     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6381     this.addEvents({
6382         // raw events
6383         /**
6384          * @event click
6385          * The raw click event for the entire grid.
6386          * @param {Roo.EventObject} e
6387          */
6388         "click" : true,
6389          /**
6390             * @event changed
6391             * Fires when the active item active state changes
6392             * @param {Roo.bootstrap.NavItem} this
6393             * @param {boolean} state the new state
6394              
6395          */
6396         'changed': true,
6397         /**
6398             * @event scrollto
6399             * Fires when scroll to element
6400             * @param {Roo.bootstrap.NavItem} this
6401             * @param {Object} options
6402             * @param {Roo.EventObject} e
6403              
6404          */
6405         'scrollto': true
6406     });
6407    
6408 };
6409
6410 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6411     
6412     href: false,
6413     html: '',
6414     badge: '',
6415     icon: false,
6416     fa : false,
6417     glyphicon: false,
6418     active: false,
6419     preventDefault : false,
6420     tabId : false,
6421     tagtype : 'a',
6422     tag: 'li',
6423     disabled : false,
6424     animateRef : false,
6425     was_active : false,
6426     button_weight : '',
6427     button_outline : false,
6428     linkcls : '',
6429     navLink: false,
6430     
6431     getAutoCreate : function(){
6432          
6433         var cfg = {
6434             tag: this.tag,
6435             cls: 'nav-item'
6436         };
6437         
6438         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6439         
6440         if (this.active) {
6441             cfg.cls +=  ' active' ;
6442         }
6443         if (this.disabled) {
6444             cfg.cls += ' disabled';
6445         }
6446         
6447         // BS4 only?
6448         if (this.button_weight.length) {
6449             cfg.tag = this.href ? 'a' : 'button';
6450             cfg.html = this.html || '';
6451             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6452             if (this.href) {
6453                 cfg.href = this.href;
6454             }
6455             if (this.fa) {
6456                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6457             } else {
6458                 cfg.cls += " nav-html";
6459             }
6460             
6461             // menu .. should add dropdown-menu class - so no need for carat..
6462             
6463             if (this.badge !== '') {
6464                  
6465                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6466             }
6467             return cfg;
6468         }
6469         
6470         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6471             cfg.cn = [
6472                 {
6473                     tag: this.tagtype,
6474                     href : this.href || "#",
6475                     html: this.html || '',
6476                     cls : ''
6477                 }
6478             ];
6479             if (this.tagtype == 'a') {
6480                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6481         
6482             }
6483             if (this.icon) {
6484                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else  if (this.fa) {
6486                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else if(this.glyphicon) {
6488                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6489             } else {
6490                 cfg.cn[0].cls += " nav-html";
6491             }
6492             
6493             if (this.menu) {
6494                 cfg.cn[0].html += " <span class='caret'></span>";
6495              
6496             }
6497             
6498             if (this.badge !== '') {
6499                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6500             }
6501         }
6502         
6503         
6504         
6505         return cfg;
6506     },
6507     onRender : function(ct, position)
6508     {
6509        // Roo.log("Call onRender: " + this.xtype);
6510         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6511             this.tag = 'div';
6512         }
6513         
6514         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6515         this.navLink = this.el.select('.nav-link',true).first();
6516         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6517         return ret;
6518     },
6519       
6520     
6521     initEvents: function() 
6522     {
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         //if(this.tagtype == 'span'){
6532         //    this.el.select('span',true).on('click', this.onClick, this);
6533         //}
6534        
6535         // at this point parent should be available..
6536         this.parent().register(this);
6537     },
6538     
6539     onClick : function(e)
6540     {
6541         if (e.getTarget('.dropdown-menu-item')) {
6542             // did you click on a menu itemm.... - then don't trigger onclick..
6543             return;
6544         }
6545         
6546         if(
6547                 this.preventDefault || 
6548                 this.href == '#' 
6549         ){
6550             Roo.log("NavItem - prevent Default?");
6551             e.preventDefault();
6552         }
6553         
6554         if (this.disabled) {
6555             return;
6556         }
6557         
6558         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559         if (tg && tg.transition) {
6560             Roo.log("waiting for the transitionend");
6561             return;
6562         }
6563         
6564         
6565         
6566         //Roo.log("fire event clicked");
6567         if(this.fireEvent('click', this, e) === false){
6568             return;
6569         };
6570         
6571         if(this.tagtype == 'span'){
6572             return;
6573         }
6574         
6575         //Roo.log(this.href);
6576         var ael = this.el.select('a',true).first();
6577         //Roo.log(ael);
6578         
6579         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6580             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6581             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6582                 return; // ignore... - it's a 'hash' to another page.
6583             }
6584             Roo.log("NavItem - prevent Default?");
6585             e.preventDefault();
6586             this.scrollToElement(e);
6587         }
6588         
6589         
6590         var p =  this.parent();
6591    
6592         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6593             if (typeof(p.setActiveItem) !== 'undefined') {
6594                 p.setActiveItem(this);
6595             }
6596         }
6597         
6598         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6599         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6600             // remove the collapsed menu expand...
6601             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6602         }
6603     },
6604     
6605     isActive: function () {
6606         return this.active
6607     },
6608     setActive : function(state, fire, is_was_active)
6609     {
6610         if (this.active && !state && this.navId) {
6611             this.was_active = true;
6612             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6613             if (nv) {
6614                 nv.clearWasActive(this);
6615             }
6616             
6617         }
6618         this.active = state;
6619         
6620         if (!state ) {
6621             this.el.removeClass('active');
6622             this.navLink ? this.navLink.removeClass('active') : false;
6623         } else if (!this.el.hasClass('active')) {
6624             
6625             this.el.addClass('active');
6626             if (Roo.bootstrap.version == 4 && this.navLink ) {
6627                 this.navLink.addClass('active');
6628             }
6629             
6630         }
6631         if (fire) {
6632             this.fireEvent('changed', this, state);
6633         }
6634         
6635         // show a panel if it's registered and related..
6636         
6637         if (!this.navId || !this.tabId || !state || is_was_active) {
6638             return;
6639         }
6640         
6641         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6642         if (!tg) {
6643             return;
6644         }
6645         var pan = tg.getPanelByName(this.tabId);
6646         if (!pan) {
6647             return;
6648         }
6649         // if we can not flip to new panel - go back to old nav highlight..
6650         if (false == tg.showPanel(pan)) {
6651             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6652             if (nv) {
6653                 var onav = nv.getWasActive();
6654                 if (onav) {
6655                     onav.setActive(true, false, true);
6656                 }
6657             }
6658             
6659         }
6660         
6661         
6662         
6663     },
6664      // this should not be here...
6665     setDisabled : function(state)
6666     {
6667         this.disabled = state;
6668         if (!state ) {
6669             this.el.removeClass('disabled');
6670         } else if (!this.el.hasClass('disabled')) {
6671             this.el.addClass('disabled');
6672         }
6673         
6674     },
6675     
6676     /**
6677      * Fetch the element to display the tooltip on.
6678      * @return {Roo.Element} defaults to this.el
6679      */
6680     tooltipEl : function()
6681     {
6682         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6683     },
6684     
6685     scrollToElement : function(e)
6686     {
6687         var c = document.body;
6688         
6689         /*
6690          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6691          */
6692         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6693             c = document.documentElement;
6694         }
6695         
6696         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6697         
6698         if(!target){
6699             return;
6700         }
6701
6702         var o = target.calcOffsetsTo(c);
6703         
6704         var options = {
6705             target : target,
6706             value : o[1]
6707         };
6708         
6709         this.fireEvent('scrollto', this, options, e);
6710         
6711         Roo.get(c).scrollTo('top', options.value, true);
6712         
6713         return;
6714     },
6715     /**
6716      * Set the HTML (text content) of the item
6717      * @param {string} html  content for the nav item
6718      */
6719     setHtml : function(html)
6720     {
6721         this.html = html;
6722         this.htmlEl.dom.innerHTML = html;
6723         
6724     } 
6725 });
6726  
6727
6728  /*
6729  * - LGPL
6730  *
6731  * sidebar item
6732  *
6733  *  li
6734  *    <span> icon </span>
6735  *    <span> text </span>
6736  *    <span>badge </span>
6737  */
6738
6739 /**
6740  * @class Roo.bootstrap.NavSidebarItem
6741  * @extends Roo.bootstrap.NavItem
6742  * Bootstrap Navbar.NavSidebarItem class
6743  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6744  * {Boolean} open is the menu open
6745  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6746  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6747  * {String} buttonSize (sm|md|lg)the extra classes for the button
6748  * {Boolean} showArrow show arrow next to the text (default true)
6749  * @constructor
6750  * Create a new Navbar Button
6751  * @param {Object} config The config object
6752  */
6753 Roo.bootstrap.NavSidebarItem = function(config){
6754     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6755     this.addEvents({
6756         // raw events
6757         /**
6758          * @event click
6759          * The raw click event for the entire grid.
6760          * @param {Roo.EventObject} e
6761          */
6762         "click" : true,
6763          /**
6764             * @event changed
6765             * Fires when the active item active state changes
6766             * @param {Roo.bootstrap.NavSidebarItem} this
6767             * @param {boolean} state the new state
6768              
6769          */
6770         'changed': true
6771     });
6772    
6773 };
6774
6775 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6776     
6777     badgeWeight : 'default',
6778     
6779     open: false,
6780     
6781     buttonView : false,
6782     
6783     buttonWeight : 'default',
6784     
6785     buttonSize : 'md',
6786     
6787     showArrow : true,
6788     
6789     getAutoCreate : function(){
6790         
6791         
6792         var a = {
6793                 tag: 'a',
6794                 href : this.href || '#',
6795                 cls: '',
6796                 html : '',
6797                 cn : []
6798         };
6799         
6800         if(this.buttonView){
6801             a = {
6802                 tag: 'button',
6803                 href : this.href || '#',
6804                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6805                 html : this.html,
6806                 cn : []
6807             };
6808         }
6809         
6810         var cfg = {
6811             tag: 'li',
6812             cls: '',
6813             cn: [ a ]
6814         };
6815         
6816         if (this.active) {
6817             cfg.cls += ' active';
6818         }
6819         
6820         if (this.disabled) {
6821             cfg.cls += ' disabled';
6822         }
6823         if (this.open) {
6824             cfg.cls += ' open x-open';
6825         }
6826         // left icon..
6827         if (this.glyphicon || this.icon) {
6828             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6829             a.cn.push({ tag : 'i', cls : c }) ;
6830         }
6831         
6832         if(!this.buttonView){
6833             var span = {
6834                 tag: 'span',
6835                 html : this.html || ''
6836             };
6837
6838             a.cn.push(span);
6839             
6840         }
6841         
6842         if (this.badge !== '') {
6843             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6844         }
6845         
6846         if (this.menu) {
6847             
6848             if(this.showArrow){
6849                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6850             }
6851             
6852             a.cls += ' dropdown-toggle treeview' ;
6853         }
6854         
6855         return cfg;
6856     },
6857     
6858     initEvents : function()
6859     { 
6860         if (typeof (this.menu) != 'undefined') {
6861             this.menu.parentType = this.xtype;
6862             this.menu.triggerEl = this.el;
6863             this.menu = this.addxtype(Roo.apply({}, this.menu));
6864         }
6865         
6866         this.el.on('click', this.onClick, this);
6867         
6868         if(this.badge !== ''){
6869             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6870         }
6871         
6872     },
6873     
6874     onClick : function(e)
6875     {
6876         if(this.disabled){
6877             e.preventDefault();
6878             return;
6879         }
6880         
6881         if(this.preventDefault){
6882             e.preventDefault();
6883         }
6884         
6885         this.fireEvent('click', this, e);
6886     },
6887     
6888     disable : function()
6889     {
6890         this.setDisabled(true);
6891     },
6892     
6893     enable : function()
6894     {
6895         this.setDisabled(false);
6896     },
6897     
6898     setDisabled : function(state)
6899     {
6900         if(this.disabled == state){
6901             return;
6902         }
6903         
6904         this.disabled = state;
6905         
6906         if (state) {
6907             this.el.addClass('disabled');
6908             return;
6909         }
6910         
6911         this.el.removeClass('disabled');
6912         
6913         return;
6914     },
6915     
6916     setActive : function(state)
6917     {
6918         if(this.active == state){
6919             return;
6920         }
6921         
6922         this.active = state;
6923         
6924         if (state) {
6925             this.el.addClass('active');
6926             return;
6927         }
6928         
6929         this.el.removeClass('active');
6930         
6931         return;
6932     },
6933     
6934     isActive: function () 
6935     {
6936         return this.active;
6937     },
6938     
6939     setBadge : function(str)
6940     {
6941         if(!this.badgeEl){
6942             return;
6943         }
6944         
6945         this.badgeEl.dom.innerHTML = str;
6946     }
6947     
6948    
6949      
6950  
6951 });
6952  
6953
6954  /*
6955  * - LGPL
6956  *
6957  *  Breadcrumb Nav
6958  * 
6959  */
6960 Roo.namespace('Roo.bootstrap.breadcrumb');
6961
6962
6963 /**
6964  * @class Roo.bootstrap.breadcrumb.Nav
6965  * @extends Roo.bootstrap.Component
6966  * Bootstrap Breadcrumb Nav Class
6967  *  
6968  * @children Roo.bootstrap.breadcrumb.Item
6969  * 
6970  * @constructor
6971  * Create a new breadcrumb.Nav
6972  * @param {Object} config The config object
6973  */
6974
6975
6976 Roo.bootstrap.breadcrumb.Nav = function(config){
6977     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6978     
6979     
6980 };
6981
6982 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6983     
6984     getAutoCreate : function()
6985     {
6986
6987         var cfg = {
6988             tag: 'nav',
6989             cn : [
6990                 {
6991                     tag : 'ol',
6992                     cls : 'breadcrumb'
6993                 }
6994             ]
6995             
6996         };
6997           
6998         return cfg;
6999     },
7000     
7001     initEvents: function()
7002     {
7003         this.olEl = this.el.select('ol',true).first();    
7004     },
7005     getChildContainer : function()
7006     {
7007         return this.olEl;  
7008     }
7009     
7010 });
7011
7012  /*
7013  * - LGPL
7014  *
7015  *  Breadcrumb Item
7016  * 
7017  */
7018
7019
7020 /**
7021  * @class Roo.bootstrap.breadcrumb.Nav
7022  * @extends Roo.bootstrap.Component
7023  * Bootstrap Breadcrumb Nav Class
7024  *  
7025  * @children Roo.bootstrap.breadcrumb.Component
7026  * @cfg {String} html the content of the link.
7027  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7028  * @cfg {Boolean} active is it active
7029
7030  * 
7031  * @constructor
7032  * Create a new breadcrumb.Nav
7033  * @param {Object} config The config object
7034  */
7035
7036 Roo.bootstrap.breadcrumb.Item = function(config){
7037     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7038     this.addEvents({
7039         // img events
7040         /**
7041          * @event click
7042          * The img click event for the img.
7043          * @param {Roo.EventObject} e
7044          */
7045         "click" : true
7046     });
7047     
7048 };
7049
7050 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7051     
7052     href: false,
7053     html : '',
7054     
7055     getAutoCreate : function()
7056     {
7057
7058         var cfg = {
7059             tag: 'li',
7060             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7061         };
7062         if (this.href !== false) {
7063             cfg.cn = [{
7064                 tag : 'a',
7065                 href : this.href,
7066                 html : this.html
7067             }];
7068         } else {
7069             cfg.html = this.html;
7070         }
7071         
7072         return cfg;
7073     },
7074     
7075     initEvents: function()
7076     {
7077         if (this.href) {
7078             this.el.select('a', true).first().on('click',this.onClick, this)
7079         }
7080         
7081     },
7082     onClick : function(e)
7083     {
7084         e.preventDefault();
7085         this.fireEvent('click',this,  e);
7086     }
7087     
7088 });
7089
7090  /*
7091  * - LGPL
7092  *
7093  * row
7094  * 
7095  */
7096
7097 /**
7098  * @class Roo.bootstrap.Row
7099  * @extends Roo.bootstrap.Component
7100  * @children Roo.bootstrap.Component
7101  * Bootstrap Row class (contains columns...)
7102  * 
7103  * @constructor
7104  * Create a new Row
7105  * @param {Object} config The config object
7106  */
7107
7108 Roo.bootstrap.Row = function(config){
7109     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7110 };
7111
7112 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7113     
7114     getAutoCreate : function(){
7115        return {
7116             cls: 'row clearfix'
7117        };
7118     }
7119     
7120     
7121 });
7122
7123  
7124
7125  /*
7126  * - LGPL
7127  *
7128  * pagination
7129  * 
7130  */
7131
7132 /**
7133  * @class Roo.bootstrap.Pagination
7134  * @extends Roo.bootstrap.Component
7135  * Bootstrap Pagination class
7136  * @cfg {String} size xs | sm | md | lg
7137  * @cfg {Boolean} inverse false | true
7138  * 
7139  * @constructor
7140  * Create a new Pagination
7141  * @param {Object} config The config object
7142  */
7143
7144 Roo.bootstrap.Pagination = function(config){
7145     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7146 };
7147
7148 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7149     
7150     cls: false,
7151     size: false,
7152     inverse: false,
7153     
7154     getAutoCreate : function(){
7155         var cfg = {
7156             tag: 'ul',
7157                 cls: 'pagination'
7158         };
7159         if (this.inverse) {
7160             cfg.cls += ' inverse';
7161         }
7162         if (this.html) {
7163             cfg.html=this.html;
7164         }
7165         if (this.cls) {
7166             cfg.cls += " " + this.cls;
7167         }
7168         return cfg;
7169     }
7170    
7171 });
7172
7173  
7174
7175  /*
7176  * - LGPL
7177  *
7178  * Pagination item
7179  * 
7180  */
7181
7182
7183 /**
7184  * @class Roo.bootstrap.PaginationItem
7185  * @extends Roo.bootstrap.Component
7186  * Bootstrap PaginationItem class
7187  * @cfg {String} html text
7188  * @cfg {String} href the link
7189  * @cfg {Boolean} preventDefault (true | false) default true
7190  * @cfg {Boolean} active (true | false) default false
7191  * @cfg {Boolean} disabled default false
7192  * 
7193  * 
7194  * @constructor
7195  * Create a new PaginationItem
7196  * @param {Object} config The config object
7197  */
7198
7199
7200 Roo.bootstrap.PaginationItem = function(config){
7201     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7202     this.addEvents({
7203         // raw events
7204         /**
7205          * @event click
7206          * The raw click event for the entire grid.
7207          * @param {Roo.EventObject} e
7208          */
7209         "click" : true
7210     });
7211 };
7212
7213 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7214     
7215     href : false,
7216     html : false,
7217     preventDefault: true,
7218     active : false,
7219     cls : false,
7220     disabled: false,
7221     
7222     getAutoCreate : function(){
7223         var cfg= {
7224             tag: 'li',
7225             cn: [
7226                 {
7227                     tag : 'a',
7228                     href : this.href ? this.href : '#',
7229                     html : this.html ? this.html : ''
7230                 }
7231             ]
7232         };
7233         
7234         if(this.cls){
7235             cfg.cls = this.cls;
7236         }
7237         
7238         if(this.disabled){
7239             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7240         }
7241         
7242         if(this.active){
7243             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7244         }
7245         
7246         return cfg;
7247     },
7248     
7249     initEvents: function() {
7250         
7251         this.el.on('click', this.onClick, this);
7252         
7253     },
7254     onClick : function(e)
7255     {
7256         Roo.log('PaginationItem on click ');
7257         if(this.preventDefault){
7258             e.preventDefault();
7259         }
7260         
7261         if(this.disabled){
7262             return;
7263         }
7264         
7265         this.fireEvent('click', this, e);
7266     }
7267    
7268 });
7269
7270  
7271
7272  /*
7273  * - LGPL
7274  *
7275  * slider
7276  * 
7277  */
7278
7279
7280 /**
7281  * @class Roo.bootstrap.Slider
7282  * @extends Roo.bootstrap.Component
7283  * Bootstrap Slider class
7284  *    
7285  * @constructor
7286  * Create a new Slider
7287  * @param {Object} config The config object
7288  */
7289
7290 Roo.bootstrap.Slider = function(config){
7291     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7292 };
7293
7294 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7295     
7296     getAutoCreate : function(){
7297         
7298         var cfg = {
7299             tag: 'div',
7300             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7301             cn: [
7302                 {
7303                     tag: 'a',
7304                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7305                 }
7306             ]
7307         };
7308         
7309         return cfg;
7310     }
7311    
7312 });
7313
7314  /*
7315  * Based on:
7316  * Ext JS Library 1.1.1
7317  * Copyright(c) 2006-2007, Ext JS, LLC.
7318  *
7319  * Originally Released Under LGPL - original licence link has changed is not relivant.
7320  *
7321  * Fork - LGPL
7322  * <script type="text/javascript">
7323  */
7324  /**
7325  * @extends Roo.dd.DDProxy
7326  * @class Roo.grid.SplitDragZone
7327  * Support for Column Header resizing
7328  * @constructor
7329  * @param {Object} config
7330  */
7331 // private
7332 // This is a support class used internally by the Grid components
7333 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7334     this.grid = grid;
7335     this.view = grid.getView();
7336     this.proxy = this.view.resizeProxy;
7337     Roo.grid.SplitDragZone.superclass.constructor.call(
7338         this,
7339         hd, // ID
7340         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7341         {  // CONFIG
7342             dragElId : Roo.id(this.proxy.dom),
7343             resizeFrame:false
7344         }
7345     );
7346     
7347     this.setHandleElId(Roo.id(hd));
7348     if (hd2 !== false) {
7349         this.setOuterHandleElId(Roo.id(hd2));
7350     }
7351     
7352     this.scroll = false;
7353 };
7354 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7355     fly: Roo.Element.fly,
7356
7357     b4StartDrag : function(x, y){
7358         this.view.headersDisabled = true;
7359         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7360                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7361         );
7362         this.proxy.setHeight(h);
7363         
7364         // for old system colWidth really stored the actual width?
7365         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7366         // which in reality did not work.. - it worked only for fixed sizes
7367         // for resizable we need to use actual sizes.
7368         var w = this.cm.getColumnWidth(this.cellIndex);
7369         if (!this.view.mainWrap) {
7370             // bootstrap.
7371             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7372         }
7373         
7374         
7375         
7376         // this was w-this.grid.minColumnWidth;
7377         // doesnt really make sense? - w = thie curren width or the rendered one?
7378         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7379         this.resetConstraints();
7380         this.setXConstraint(minw, 1000);
7381         this.setYConstraint(0, 0);
7382         this.minX = x - minw;
7383         this.maxX = x + 1000;
7384         this.startPos = x;
7385         if (!this.view.mainWrap) { // this is Bootstrap code..
7386             this.getDragEl().style.display='block';
7387         }
7388         
7389         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7390     },
7391
7392
7393     handleMouseDown : function(e){
7394         ev = Roo.EventObject.setEvent(e);
7395         var t = this.fly(ev.getTarget());
7396         if(t.hasClass("x-grid-split")){
7397             this.cellIndex = this.view.getCellIndex(t.dom);
7398             this.split = t.dom;
7399             this.cm = this.grid.colModel;
7400             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7401                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7402             }
7403         }
7404     },
7405
7406     endDrag : function(e){
7407         this.view.headersDisabled = false;
7408         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7409         var diff = endX - this.startPos;
7410         // 
7411         var w = this.cm.getColumnWidth(this.cellIndex);
7412         if (!this.view.mainWrap) {
7413             w = 0;
7414         }
7415         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7416     },
7417
7418     autoOffset : function(){
7419         this.setDelta(0,0);
7420     }
7421 });/*
7422  * Based on:
7423  * Ext JS Library 1.1.1
7424  * Copyright(c) 2006-2007, Ext JS, LLC.
7425  *
7426  * Originally Released Under LGPL - original licence link has changed is not relivant.
7427  *
7428  * Fork - LGPL
7429  * <script type="text/javascript">
7430  */
7431
7432 /**
7433  * @class Roo.grid.AbstractSelectionModel
7434  * @extends Roo.util.Observable
7435  * @abstract
7436  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7437  * implemented by descendant classes.  This class should not be directly instantiated.
7438  * @constructor
7439  */
7440 Roo.grid.AbstractSelectionModel = function(){
7441     this.locked = false;
7442     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7443 };
7444
7445 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7446     /** @ignore Called by the grid automatically. Do not call directly. */
7447     init : function(grid){
7448         this.grid = grid;
7449         this.initEvents();
7450     },
7451
7452     /**
7453      * Locks the selections.
7454      */
7455     lock : function(){
7456         this.locked = true;
7457     },
7458
7459     /**
7460      * Unlocks the selections.
7461      */
7462     unlock : function(){
7463         this.locked = false;
7464     },
7465
7466     /**
7467      * Returns true if the selections are locked.
7468      * @return {Boolean}
7469      */
7470     isLocked : function(){
7471         return this.locked;
7472     }
7473 });/*
7474  * Based on:
7475  * Ext JS Library 1.1.1
7476  * Copyright(c) 2006-2007, Ext JS, LLC.
7477  *
7478  * Originally Released Under LGPL - original licence link has changed is not relivant.
7479  *
7480  * Fork - LGPL
7481  * <script type="text/javascript">
7482  */
7483 /**
7484  * @extends Roo.grid.AbstractSelectionModel
7485  * @class Roo.grid.RowSelectionModel
7486  * The default SelectionModel used by {@link Roo.grid.Grid}.
7487  * It supports multiple selections and keyboard selection/navigation. 
7488  * @constructor
7489  * @param {Object} config
7490  */
7491 Roo.grid.RowSelectionModel = function(config){
7492     Roo.apply(this, config);
7493     this.selections = new Roo.util.MixedCollection(false, function(o){
7494         return o.id;
7495     });
7496
7497     this.last = false;
7498     this.lastActive = false;
7499
7500     this.addEvents({
7501         /**
7502         * @event selectionchange
7503         * Fires when the selection changes
7504         * @param {SelectionModel} this
7505         */
7506        "selectionchange" : true,
7507        /**
7508         * @event afterselectionchange
7509         * Fires after the selection changes (eg. by key press or clicking)
7510         * @param {SelectionModel} this
7511         */
7512        "afterselectionchange" : true,
7513        /**
7514         * @event beforerowselect
7515         * Fires when a row is selected being selected, return false to cancel.
7516         * @param {SelectionModel} this
7517         * @param {Number} rowIndex The selected index
7518         * @param {Boolean} keepExisting False if other selections will be cleared
7519         */
7520        "beforerowselect" : true,
7521        /**
7522         * @event rowselect
7523         * Fires when a row is selected.
7524         * @param {SelectionModel} this
7525         * @param {Number} rowIndex The selected index
7526         * @param {Roo.data.Record} r The record
7527         */
7528        "rowselect" : true,
7529        /**
7530         * @event rowdeselect
7531         * Fires when a row is deselected.
7532         * @param {SelectionModel} this
7533         * @param {Number} rowIndex The selected index
7534         */
7535         "rowdeselect" : true
7536     });
7537     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7538     this.locked = false;
7539 };
7540
7541 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7542     /**
7543      * @cfg {Boolean} singleSelect
7544      * True to allow selection of only one row at a time (defaults to false)
7545      */
7546     singleSelect : false,
7547
7548     // private
7549     initEvents : function(){
7550
7551         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7552             this.grid.on("mousedown", this.handleMouseDown, this);
7553         }else{ // allow click to work like normal
7554             this.grid.on("rowclick", this.handleDragableRowClick, this);
7555         }
7556         // bootstrap does not have a view..
7557         var view = this.grid.view ? this.grid.view : this.grid;
7558         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7559             "up" : function(e){
7560                 if(!e.shiftKey){
7561                     this.selectPrevious(e.shiftKey);
7562                 }else if(this.last !== false && this.lastActive !== false){
7563                     var last = this.last;
7564                     this.selectRange(this.last,  this.lastActive-1);
7565                     view.focusRow(this.lastActive);
7566                     if(last !== false){
7567                         this.last = last;
7568                     }
7569                 }else{
7570                     this.selectFirstRow();
7571                 }
7572                 this.fireEvent("afterselectionchange", this);
7573             },
7574             "down" : function(e){
7575                 if(!e.shiftKey){
7576                     this.selectNext(e.shiftKey);
7577                 }else if(this.last !== false && this.lastActive !== false){
7578                     var last = this.last;
7579                     this.selectRange(this.last,  this.lastActive+1);
7580                     view.focusRow(this.lastActive);
7581                     if(last !== false){
7582                         this.last = last;
7583                     }
7584                 }else{
7585                     this.selectFirstRow();
7586                 }
7587                 this.fireEvent("afterselectionchange", this);
7588             },
7589             scope: this
7590         });
7591
7592          
7593         view.on("refresh", this.onRefresh, this);
7594         view.on("rowupdated", this.onRowUpdated, this);
7595         view.on("rowremoved", this.onRemove, this);
7596     },
7597
7598     // private
7599     onRefresh : function(){
7600         var ds = this.grid.ds, i, v = this.grid.view;
7601         var s = this.selections;
7602         s.each(function(r){
7603             if((i = ds.indexOfId(r.id)) != -1){
7604                 v.onRowSelect(i);
7605                 s.add(ds.getAt(i)); // updating the selection relate data
7606             }else{
7607                 s.remove(r);
7608             }
7609         });
7610     },
7611
7612     // private
7613     onRemove : function(v, index, r){
7614         this.selections.remove(r);
7615     },
7616
7617     // private
7618     onRowUpdated : function(v, index, r){
7619         if(this.isSelected(r)){
7620             v.onRowSelect(index);
7621         }
7622     },
7623
7624     /**
7625      * Select records.
7626      * @param {Array} records The records to select
7627      * @param {Boolean} keepExisting (optional) True to keep existing selections
7628      */
7629     selectRecords : function(records, keepExisting){
7630         if(!keepExisting){
7631             this.clearSelections();
7632         }
7633         var ds = this.grid.ds;
7634         for(var i = 0, len = records.length; i < len; i++){
7635             this.selectRow(ds.indexOf(records[i]), true);
7636         }
7637     },
7638
7639     /**
7640      * Gets the number of selected rows.
7641      * @return {Number}
7642      */
7643     getCount : function(){
7644         return this.selections.length;
7645     },
7646
7647     /**
7648      * Selects the first row in the grid.
7649      */
7650     selectFirstRow : function(){
7651         this.selectRow(0);
7652     },
7653
7654     /**
7655      * Select the last row.
7656      * @param {Boolean} keepExisting (optional) True to keep existing selections
7657      */
7658     selectLastRow : function(keepExisting){
7659         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7660     },
7661
7662     /**
7663      * Selects the row immediately following the last selected row.
7664      * @param {Boolean} keepExisting (optional) True to keep existing selections
7665      */
7666     selectNext : function(keepExisting){
7667         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7668             this.selectRow(this.last+1, keepExisting);
7669             var view = this.grid.view ? this.grid.view : this.grid;
7670             view.focusRow(this.last);
7671         }
7672     },
7673
7674     /**
7675      * Selects the row that precedes the last selected row.
7676      * @param {Boolean} keepExisting (optional) True to keep existing selections
7677      */
7678     selectPrevious : function(keepExisting){
7679         if(this.last){
7680             this.selectRow(this.last-1, keepExisting);
7681             var view = this.grid.view ? this.grid.view : this.grid;
7682             view.focusRow(this.last);
7683         }
7684     },
7685
7686     /**
7687      * Returns the selected records
7688      * @return {Array} Array of selected records
7689      */
7690     getSelections : function(){
7691         return [].concat(this.selections.items);
7692     },
7693
7694     /**
7695      * Returns the first selected record.
7696      * @return {Record}
7697      */
7698     getSelected : function(){
7699         return this.selections.itemAt(0);
7700     },
7701
7702
7703     /**
7704      * Clears all selections.
7705      */
7706     clearSelections : function(fast){
7707         if(this.locked) {
7708             return;
7709         }
7710         if(fast !== true){
7711             var ds = this.grid.ds;
7712             var s = this.selections;
7713             s.each(function(r){
7714                 this.deselectRow(ds.indexOfId(r.id));
7715             }, this);
7716             s.clear();
7717         }else{
7718             this.selections.clear();
7719         }
7720         this.last = false;
7721     },
7722
7723
7724     /**
7725      * Selects all rows.
7726      */
7727     selectAll : function(){
7728         if(this.locked) {
7729             return;
7730         }
7731         this.selections.clear();
7732         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7733             this.selectRow(i, true);
7734         }
7735     },
7736
7737     /**
7738      * Returns True if there is a selection.
7739      * @return {Boolean}
7740      */
7741     hasSelection : function(){
7742         return this.selections.length > 0;
7743     },
7744
7745     /**
7746      * Returns True if the specified row is selected.
7747      * @param {Number/Record} record The record or index of the record to check
7748      * @return {Boolean}
7749      */
7750     isSelected : function(index){
7751         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7752         return (r && this.selections.key(r.id) ? true : false);
7753     },
7754
7755     /**
7756      * Returns True if the specified record id is selected.
7757      * @param {String} id The id of record to check
7758      * @return {Boolean}
7759      */
7760     isIdSelected : function(id){
7761         return (this.selections.key(id) ? true : false);
7762     },
7763
7764     // private
7765     handleMouseDown : function(e, t)
7766     {
7767         var view = this.grid.view ? this.grid.view : this.grid;
7768         var rowIndex;
7769         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7770             return;
7771         };
7772         if(e.shiftKey && this.last !== false){
7773             var last = this.last;
7774             this.selectRange(last, rowIndex, e.ctrlKey);
7775             this.last = last; // reset the last
7776             view.focusRow(rowIndex);
7777         }else{
7778             var isSelected = this.isSelected(rowIndex);
7779             if(e.button !== 0 && isSelected){
7780                 view.focusRow(rowIndex);
7781             }else if(e.ctrlKey && isSelected){
7782                 this.deselectRow(rowIndex);
7783             }else if(!isSelected){
7784                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7785                 view.focusRow(rowIndex);
7786             }
7787         }
7788         this.fireEvent("afterselectionchange", this);
7789     },
7790     // private
7791     handleDragableRowClick :  function(grid, rowIndex, e) 
7792     {
7793         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7794             this.selectRow(rowIndex, false);
7795             var view = this.grid.view ? this.grid.view : this.grid;
7796             view.focusRow(rowIndex);
7797              this.fireEvent("afterselectionchange", this);
7798         }
7799     },
7800     
7801     /**
7802      * Selects multiple rows.
7803      * @param {Array} rows Array of the indexes of the row to select
7804      * @param {Boolean} keepExisting (optional) True to keep existing selections
7805      */
7806     selectRows : function(rows, keepExisting){
7807         if(!keepExisting){
7808             this.clearSelections();
7809         }
7810         for(var i = 0, len = rows.length; i < len; i++){
7811             this.selectRow(rows[i], true);
7812         }
7813     },
7814
7815     /**
7816      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7817      * @param {Number} startRow The index of the first row in the range
7818      * @param {Number} endRow The index of the last row in the range
7819      * @param {Boolean} keepExisting (optional) True to retain existing selections
7820      */
7821     selectRange : function(startRow, endRow, keepExisting){
7822         if(this.locked) {
7823             return;
7824         }
7825         if(!keepExisting){
7826             this.clearSelections();
7827         }
7828         if(startRow <= endRow){
7829             for(var i = startRow; i <= endRow; i++){
7830                 this.selectRow(i, true);
7831             }
7832         }else{
7833             for(var i = startRow; i >= endRow; i--){
7834                 this.selectRow(i, true);
7835             }
7836         }
7837     },
7838
7839     /**
7840      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7841      * @param {Number} startRow The index of the first row in the range
7842      * @param {Number} endRow The index of the last row in the range
7843      */
7844     deselectRange : function(startRow, endRow, preventViewNotify){
7845         if(this.locked) {
7846             return;
7847         }
7848         for(var i = startRow; i <= endRow; i++){
7849             this.deselectRow(i, preventViewNotify);
7850         }
7851     },
7852
7853     /**
7854      * Selects a row.
7855      * @param {Number} row The index of the row to select
7856      * @param {Boolean} keepExisting (optional) True to keep existing selections
7857      */
7858     selectRow : function(index, keepExisting, preventViewNotify){
7859         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7860             return;
7861         }
7862         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7863             if(!keepExisting || this.singleSelect){
7864                 this.clearSelections();
7865             }
7866             var r = this.grid.ds.getAt(index);
7867             this.selections.add(r);
7868             this.last = this.lastActive = index;
7869             if(!preventViewNotify){
7870                 var view = this.grid.view ? this.grid.view : this.grid;
7871                 view.onRowSelect(index);
7872             }
7873             this.fireEvent("rowselect", this, index, r);
7874             this.fireEvent("selectionchange", this);
7875         }
7876     },
7877
7878     /**
7879      * Deselects a row.
7880      * @param {Number} row The index of the row to deselect
7881      */
7882     deselectRow : function(index, preventViewNotify){
7883         if(this.locked) {
7884             return;
7885         }
7886         if(this.last == index){
7887             this.last = false;
7888         }
7889         if(this.lastActive == index){
7890             this.lastActive = false;
7891         }
7892         var r = this.grid.ds.getAt(index);
7893         this.selections.remove(r);
7894         if(!preventViewNotify){
7895             var view = this.grid.view ? this.grid.view : this.grid;
7896             view.onRowDeselect(index);
7897         }
7898         this.fireEvent("rowdeselect", this, index);
7899         this.fireEvent("selectionchange", this);
7900     },
7901
7902     // private
7903     restoreLast : function(){
7904         if(this._last){
7905             this.last = this._last;
7906         }
7907     },
7908
7909     // private
7910     acceptsNav : function(row, col, cm){
7911         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7912     },
7913
7914     // private
7915     onEditorKey : function(field, e){
7916         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7917         if(k == e.TAB){
7918             e.stopEvent();
7919             ed.completeEdit();
7920             if(e.shiftKey){
7921                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7922             }else{
7923                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7924             }
7925         }else if(k == e.ENTER && !e.ctrlKey){
7926             e.stopEvent();
7927             ed.completeEdit();
7928             if(e.shiftKey){
7929                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7930             }else{
7931                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7932             }
7933         }else if(k == e.ESC){
7934             ed.cancelEdit();
7935         }
7936         if(newCell){
7937             g.startEditing(newCell[0], newCell[1]);
7938         }
7939     }
7940 });/*
7941  * Based on:
7942  * Ext JS Library 1.1.1
7943  * Copyright(c) 2006-2007, Ext JS, LLC.
7944  *
7945  * Originally Released Under LGPL - original licence link has changed is not relivant.
7946  *
7947  * Fork - LGPL
7948  * <script type="text/javascript">
7949  */
7950  
7951
7952 /**
7953  * @class Roo.grid.ColumnModel
7954  * @extends Roo.util.Observable
7955  * This is the default implementation of a ColumnModel used by the Grid. It defines
7956  * the columns in the grid.
7957  * <br>Usage:<br>
7958  <pre><code>
7959  var colModel = new Roo.grid.ColumnModel([
7960         {header: "Ticker", width: 60, sortable: true, locked: true},
7961         {header: "Company Name", width: 150, sortable: true},
7962         {header: "Market Cap.", width: 100, sortable: true},
7963         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7964         {header: "Employees", width: 100, sortable: true, resizable: false}
7965  ]);
7966  </code></pre>
7967  * <p>
7968  
7969  * The config options listed for this class are options which may appear in each
7970  * individual column definition.
7971  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7972  * @constructor
7973  * @param {Object} config An Array of column config objects. See this class's
7974  * config objects for details.
7975 */
7976 Roo.grid.ColumnModel = function(config){
7977         /**
7978      * The config passed into the constructor
7979      */
7980     this.config = []; //config;
7981     this.lookup = {};
7982
7983     // if no id, create one
7984     // if the column does not have a dataIndex mapping,
7985     // map it to the order it is in the config
7986     for(var i = 0, len = config.length; i < len; i++){
7987         this.addColumn(config[i]);
7988         
7989     }
7990
7991     /**
7992      * The width of columns which have no width specified (defaults to 100)
7993      * @type Number
7994      */
7995     this.defaultWidth = 100;
7996
7997     /**
7998      * Default sortable of columns which have no sortable specified (defaults to false)
7999      * @type Boolean
8000      */
8001     this.defaultSortable = false;
8002
8003     this.addEvents({
8004         /**
8005              * @event widthchange
8006              * Fires when the width of a column changes.
8007              * @param {ColumnModel} this
8008              * @param {Number} columnIndex The column index
8009              * @param {Number} newWidth The new width
8010              */
8011             "widthchange": true,
8012         /**
8013              * @event headerchange
8014              * Fires when the text of a header changes.
8015              * @param {ColumnModel} this
8016              * @param {Number} columnIndex The column index
8017              * @param {Number} newText The new header text
8018              */
8019             "headerchange": true,
8020         /**
8021              * @event hiddenchange
8022              * Fires when a column is hidden or "unhidden".
8023              * @param {ColumnModel} this
8024              * @param {Number} columnIndex The column index
8025              * @param {Boolean} hidden true if hidden, false otherwise
8026              */
8027             "hiddenchange": true,
8028             /**
8029          * @event columnmoved
8030          * Fires when a column is moved.
8031          * @param {ColumnModel} this
8032          * @param {Number} oldIndex
8033          * @param {Number} newIndex
8034          */
8035         "columnmoved" : true,
8036         /**
8037          * @event columlockchange
8038          * Fires when a column's locked state is changed
8039          * @param {ColumnModel} this
8040          * @param {Number} colIndex
8041          * @param {Boolean} locked true if locked
8042          */
8043         "columnlockchange" : true
8044     });
8045     Roo.grid.ColumnModel.superclass.constructor.call(this);
8046 };
8047 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8048     /**
8049      * @cfg {String} header The header text to display in the Grid view.
8050      */
8051         /**
8052      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8053      */
8054         /**
8055      * @cfg {String} smHeader Header at Bootsrap Small width
8056      */
8057         /**
8058      * @cfg {String} mdHeader Header at Bootsrap Medium width
8059      */
8060         /**
8061      * @cfg {String} lgHeader Header at Bootsrap Large width
8062      */
8063         /**
8064      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8065      */
8066     /**
8067      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8068      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8069      * specified, the column's index is used as an index into the Record's data Array.
8070      */
8071     /**
8072      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8073      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8074      */
8075     /**
8076      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8077      * Defaults to the value of the {@link #defaultSortable} property.
8078      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8079      */
8080     /**
8081      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8082      */
8083     /**
8084      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8085      */
8086     /**
8087      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8088      */
8089     /**
8090      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8091      */
8092     /**
8093      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8094      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8095      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8096      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8097      */
8098        /**
8099      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8100      */
8101     /**
8102      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8103      */
8104     /**
8105      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8106      */
8107     /**
8108      * @cfg {String} cursor (Optional)
8109      */
8110     /**
8111      * @cfg {String} tooltip (Optional)
8112      */
8113     /**
8114      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8115      */
8116     /**
8117      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8118      */
8119     /**
8120      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8121      */
8122     /**
8123      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8124      */
8125         /**
8126      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8127      */
8128     /**
8129      * Returns the id of the column at the specified index.
8130      * @param {Number} index The column index
8131      * @return {String} the id
8132      */
8133     getColumnId : function(index){
8134         return this.config[index].id;
8135     },
8136
8137     /**
8138      * Returns the column for a specified id.
8139      * @param {String} id The column id
8140      * @return {Object} the column
8141      */
8142     getColumnById : function(id){
8143         return this.lookup[id];
8144     },
8145
8146     
8147     /**
8148      * Returns the column Object for a specified dataIndex.
8149      * @param {String} dataIndex The column dataIndex
8150      * @return {Object|Boolean} the column or false if not found
8151      */
8152     getColumnByDataIndex: function(dataIndex){
8153         var index = this.findColumnIndex(dataIndex);
8154         return index > -1 ? this.config[index] : false;
8155     },
8156     
8157     /**
8158      * Returns the index for a specified column id.
8159      * @param {String} id The column id
8160      * @return {Number} the index, or -1 if not found
8161      */
8162     getIndexById : function(id){
8163         for(var i = 0, len = this.config.length; i < len; i++){
8164             if(this.config[i].id == id){
8165                 return i;
8166             }
8167         }
8168         return -1;
8169     },
8170     
8171     /**
8172      * Returns the index for a specified column dataIndex.
8173      * @param {String} dataIndex The column dataIndex
8174      * @return {Number} the index, or -1 if not found
8175      */
8176     
8177     findColumnIndex : function(dataIndex){
8178         for(var i = 0, len = this.config.length; i < len; i++){
8179             if(this.config[i].dataIndex == dataIndex){
8180                 return i;
8181             }
8182         }
8183         return -1;
8184     },
8185     
8186     
8187     moveColumn : function(oldIndex, newIndex){
8188         var c = this.config[oldIndex];
8189         this.config.splice(oldIndex, 1);
8190         this.config.splice(newIndex, 0, c);
8191         this.dataMap = null;
8192         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8193     },
8194
8195     isLocked : function(colIndex){
8196         return this.config[colIndex].locked === true;
8197     },
8198
8199     setLocked : function(colIndex, value, suppressEvent){
8200         if(this.isLocked(colIndex) == value){
8201             return;
8202         }
8203         this.config[colIndex].locked = value;
8204         if(!suppressEvent){
8205             this.fireEvent("columnlockchange", this, colIndex, value);
8206         }
8207     },
8208
8209     getTotalLockedWidth : function(){
8210         var totalWidth = 0;
8211         for(var i = 0; i < this.config.length; i++){
8212             if(this.isLocked(i) && !this.isHidden(i)){
8213                 this.totalWidth += this.getColumnWidth(i);
8214             }
8215         }
8216         return totalWidth;
8217     },
8218
8219     getLockedCount : function(){
8220         for(var i = 0, len = this.config.length; i < len; i++){
8221             if(!this.isLocked(i)){
8222                 return i;
8223             }
8224         }
8225         
8226         return this.config.length;
8227     },
8228
8229     /**
8230      * Returns the number of columns.
8231      * @return {Number}
8232      */
8233     getColumnCount : function(visibleOnly){
8234         if(visibleOnly === true){
8235             var c = 0;
8236             for(var i = 0, len = this.config.length; i < len; i++){
8237                 if(!this.isHidden(i)){
8238                     c++;
8239                 }
8240             }
8241             return c;
8242         }
8243         return this.config.length;
8244     },
8245
8246     /**
8247      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8248      * @param {Function} fn
8249      * @param {Object} scope (optional)
8250      * @return {Array} result
8251      */
8252     getColumnsBy : function(fn, scope){
8253         var r = [];
8254         for(var i = 0, len = this.config.length; i < len; i++){
8255             var c = this.config[i];
8256             if(fn.call(scope||this, c, i) === true){
8257                 r[r.length] = c;
8258             }
8259         }
8260         return r;
8261     },
8262
8263     /**
8264      * Returns true if the specified column is sortable.
8265      * @param {Number} col The column index
8266      * @return {Boolean}
8267      */
8268     isSortable : function(col){
8269         if(typeof this.config[col].sortable == "undefined"){
8270             return this.defaultSortable;
8271         }
8272         return this.config[col].sortable;
8273     },
8274
8275     /**
8276      * Returns the rendering (formatting) function defined for the column.
8277      * @param {Number} col The column index.
8278      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8279      */
8280     getRenderer : function(col){
8281         if(!this.config[col].renderer){
8282             return Roo.grid.ColumnModel.defaultRenderer;
8283         }
8284         return this.config[col].renderer;
8285     },
8286
8287     /**
8288      * Sets the rendering (formatting) function for a column.
8289      * @param {Number} col The column index
8290      * @param {Function} fn The function to use to process the cell's raw data
8291      * to return HTML markup for the grid view. The render function is called with
8292      * the following parameters:<ul>
8293      * <li>Data value.</li>
8294      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8295      * <li>css A CSS style string to apply to the table cell.</li>
8296      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8297      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8298      * <li>Row index</li>
8299      * <li>Column index</li>
8300      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8301      */
8302     setRenderer : function(col, fn){
8303         this.config[col].renderer = fn;
8304     },
8305
8306     /**
8307      * Returns the width for the specified column.
8308      * @param {Number} col The column index
8309      * @param (optional) {String} gridSize bootstrap width size.
8310      * @return {Number}
8311      */
8312     getColumnWidth : function(col, gridSize)
8313         {
8314                 var cfg = this.config[col];
8315                 
8316                 if (typeof(gridSize) == 'undefined') {
8317                         return cfg.width * 1 || this.defaultWidth;
8318                 }
8319                 if (gridSize === false) { // if we set it..
8320                         return cfg.width || false;
8321                 }
8322                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8323                 
8324                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8325                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8326                                 continue;
8327                         }
8328                         return cfg[ sizes[i] ];
8329                 }
8330                 return 1;
8331                 
8332     },
8333
8334     /**
8335      * Sets the width for a column.
8336      * @param {Number} col The column index
8337      * @param {Number} width The new width
8338      */
8339     setColumnWidth : function(col, width, suppressEvent){
8340         this.config[col].width = width;
8341         this.totalWidth = null;
8342         if(!suppressEvent){
8343              this.fireEvent("widthchange", this, col, width);
8344         }
8345     },
8346
8347     /**
8348      * Returns the total width of all columns.
8349      * @param {Boolean} includeHidden True to include hidden column widths
8350      * @return {Number}
8351      */
8352     getTotalWidth : function(includeHidden){
8353         if(!this.totalWidth){
8354             this.totalWidth = 0;
8355             for(var i = 0, len = this.config.length; i < len; i++){
8356                 if(includeHidden || !this.isHidden(i)){
8357                     this.totalWidth += this.getColumnWidth(i);
8358                 }
8359             }
8360         }
8361         return this.totalWidth;
8362     },
8363
8364     /**
8365      * Returns the header for the specified column.
8366      * @param {Number} col The column index
8367      * @return {String}
8368      */
8369     getColumnHeader : function(col){
8370         return this.config[col].header;
8371     },
8372
8373     /**
8374      * Sets the header for a column.
8375      * @param {Number} col The column index
8376      * @param {String} header The new header
8377      */
8378     setColumnHeader : function(col, header){
8379         this.config[col].header = header;
8380         this.fireEvent("headerchange", this, col, header);
8381     },
8382
8383     /**
8384      * Returns the tooltip for the specified column.
8385      * @param {Number} col The column index
8386      * @return {String}
8387      */
8388     getColumnTooltip : function(col){
8389             return this.config[col].tooltip;
8390     },
8391     /**
8392      * Sets the tooltip for a column.
8393      * @param {Number} col The column index
8394      * @param {String} tooltip The new tooltip
8395      */
8396     setColumnTooltip : function(col, tooltip){
8397             this.config[col].tooltip = tooltip;
8398     },
8399
8400     /**
8401      * Returns the dataIndex for the specified column.
8402      * @param {Number} col The column index
8403      * @return {Number}
8404      */
8405     getDataIndex : function(col){
8406         return this.config[col].dataIndex;
8407     },
8408
8409     /**
8410      * Sets the dataIndex for a column.
8411      * @param {Number} col The column index
8412      * @param {Number} dataIndex The new dataIndex
8413      */
8414     setDataIndex : function(col, dataIndex){
8415         this.config[col].dataIndex = dataIndex;
8416     },
8417
8418     
8419     
8420     /**
8421      * Returns true if the cell is editable.
8422      * @param {Number} colIndex The column index
8423      * @param {Number} rowIndex The row index - this is nto actually used..?
8424      * @return {Boolean}
8425      */
8426     isCellEditable : function(colIndex, rowIndex){
8427         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8428     },
8429
8430     /**
8431      * Returns the editor defined for the cell/column.
8432      * return false or null to disable editing.
8433      * @param {Number} colIndex The column index
8434      * @param {Number} rowIndex The row index
8435      * @return {Object}
8436      */
8437     getCellEditor : function(colIndex, rowIndex){
8438         return this.config[colIndex].editor;
8439     },
8440
8441     /**
8442      * Sets if a column is editable.
8443      * @param {Number} col The column index
8444      * @param {Boolean} editable True if the column is editable
8445      */
8446     setEditable : function(col, editable){
8447         this.config[col].editable = editable;
8448     },
8449
8450
8451     /**
8452      * Returns true if the column is hidden.
8453      * @param {Number} colIndex The column index
8454      * @return {Boolean}
8455      */
8456     isHidden : function(colIndex){
8457         return this.config[colIndex].hidden;
8458     },
8459
8460
8461     /**
8462      * Returns true if the column width cannot be changed
8463      */
8464     isFixed : function(colIndex){
8465         return this.config[colIndex].fixed;
8466     },
8467
8468     /**
8469      * Returns true if the column can be resized
8470      * @return {Boolean}
8471      */
8472     isResizable : function(colIndex){
8473         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8474     },
8475     /**
8476      * Sets if a column is hidden.
8477      * @param {Number} colIndex The column index
8478      * @param {Boolean} hidden True if the column is hidden
8479      */
8480     setHidden : function(colIndex, hidden){
8481         this.config[colIndex].hidden = hidden;
8482         this.totalWidth = null;
8483         this.fireEvent("hiddenchange", this, colIndex, hidden);
8484     },
8485
8486     /**
8487      * Sets the editor for a column.
8488      * @param {Number} col The column index
8489      * @param {Object} editor The editor object
8490      */
8491     setEditor : function(col, editor){
8492         this.config[col].editor = editor;
8493     },
8494     /**
8495      * Add a column (experimental...) - defaults to adding to the end..
8496      * @param {Object} config 
8497     */
8498     addColumn : function(c)
8499     {
8500     
8501         var i = this.config.length;
8502         this.config[i] = c;
8503         
8504         if(typeof c.dataIndex == "undefined"){
8505             c.dataIndex = i;
8506         }
8507         if(typeof c.renderer == "string"){
8508             c.renderer = Roo.util.Format[c.renderer];
8509         }
8510         if(typeof c.id == "undefined"){
8511             c.id = Roo.id();
8512         }
8513         if(c.editor && c.editor.xtype){
8514             c.editor  = Roo.factory(c.editor, Roo.grid);
8515         }
8516         if(c.editor && c.editor.isFormField){
8517             c.editor = new Roo.grid.GridEditor(c.editor);
8518         }
8519         this.lookup[c.id] = c;
8520     }
8521     
8522 });
8523
8524 Roo.grid.ColumnModel.defaultRenderer = function(value)
8525 {
8526     if(typeof value == "object") {
8527         return value;
8528     }
8529         if(typeof value == "string" && value.length < 1){
8530             return "&#160;";
8531         }
8532     
8533         return String.format("{0}", value);
8534 };
8535
8536 // Alias for backwards compatibility
8537 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8538 /*
8539  * Based on:
8540  * Ext JS Library 1.1.1
8541  * Copyright(c) 2006-2007, Ext JS, LLC.
8542  *
8543  * Originally Released Under LGPL - original licence link has changed is not relivant.
8544  *
8545  * Fork - LGPL
8546  * <script type="text/javascript">
8547  */
8548  
8549 /**
8550  * @class Roo.LoadMask
8551  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8552  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8553  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8554  * element's UpdateManager load indicator and will be destroyed after the initial load.
8555  * @constructor
8556  * Create a new LoadMask
8557  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8558  * @param {Object} config The config object
8559  */
8560 Roo.LoadMask = function(el, config){
8561     this.el = Roo.get(el);
8562     Roo.apply(this, config);
8563     if(this.store){
8564         this.store.on('beforeload', this.onBeforeLoad, this);
8565         this.store.on('load', this.onLoad, this);
8566         this.store.on('loadexception', this.onLoadException, this);
8567         this.removeMask = false;
8568     }else{
8569         var um = this.el.getUpdateManager();
8570         um.showLoadIndicator = false; // disable the default indicator
8571         um.on('beforeupdate', this.onBeforeLoad, this);
8572         um.on('update', this.onLoad, this);
8573         um.on('failure', this.onLoad, this);
8574         this.removeMask = true;
8575     }
8576 };
8577
8578 Roo.LoadMask.prototype = {
8579     /**
8580      * @cfg {Boolean} removeMask
8581      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8582      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8583      */
8584     removeMask : false,
8585     /**
8586      * @cfg {String} msg
8587      * The text to display in a centered loading message box (defaults to 'Loading...')
8588      */
8589     msg : 'Loading...',
8590     /**
8591      * @cfg {String} msgCls
8592      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8593      */
8594     msgCls : 'x-mask-loading',
8595
8596     /**
8597      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8598      * @type Boolean
8599      */
8600     disabled: false,
8601
8602     /**
8603      * Disables the mask to prevent it from being displayed
8604      */
8605     disable : function(){
8606        this.disabled = true;
8607     },
8608
8609     /**
8610      * Enables the mask so that it can be displayed
8611      */
8612     enable : function(){
8613         this.disabled = false;
8614     },
8615     
8616     onLoadException : function()
8617     {
8618         Roo.log(arguments);
8619         
8620         if (typeof(arguments[3]) != 'undefined') {
8621             Roo.MessageBox.alert("Error loading",arguments[3]);
8622         } 
8623         /*
8624         try {
8625             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8626                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8627             }   
8628         } catch(e) {
8629             
8630         }
8631         */
8632     
8633         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8634     },
8635     // private
8636     onLoad : function()
8637     {
8638         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8639     },
8640
8641     // private
8642     onBeforeLoad : function(){
8643         if(!this.disabled){
8644             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8645         }
8646     },
8647
8648     // private
8649     destroy : function(){
8650         if(this.store){
8651             this.store.un('beforeload', this.onBeforeLoad, this);
8652             this.store.un('load', this.onLoad, this);
8653             this.store.un('loadexception', this.onLoadException, this);
8654         }else{
8655             var um = this.el.getUpdateManager();
8656             um.un('beforeupdate', this.onBeforeLoad, this);
8657             um.un('update', this.onLoad, this);
8658             um.un('failure', this.onLoad, this);
8659         }
8660     }
8661 };/**
8662  * @class Roo.bootstrap.Table
8663  * @licence LGBL
8664  * @extends Roo.bootstrap.Component
8665  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8666  * Similar to Roo.grid.Grid
8667  * <pre><code>
8668  var table = Roo.factory({
8669     xtype : 'Table',
8670     xns : Roo.bootstrap,
8671     autoSizeColumns: true,
8672     
8673     
8674     store : {
8675         xtype : 'Store',
8676         xns : Roo.data,
8677         remoteSort : true,
8678         sortInfo : { direction : 'ASC', field: 'name' },
8679         proxy : {
8680            xtype : 'HttpProxy',
8681            xns : Roo.data,
8682            method : 'GET',
8683            url : 'https://example.com/some.data.url.json'
8684         },
8685         reader : {
8686            xtype : 'JsonReader',
8687            xns : Roo.data,
8688            fields : [ 'id', 'name', whatever' ],
8689            id : 'id',
8690            root : 'data'
8691         }
8692     },
8693     cm : [
8694         {
8695             xtype : 'ColumnModel',
8696             xns : Roo.grid,
8697             align : 'center',
8698             cursor : 'pointer',
8699             dataIndex : 'is_in_group',
8700             header : "Name",
8701             sortable : true,
8702             renderer : function(v, x , r) {  
8703             
8704                 return String.format("{0}", v)
8705             }
8706             width : 3
8707         } // more columns..
8708     ],
8709     selModel : {
8710         xtype : 'RowSelectionModel',
8711         xns : Roo.bootstrap.Table
8712         // you can add listeners to catch selection change here....
8713     }
8714      
8715
8716  });
8717  // set any options
8718  grid.render(Roo.get("some-div"));
8719 </code></pre>
8720
8721 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8722
8723
8724
8725  *
8726  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8727  * @cfg {Roo.data.Store} store The data store to use
8728  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8729  * 
8730  * @cfg {String} cls table class
8731  *
8732  * 
8733  * @cfg {boolean} striped Should the rows be alternative striped
8734  * @cfg {boolean} bordered Add borders to the table
8735  * @cfg {boolean} hover Add hover highlighting
8736  * @cfg {boolean} condensed Format condensed
8737  * @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,
8738  *                also adds table-responsive (see bootstrap docs for details)
8739  * @cfg {Boolean} loadMask (true|false) default false
8740  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8741  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8742  * @cfg {Boolean} rowSelection (true|false) default false
8743  * @cfg {Boolean} cellSelection (true|false) default false
8744  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8745  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8746  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8747  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8748  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8749  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8750  * 
8751  * @constructor
8752  * Create a new Table
8753  * @param {Object} config The config object
8754  */
8755
8756 Roo.bootstrap.Table = function(config)
8757 {
8758     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8759      
8760     // BC...
8761     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8762     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8763     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8764     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8765     
8766     this.view = this; // compat with grid.
8767     
8768     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8769     if (this.sm) {
8770         this.sm.grid = this;
8771         this.selModel = Roo.factory(this.sm, Roo.grid);
8772         this.sm = this.selModel;
8773         this.sm.xmodule = this.xmodule || false;
8774     }
8775     
8776     if (this.cm && typeof(this.cm.config) == 'undefined') {
8777         this.colModel = new Roo.grid.ColumnModel(this.cm);
8778         this.cm = this.colModel;
8779         this.cm.xmodule = this.xmodule || false;
8780     }
8781     if (this.store) {
8782         this.store= Roo.factory(this.store, Roo.data);
8783         this.ds = this.store;
8784         this.ds.xmodule = this.xmodule || false;
8785          
8786     }
8787     if (this.footer && this.store) {
8788         this.footer.dataSource = this.ds;
8789         this.footer = Roo.factory(this.footer);
8790     }
8791     
8792     /** @private */
8793     this.addEvents({
8794         /**
8795          * @event cellclick
8796          * Fires when a cell is clicked
8797          * @param {Roo.bootstrap.Table} this
8798          * @param {Roo.Element} el
8799          * @param {Number} rowIndex
8800          * @param {Number} columnIndex
8801          * @param {Roo.EventObject} e
8802          */
8803         "cellclick" : true,
8804         /**
8805          * @event celldblclick
8806          * Fires when a cell is double clicked
8807          * @param {Roo.bootstrap.Table} this
8808          * @param {Roo.Element} el
8809          * @param {Number} rowIndex
8810          * @param {Number} columnIndex
8811          * @param {Roo.EventObject} e
8812          */
8813         "celldblclick" : true,
8814         /**
8815          * @event rowclick
8816          * Fires when a row is clicked
8817          * @param {Roo.bootstrap.Table} this
8818          * @param {Roo.Element} el
8819          * @param {Number} rowIndex
8820          * @param {Roo.EventObject} e
8821          */
8822         "rowclick" : true,
8823         /**
8824          * @event rowdblclick
8825          * Fires when a row is double clicked
8826          * @param {Roo.bootstrap.Table} this
8827          * @param {Roo.Element} el
8828          * @param {Number} rowIndex
8829          * @param {Roo.EventObject} e
8830          */
8831         "rowdblclick" : true,
8832         /**
8833          * @event mouseover
8834          * Fires when a mouseover occur
8835          * @param {Roo.bootstrap.Table} this
8836          * @param {Roo.Element} el
8837          * @param {Number} rowIndex
8838          * @param {Number} columnIndex
8839          * @param {Roo.EventObject} e
8840          */
8841         "mouseover" : true,
8842         /**
8843          * @event mouseout
8844          * Fires when a mouseout occur
8845          * @param {Roo.bootstrap.Table} this
8846          * @param {Roo.Element} el
8847          * @param {Number} rowIndex
8848          * @param {Number} columnIndex
8849          * @param {Roo.EventObject} e
8850          */
8851         "mouseout" : true,
8852         /**
8853          * @event rowclass
8854          * Fires when a row is rendered, so you can change add a style to it.
8855          * @param {Roo.bootstrap.Table} this
8856          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8857          */
8858         'rowclass' : true,
8859           /**
8860          * @event rowsrendered
8861          * Fires when all the  rows have been rendered
8862          * @param {Roo.bootstrap.Table} this
8863          */
8864         'rowsrendered' : true,
8865         /**
8866          * @event contextmenu
8867          * The raw contextmenu event for the entire grid.
8868          * @param {Roo.EventObject} e
8869          */
8870         "contextmenu" : true,
8871         /**
8872          * @event rowcontextmenu
8873          * Fires when a row is right clicked
8874          * @param {Roo.bootstrap.Table} this
8875          * @param {Number} rowIndex
8876          * @param {Roo.EventObject} e
8877          */
8878         "rowcontextmenu" : true,
8879         /**
8880          * @event cellcontextmenu
8881          * Fires when a cell is right clicked
8882          * @param {Roo.bootstrap.Table} this
8883          * @param {Number} rowIndex
8884          * @param {Number} cellIndex
8885          * @param {Roo.EventObject} e
8886          */
8887          "cellcontextmenu" : true,
8888          /**
8889          * @event headercontextmenu
8890          * Fires when a header is right clicked
8891          * @param {Roo.bootstrap.Table} this
8892          * @param {Number} columnIndex
8893          * @param {Roo.EventObject} e
8894          */
8895         "headercontextmenu" : true,
8896         /**
8897          * @event mousedown
8898          * The raw mousedown event for the entire grid.
8899          * @param {Roo.EventObject} e
8900          */
8901         "mousedown" : true
8902         
8903     });
8904 };
8905
8906 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8907     
8908     cls: false,
8909     
8910     striped : false,
8911     scrollBody : false,
8912     bordered: false,
8913     hover:  false,
8914     condensed : false,
8915     responsive : false,
8916     sm : false,
8917     cm : false,
8918     store : false,
8919     loadMask : false,
8920     footerShow : true,
8921     headerShow : true,
8922     enableColumnResize: true,
8923   
8924     rowSelection : false,
8925     cellSelection : false,
8926     layout : false,
8927
8928     minColumnWidth : 50,
8929     
8930     // Roo.Element - the tbody
8931     bodyEl: false,  // <tbody> Roo.Element - thead element    
8932     headEl: false,  // <thead> Roo.Element - thead element
8933     resizeProxy : false, // proxy element for dragging?
8934
8935
8936     
8937     container: false, // used by gridpanel...
8938     
8939     lazyLoad : false,
8940     
8941     CSS : Roo.util.CSS,
8942     
8943     auto_hide_footer : false,
8944     
8945     view: false, // actually points to this..
8946     
8947     getAutoCreate : function()
8948     {
8949         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8950         
8951         cfg = {
8952             tag: 'table',
8953             cls : 'table', 
8954             cn : []
8955         };
8956         // this get's auto added by panel.Grid
8957         if (this.scrollBody) {
8958             cfg.cls += ' table-body-fixed';
8959         }    
8960         if (this.striped) {
8961             cfg.cls += ' table-striped';
8962         }
8963         
8964         if (this.hover) {
8965             cfg.cls += ' table-hover';
8966         }
8967         if (this.bordered) {
8968             cfg.cls += ' table-bordered';
8969         }
8970         if (this.condensed) {
8971             cfg.cls += ' table-condensed';
8972         }
8973         
8974         if (this.responsive) {
8975             cfg.cls += ' table-responsive';
8976         }
8977         
8978         if (this.cls) {
8979             cfg.cls+=  ' ' +this.cls;
8980         }
8981         
8982         
8983         
8984         if (this.layout) {
8985             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8986         }
8987         
8988         if(this.store || this.cm){
8989             if(this.headerShow){
8990                 cfg.cn.push(this.renderHeader());
8991             }
8992             
8993             cfg.cn.push(this.renderBody());
8994             
8995             if(this.footerShow){
8996                 cfg.cn.push(this.renderFooter());
8997             }
8998             // where does this come from?
8999             //cfg.cls+=  ' TableGrid';
9000         }
9001         
9002         return { cn : [ cfg ] };
9003     },
9004     
9005     initEvents : function()
9006     {   
9007         if(!this.store || !this.cm){
9008             return;
9009         }
9010         if (this.selModel) {
9011             this.selModel.initEvents();
9012         }
9013         
9014         
9015         //Roo.log('initEvents with ds!!!!');
9016         
9017         this.bodyEl = this.el.select('tbody', true).first();
9018         this.headEl = this.el.select('thead', true).first();
9019         this.mainFoot = this.el.select('tfoot', true).first();
9020         
9021         
9022         
9023         
9024         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9025             e.on('click', this.sort, this);
9026         }, this);
9027         
9028         
9029         // why is this done????? = it breaks dialogs??
9030         //this.parent().el.setStyle('position', 'relative');
9031         
9032         
9033         if (this.footer) {
9034             this.footer.parentId = this.id;
9035             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9036             
9037             if(this.lazyLoad){
9038                 this.el.select('tfoot tr td').first().addClass('hide');
9039             }
9040         } 
9041         
9042         if(this.loadMask) {
9043             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9044         }
9045         
9046         this.store.on('load', this.onLoad, this);
9047         this.store.on('beforeload', this.onBeforeLoad, this);
9048         this.store.on('update', this.onUpdate, this);
9049         this.store.on('add', this.onAdd, this);
9050         this.store.on("clear", this.clear, this);
9051         
9052         this.el.on("contextmenu", this.onContextMenu, this);
9053         
9054         
9055         this.cm.on("headerchange", this.onHeaderChange, this);
9056         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9057
9058  //?? does bodyEl get replaced on render?
9059         this.bodyEl.on("click", this.onClick, this);
9060         this.bodyEl.on("dblclick", this.onDblClick, this);        
9061         this.bodyEl.on('scroll', this.onBodyScroll, this);
9062
9063         // guessing mainbody will work - this relays usually caught by selmodel at present.
9064         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9065   
9066   
9067         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9068         
9069   
9070         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9071             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9072         }
9073         
9074         this.initCSS();
9075     },
9076     // Compatibility with grid - we implement all the view features at present.
9077     getView : function()
9078     {
9079         return this;
9080     },
9081     
9082     initCSS : function()
9083     {
9084         
9085         
9086         var cm = this.cm, styles = [];
9087         this.CSS.removeStyleSheet(this.id + '-cssrules');
9088         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9089         // we can honour xs/sm/md/xl  as widths...
9090         // we first have to decide what widht we are currently at...
9091         var sz = Roo.getGridSize();
9092         
9093         var total = 0;
9094         var last = -1;
9095         var cols = []; // visable cols.
9096         var total_abs = 0;
9097         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9098             var w = cm.getColumnWidth(i, false);
9099             if(cm.isHidden(i)){
9100                 cols.push( { rel : false, abs : 0 });
9101                 continue;
9102             }
9103             if (w !== false) {
9104                 cols.push( { rel : false, abs : w });
9105                 total_abs += w;
9106                 last = i; // not really..
9107                 continue;
9108             }
9109             var w = cm.getColumnWidth(i, sz);
9110             if (w > 0) {
9111                 last = i
9112             }
9113             total += w;
9114             cols.push( { rel : w, abs : false });
9115         }
9116         
9117         var avail = this.bodyEl.dom.clientWidth - total_abs;
9118         
9119         var unitWidth = Math.floor(avail / total);
9120         var rem = avail - (unitWidth * total);
9121         
9122         var hidden, width, pos = 0 , splithide , left;
9123         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9124             
9125             hidden = 'display:none;';
9126             left = '';
9127             width  = 'width:0px;';
9128             splithide = '';
9129             if(!cm.isHidden(i)){
9130                 hidden = '';
9131                 
9132                 
9133                 // we can honour xs/sm/md/xl ?
9134                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9135                 if (w===0) {
9136                     hidden = 'display:none;';
9137                 }
9138                 // width should return a small number...
9139                 if (i == last) {
9140                     w+=rem; // add the remaining with..
9141                 }
9142                 pos += w;
9143                 left = "left:" + (pos -4) + "px;";
9144                 width = "width:" + w+ "px;";
9145                 
9146             }
9147             if (this.responsive) {
9148                 width = '';
9149                 left = '';
9150                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9151                 splithide = 'display: none;';
9152             }
9153             
9154             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9155             if (this.headEl) {
9156                 if (i == last) {
9157                     splithide = 'display:none;';
9158                 }
9159                 
9160                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9161                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9162                 );
9163             }
9164             
9165         }
9166         //Roo.log(styles.join(''));
9167         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9168         
9169     },
9170     
9171     
9172     
9173     onContextMenu : function(e, t)
9174     {
9175         this.processEvent("contextmenu", e);
9176     },
9177     
9178     processEvent : function(name, e)
9179     {
9180         if (name != 'touchstart' ) {
9181             this.fireEvent(name, e);    
9182         }
9183         
9184         var t = e.getTarget();
9185         
9186         var cell = Roo.get(t);
9187         
9188         if(!cell){
9189             return;
9190         }
9191         
9192         if(cell.findParent('tfoot', false, true)){
9193             return;
9194         }
9195         
9196         if(cell.findParent('thead', false, true)){
9197             
9198             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9199                 cell = Roo.get(t).findParent('th', false, true);
9200                 if (!cell) {
9201                     Roo.log("failed to find th in thead?");
9202                     Roo.log(e.getTarget());
9203                     return;
9204                 }
9205             }
9206             
9207             var cellIndex = cell.dom.cellIndex;
9208             
9209             var ename = name == 'touchstart' ? 'click' : name;
9210             this.fireEvent("header" + ename, this, cellIndex, e);
9211             
9212             return;
9213         }
9214         
9215         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9216             cell = Roo.get(t).findParent('td', false, true);
9217             if (!cell) {
9218                 Roo.log("failed to find th in tbody?");
9219                 Roo.log(e.getTarget());
9220                 return;
9221             }
9222         }
9223         
9224         var row = cell.findParent('tr', false, true);
9225         var cellIndex = cell.dom.cellIndex;
9226         var rowIndex = row.dom.rowIndex - 1;
9227         
9228         if(row !== false){
9229             
9230             this.fireEvent("row" + name, this, rowIndex, e);
9231             
9232             if(cell !== false){
9233             
9234                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9235             }
9236         }
9237         
9238     },
9239     
9240     onMouseover : function(e, el)
9241     {
9242         var cell = Roo.get(el);
9243         
9244         if(!cell){
9245             return;
9246         }
9247         
9248         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9249             cell = cell.findParent('td', false, true);
9250         }
9251         
9252         var row = cell.findParent('tr', false, true);
9253         var cellIndex = cell.dom.cellIndex;
9254         var rowIndex = row.dom.rowIndex - 1; // start from 0
9255         
9256         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9257         
9258     },
9259     
9260     onMouseout : function(e, el)
9261     {
9262         var cell = Roo.get(el);
9263         
9264         if(!cell){
9265             return;
9266         }
9267         
9268         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9269             cell = cell.findParent('td', false, true);
9270         }
9271         
9272         var row = cell.findParent('tr', false, true);
9273         var cellIndex = cell.dom.cellIndex;
9274         var rowIndex = row.dom.rowIndex - 1; // start from 0
9275         
9276         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9277         
9278     },
9279     
9280     onClick : function(e, el)
9281     {
9282         var cell = Roo.get(el);
9283         
9284         if(!cell || (!this.cellSelection && !this.rowSelection)){
9285             return;
9286         }
9287         
9288         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9289             cell = cell.findParent('td', false, true);
9290         }
9291         
9292         if(!cell || typeof(cell) == 'undefined'){
9293             return;
9294         }
9295         
9296         var row = cell.findParent('tr', false, true);
9297         
9298         if(!row || typeof(row) == 'undefined'){
9299             return;
9300         }
9301         
9302         var cellIndex = cell.dom.cellIndex;
9303         var rowIndex = this.getRowIndex(row);
9304         
9305         // why??? - should these not be based on SelectionModel?
9306         //if(this.cellSelection){
9307             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9308         //}
9309         
9310         //if(this.rowSelection){
9311             this.fireEvent('rowclick', this, row, rowIndex, e);
9312         //}
9313          
9314     },
9315         
9316     onDblClick : function(e,el)
9317     {
9318         var cell = Roo.get(el);
9319         
9320         if(!cell || (!this.cellSelection && !this.rowSelection)){
9321             return;
9322         }
9323         
9324         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9325             cell = cell.findParent('td', false, true);
9326         }
9327         
9328         if(!cell || typeof(cell) == 'undefined'){
9329             return;
9330         }
9331         
9332         var row = cell.findParent('tr', false, true);
9333         
9334         if(!row || typeof(row) == 'undefined'){
9335             return;
9336         }
9337         
9338         var cellIndex = cell.dom.cellIndex;
9339         var rowIndex = this.getRowIndex(row);
9340         
9341         if(this.cellSelection){
9342             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9343         }
9344         
9345         if(this.rowSelection){
9346             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9347         }
9348     },
9349     findRowIndex : function(el)
9350     {
9351         var cell = Roo.get(el);
9352         if(!cell) {
9353             return false;
9354         }
9355         var row = cell.findParent('tr', false, true);
9356         
9357         if(!row || typeof(row) == 'undefined'){
9358             return false;
9359         }
9360         return this.getRowIndex(row);
9361     },
9362     sort : function(e,el)
9363     {
9364         var col = Roo.get(el);
9365         
9366         if(!col.hasClass('sortable')){
9367             return;
9368         }
9369         
9370         var sort = col.attr('sort');
9371         var dir = 'ASC';
9372         
9373         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9374             dir = 'DESC';
9375         }
9376         
9377         this.store.sortInfo = {field : sort, direction : dir};
9378         
9379         if (this.footer) {
9380             Roo.log("calling footer first");
9381             this.footer.onClick('first');
9382         } else {
9383         
9384             this.store.load({ params : { start : 0 } });
9385         }
9386     },
9387     
9388     renderHeader : function()
9389     {
9390         var header = {
9391             tag: 'thead',
9392             cn : []
9393         };
9394         
9395         var cm = this.cm;
9396         this.totalWidth = 0;
9397         
9398         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9399             
9400             var config = cm.config[i];
9401             
9402             var c = {
9403                 tag: 'th',
9404                 cls : 'x-hcol-' + i,
9405                 style : '',
9406                 
9407                 html: cm.getColumnHeader(i)
9408             };
9409             
9410             var tooltip = cm.getColumnTooltip(i);
9411             if (tooltip) {
9412                 c.tooltip = tooltip;
9413             }
9414             
9415             
9416             var hh = '';
9417             
9418             if(typeof(config.sortable) != 'undefined' && config.sortable){
9419                 c.cls += ' sortable';
9420                 c.html = '<i class="fa"></i>' + c.html;
9421             }
9422             
9423             // could use BS4 hidden-..-down 
9424             
9425             if(typeof(config.lgHeader) != 'undefined'){
9426                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9427             }
9428             
9429             if(typeof(config.mdHeader) != 'undefined'){
9430                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9431             }
9432             
9433             if(typeof(config.smHeader) != 'undefined'){
9434                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9435             }
9436             
9437             if(typeof(config.xsHeader) != 'undefined'){
9438                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9439             }
9440             
9441             if(hh.length){
9442                 c.html = hh;
9443             }
9444             
9445             if(typeof(config.tooltip) != 'undefined'){
9446                 c.tooltip = config.tooltip;
9447             }
9448             
9449             if(typeof(config.colspan) != 'undefined'){
9450                 c.colspan = config.colspan;
9451             }
9452             
9453             // hidden is handled by CSS now
9454             
9455             if(typeof(config.dataIndex) != 'undefined'){
9456                 c.sort = config.dataIndex;
9457             }
9458             
9459            
9460             
9461             if(typeof(config.align) != 'undefined' && config.align.length){
9462                 c.style += ' text-align:' + config.align + ';';
9463             }
9464             
9465             /* width is done in CSS
9466              *if(typeof(config.width) != 'undefined'){
9467                 c.style += ' width:' + config.width + 'px;';
9468                 this.totalWidth += config.width;
9469             } else {
9470                 this.totalWidth += 100; // assume minimum of 100 per column?
9471             }
9472             */
9473             
9474             if(typeof(config.cls) != 'undefined'){
9475                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9476             }
9477             // this is the bit that doesnt reall work at all...
9478             
9479             if (this.responsive) {
9480                  
9481             
9482                 ['xs','sm','md','lg'].map(function(size){
9483                     
9484                     if(typeof(config[size]) == 'undefined'){
9485                         return;
9486                     }
9487                      
9488                     if (!config[size]) { // 0 = hidden
9489                         // BS 4 '0' is treated as hide that column and below.
9490                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9491                         return;
9492                     }
9493                     
9494                     c.cls += ' col-' + size + '-' + config[size] + (
9495                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9496                     );
9497                     
9498                     
9499                 });
9500             }
9501             // at the end?
9502             
9503             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9504             
9505             
9506             
9507             
9508             header.cn.push(c)
9509         }
9510         
9511         return header;
9512     },
9513     
9514     renderBody : function()
9515     {
9516         var body = {
9517             tag: 'tbody',
9518             cn : [
9519                 {
9520                     tag: 'tr',
9521                     cn : [
9522                         {
9523                             tag : 'td',
9524                             colspan :  this.cm.getColumnCount()
9525                         }
9526                     ]
9527                 }
9528             ]
9529         };
9530         
9531         return body;
9532     },
9533     
9534     renderFooter : function()
9535     {
9536         var footer = {
9537             tag: 'tfoot',
9538             cn : [
9539                 {
9540                     tag: 'tr',
9541                     cn : [
9542                         {
9543                             tag : 'td',
9544                             colspan :  this.cm.getColumnCount()
9545                         }
9546                     ]
9547                 }
9548             ]
9549         };
9550         
9551         return footer;
9552     },
9553     
9554     
9555     
9556     onLoad : function()
9557     {
9558 //        Roo.log('ds onload');
9559         this.clear();
9560         
9561         var _this = this;
9562         var cm = this.cm;
9563         var ds = this.store;
9564         
9565         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9566             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9567             if (_this.store.sortInfo) {
9568                     
9569                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9570                     e.select('i', true).addClass(['fa-arrow-up']);
9571                 }
9572                 
9573                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9574                     e.select('i', true).addClass(['fa-arrow-down']);
9575                 }
9576             }
9577         });
9578         
9579         var tbody =  this.bodyEl;
9580               
9581         if(ds.getCount() > 0){
9582             ds.data.each(function(d,rowIndex){
9583                 var row =  this.renderRow(cm, ds, rowIndex);
9584                 
9585                 tbody.createChild(row);
9586                 
9587                 var _this = this;
9588                 
9589                 if(row.cellObjects.length){
9590                     Roo.each(row.cellObjects, function(r){
9591                         _this.renderCellObject(r);
9592                     })
9593                 }
9594                 
9595             }, this);
9596         }
9597         
9598         var tfoot = this.el.select('tfoot', true).first();
9599         
9600         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9601             
9602             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9603             
9604             var total = this.ds.getTotalCount();
9605             
9606             if(this.footer.pageSize < total){
9607                 this.mainFoot.show();
9608             }
9609         }
9610         
9611         Roo.each(this.el.select('tbody td', true).elements, function(e){
9612             e.on('mouseover', _this.onMouseover, _this);
9613         });
9614         
9615         Roo.each(this.el.select('tbody td', true).elements, function(e){
9616             e.on('mouseout', _this.onMouseout, _this);
9617         });
9618         this.fireEvent('rowsrendered', this);
9619         
9620         this.autoSize();
9621         
9622         this.initCSS(); /// resize cols
9623
9624         
9625     },
9626     
9627     
9628     onUpdate : function(ds,record)
9629     {
9630         this.refreshRow(record);
9631         this.autoSize();
9632     },
9633     
9634     onRemove : function(ds, record, index, isUpdate){
9635         if(isUpdate !== true){
9636             this.fireEvent("beforerowremoved", this, index, record);
9637         }
9638         var bt = this.bodyEl.dom;
9639         
9640         var rows = this.el.select('tbody > tr', true).elements;
9641         
9642         if(typeof(rows[index]) != 'undefined'){
9643             bt.removeChild(rows[index].dom);
9644         }
9645         
9646 //        if(bt.rows[index]){
9647 //            bt.removeChild(bt.rows[index]);
9648 //        }
9649         
9650         if(isUpdate !== true){
9651             //this.stripeRows(index);
9652             //this.syncRowHeights(index, index);
9653             //this.layout();
9654             this.fireEvent("rowremoved", this, index, record);
9655         }
9656     },
9657     
9658     onAdd : function(ds, records, rowIndex)
9659     {
9660         //Roo.log('on Add called');
9661         // - note this does not handle multiple adding very well..
9662         var bt = this.bodyEl.dom;
9663         for (var i =0 ; i < records.length;i++) {
9664             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9665             //Roo.log(records[i]);
9666             //Roo.log(this.store.getAt(rowIndex+i));
9667             this.insertRow(this.store, rowIndex + i, false);
9668             return;
9669         }
9670         
9671     },
9672     
9673     
9674     refreshRow : function(record){
9675         var ds = this.store, index;
9676         if(typeof record == 'number'){
9677             index = record;
9678             record = ds.getAt(index);
9679         }else{
9680             index = ds.indexOf(record);
9681             if (index < 0) {
9682                 return; // should not happen - but seems to 
9683             }
9684         }
9685         this.insertRow(ds, index, true);
9686         this.autoSize();
9687         this.onRemove(ds, record, index+1, true);
9688         this.autoSize();
9689         //this.syncRowHeights(index, index);
9690         //this.layout();
9691         this.fireEvent("rowupdated", this, index, record);
9692     },
9693     // private - called by RowSelection
9694     onRowSelect : function(rowIndex){
9695         var row = this.getRowDom(rowIndex);
9696         row.addClass(['bg-info','info']);
9697     },
9698     // private - called by RowSelection
9699     onRowDeselect : function(rowIndex)
9700     {
9701         if (rowIndex < 0) {
9702             return;
9703         }
9704         var row = this.getRowDom(rowIndex);
9705         row.removeClass(['bg-info','info']);
9706     },
9707       /**
9708      * Focuses the specified row.
9709      * @param {Number} row The row index
9710      */
9711     focusRow : function(row)
9712     {
9713         //Roo.log('GridView.focusRow');
9714         var x = this.bodyEl.dom.scrollLeft;
9715         this.focusCell(row, 0, false);
9716         this.bodyEl.dom.scrollLeft = x;
9717
9718     },
9719      /**
9720      * Focuses the specified cell.
9721      * @param {Number} row The row index
9722      * @param {Number} col The column index
9723      * @param {Boolean} hscroll false to disable horizontal scrolling
9724      */
9725     focusCell : function(row, col, hscroll)
9726     {
9727         //Roo.log('GridView.focusCell');
9728         var el = this.ensureVisible(row, col, hscroll);
9729         // not sure what focusEL achives = it's a <a> pos relative 
9730         //this.focusEl.alignTo(el, "tl-tl");
9731         //if(Roo.isGecko){
9732         //    this.focusEl.focus();
9733         //}else{
9734         //    this.focusEl.focus.defer(1, this.focusEl);
9735         //}
9736     },
9737     
9738      /**
9739      * Scrolls the specified cell into view
9740      * @param {Number} row The row index
9741      * @param {Number} col The column index
9742      * @param {Boolean} hscroll false to disable horizontal scrolling
9743      */
9744     ensureVisible : function(row, col, hscroll)
9745     {
9746         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9747         //return null; //disable for testing.
9748         if(typeof row != "number"){
9749             row = row.rowIndex;
9750         }
9751         if(row < 0 && row >= this.ds.getCount()){
9752             return  null;
9753         }
9754         col = (col !== undefined ? col : 0);
9755         var cm = this.cm;
9756         while(cm.isHidden(col)){
9757             col++;
9758         }
9759
9760         var el = this.getCellDom(row, col);
9761         if(!el){
9762             return null;
9763         }
9764         var c = this.bodyEl.dom;
9765
9766         var ctop = parseInt(el.offsetTop, 10);
9767         var cleft = parseInt(el.offsetLeft, 10);
9768         var cbot = ctop + el.offsetHeight;
9769         var cright = cleft + el.offsetWidth;
9770
9771         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9772         var ch = 0; //?? header is not withing the area?
9773         var stop = parseInt(c.scrollTop, 10);
9774         var sleft = parseInt(c.scrollLeft, 10);
9775         var sbot = stop + ch;
9776         var sright = sleft + c.clientWidth;
9777         /*
9778         Roo.log('GridView.ensureVisible:' +
9779                 ' ctop:' + ctop +
9780                 ' c.clientHeight:' + c.clientHeight +
9781                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9782                 ' stop:' + stop +
9783                 ' cbot:' + cbot +
9784                 ' sbot:' + sbot +
9785                 ' ch:' + ch  
9786                 );
9787         */
9788         if(ctop < stop){
9789             c.scrollTop = ctop;
9790             //Roo.log("set scrolltop to ctop DISABLE?");
9791         }else if(cbot > sbot){
9792             //Roo.log("set scrolltop to cbot-ch");
9793             c.scrollTop = cbot-ch;
9794         }
9795
9796         if(hscroll !== false){
9797             if(cleft < sleft){
9798                 c.scrollLeft = cleft;
9799             }else if(cright > sright){
9800                 c.scrollLeft = cright-c.clientWidth;
9801             }
9802         }
9803
9804         return el;
9805     },
9806     
9807     
9808     insertRow : function(dm, rowIndex, isUpdate){
9809         
9810         if(!isUpdate){
9811             this.fireEvent("beforerowsinserted", this, rowIndex);
9812         }
9813             //var s = this.getScrollState();
9814         var row = this.renderRow(this.cm, this.store, rowIndex);
9815         // insert before rowIndex..
9816         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9817         
9818         var _this = this;
9819                 
9820         if(row.cellObjects.length){
9821             Roo.each(row.cellObjects, function(r){
9822                 _this.renderCellObject(r);
9823             })
9824         }
9825             
9826         if(!isUpdate){
9827             this.fireEvent("rowsinserted", this, rowIndex);
9828             //this.syncRowHeights(firstRow, lastRow);
9829             //this.stripeRows(firstRow);
9830             //this.layout();
9831         }
9832         
9833     },
9834     
9835     
9836     getRowDom : function(rowIndex)
9837     {
9838         var rows = this.el.select('tbody > tr', true).elements;
9839         
9840         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9841         
9842     },
9843     getCellDom : function(rowIndex, colIndex)
9844     {
9845         var row = this.getRowDom(rowIndex);
9846         if (row === false) {
9847             return false;
9848         }
9849         var cols = row.select('td', true).elements;
9850         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9851         
9852     },
9853     
9854     // returns the object tree for a tr..
9855   
9856     
9857     renderRow : function(cm, ds, rowIndex) 
9858     {
9859         var d = ds.getAt(rowIndex);
9860         
9861         var row = {
9862             tag : 'tr',
9863             cls : 'x-row-' + rowIndex,
9864             cn : []
9865         };
9866             
9867         var cellObjects = [];
9868         
9869         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9870             var config = cm.config[i];
9871             
9872             var renderer = cm.getRenderer(i);
9873             var value = '';
9874             var id = false;
9875             
9876             if(typeof(renderer) !== 'undefined'){
9877                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9878             }
9879             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9880             // and are rendered into the cells after the row is rendered - using the id for the element.
9881             
9882             if(typeof(value) === 'object'){
9883                 id = Roo.id();
9884                 cellObjects.push({
9885                     container : id,
9886                     cfg : value 
9887                 })
9888             }
9889             
9890             var rowcfg = {
9891                 record: d,
9892                 rowIndex : rowIndex,
9893                 colIndex : i,
9894                 rowClass : ''
9895             };
9896
9897             this.fireEvent('rowclass', this, rowcfg);
9898             
9899             var td = {
9900                 tag: 'td',
9901                 // this might end up displaying HTML?
9902                 // this is too messy... - better to only do it on columsn you know are going to be too long
9903                 //tooltip : (typeof(value) === 'object') ? '' : value,
9904                 cls : rowcfg.rowClass + ' x-col-' + i,
9905                 style: '',
9906                 html: (typeof(value) === 'object') ? '' : value
9907             };
9908             
9909             if (id) {
9910                 td.id = id;
9911             }
9912             
9913             if(typeof(config.colspan) != 'undefined'){
9914                 td.colspan = config.colspan;
9915             }
9916             
9917             
9918             
9919             if(typeof(config.align) != 'undefined' && config.align.length){
9920                 td.style += ' text-align:' + config.align + ';';
9921             }
9922             if(typeof(config.valign) != 'undefined' && config.valign.length){
9923                 td.style += ' vertical-align:' + config.valign + ';';
9924             }
9925             /*
9926             if(typeof(config.width) != 'undefined'){
9927                 td.style += ' width:' +  config.width + 'px;';
9928             }
9929             */
9930             
9931             if(typeof(config.cursor) != 'undefined'){
9932                 td.style += ' cursor:' +  config.cursor + ';';
9933             }
9934             
9935             if(typeof(config.cls) != 'undefined'){
9936                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9937             }
9938             if (this.responsive) {
9939                 ['xs','sm','md','lg'].map(function(size){
9940                     
9941                     if(typeof(config[size]) == 'undefined'){
9942                         return;
9943                     }
9944                     
9945                     
9946                       
9947                     if (!config[size]) { // 0 = hidden
9948                         // BS 4 '0' is treated as hide that column and below.
9949                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9950                         return;
9951                     }
9952                     
9953                     td.cls += ' col-' + size + '-' + config[size] + (
9954                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9955                     );
9956                      
9957     
9958                 });
9959             }
9960             row.cn.push(td);
9961            
9962         }
9963         
9964         row.cellObjects = cellObjects;
9965         
9966         return row;
9967           
9968     },
9969     
9970     
9971     
9972     onBeforeLoad : function()
9973     {
9974         
9975     },
9976      /**
9977      * Remove all rows
9978      */
9979     clear : function()
9980     {
9981         this.el.select('tbody', true).first().dom.innerHTML = '';
9982     },
9983     /**
9984      * Show or hide a row.
9985      * @param {Number} rowIndex to show or hide
9986      * @param {Boolean} state hide
9987      */
9988     setRowVisibility : function(rowIndex, state)
9989     {
9990         var bt = this.bodyEl.dom;
9991         
9992         var rows = this.el.select('tbody > tr', true).elements;
9993         
9994         if(typeof(rows[rowIndex]) == 'undefined'){
9995             return;
9996         }
9997         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9998         
9999     },
10000     
10001     
10002     getSelectionModel : function(){
10003         if(!this.selModel){
10004             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10005         }
10006         return this.selModel;
10007     },
10008     /*
10009      * Render the Roo.bootstrap object from renderder
10010      */
10011     renderCellObject : function(r)
10012     {
10013         var _this = this;
10014         
10015         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10016         
10017         var t = r.cfg.render(r.container);
10018         
10019         if(r.cfg.cn){
10020             Roo.each(r.cfg.cn, function(c){
10021                 var child = {
10022                     container: t.getChildContainer(),
10023                     cfg: c
10024                 };
10025                 _this.renderCellObject(child);
10026             })
10027         }
10028     },
10029     /**
10030      * get the Row Index from a dom element.
10031      * @param {Roo.Element} row The row to look for
10032      * @returns {Number} the row
10033      */
10034     getRowIndex : function(row)
10035     {
10036         var rowIndex = -1;
10037         
10038         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10039             if(el != row){
10040                 return;
10041             }
10042             
10043             rowIndex = index;
10044         });
10045         
10046         return rowIndex;
10047     },
10048     /**
10049      * get the header TH element for columnIndex
10050      * @param {Number} columnIndex
10051      * @returns {Roo.Element}
10052      */
10053     getHeaderIndex: function(colIndex)
10054     {
10055         var cols = this.headEl.select('th', true).elements;
10056         return cols[colIndex]; 
10057     },
10058     /**
10059      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10060      * @param {domElement} cell to look for
10061      * @returns {Number} the column
10062      */
10063     getCellIndex : function(cell)
10064     {
10065         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10066         if(id){
10067             return parseInt(id[1], 10);
10068         }
10069         return 0;
10070     },
10071      /**
10072      * Returns the grid's underlying element = used by panel.Grid
10073      * @return {Element} The element
10074      */
10075     getGridEl : function(){
10076         return this.el;
10077     },
10078      /**
10079      * Forces a resize - used by panel.Grid
10080      * @return {Element} The element
10081      */
10082     autoSize : function()
10083     {
10084         //var ctr = Roo.get(this.container.dom.parentElement);
10085         var ctr = Roo.get(this.el.dom);
10086         
10087         var thd = this.getGridEl().select('thead',true).first();
10088         var tbd = this.getGridEl().select('tbody', true).first();
10089         var tfd = this.getGridEl().select('tfoot', true).first();
10090         
10091         var cw = ctr.getWidth();
10092         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10093         
10094         if (tbd) {
10095             
10096             tbd.setWidth(ctr.getWidth());
10097             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10098             // this needs fixing for various usage - currently only hydra job advers I think..
10099             //tdb.setHeight(
10100             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10101             //); 
10102             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10103             cw -= barsize;
10104         }
10105         cw = Math.max(cw, this.totalWidth);
10106         this.getGridEl().select('tbody tr',true).setWidth(cw);
10107         this.initCSS();
10108         
10109         // resize 'expandable coloumn?
10110         
10111         return; // we doe not have a view in this design..
10112         
10113     },
10114     onBodyScroll: function()
10115     {
10116         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10117         if(this.headEl){
10118             this.headEl.setStyle({
10119                 'position' : 'relative',
10120                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10121             });
10122         }
10123         
10124         if(this.lazyLoad){
10125             
10126             var scrollHeight = this.bodyEl.dom.scrollHeight;
10127             
10128             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10129             
10130             var height = this.bodyEl.getHeight();
10131             
10132             if(scrollHeight - height == scrollTop) {
10133                 
10134                 var total = this.ds.getTotalCount();
10135                 
10136                 if(this.footer.cursor + this.footer.pageSize < total){
10137                     
10138                     this.footer.ds.load({
10139                         params : {
10140                             start : this.footer.cursor + this.footer.pageSize,
10141                             limit : this.footer.pageSize
10142                         },
10143                         add : true
10144                     });
10145                 }
10146             }
10147             
10148         }
10149     },
10150     onColumnSplitterMoved : function(i, diff)
10151     {
10152         this.userResized = true;
10153         
10154         var cm = this.colModel;
10155         
10156         var w = this.getHeaderIndex(i).getWidth() + diff;
10157         
10158         
10159         cm.setColumnWidth(i, w, true);
10160         this.initCSS();
10161         //var cid = cm.getColumnId(i); << not used in this version?
10162        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10163         
10164         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10165         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10166         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10167 */
10168         //this.updateSplitters();
10169         //this.layout(); << ??
10170         this.fireEvent("columnresize", i, w);
10171     },
10172     onHeaderChange : function()
10173     {
10174         var header = this.renderHeader();
10175         var table = this.el.select('table', true).first();
10176         
10177         this.headEl.remove();
10178         this.headEl = table.createChild(header, this.bodyEl, false);
10179         
10180         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10181             e.on('click', this.sort, this);
10182         }, this);
10183         
10184         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10185             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10186         }
10187         
10188     },
10189     
10190     onHiddenChange : function(colModel, colIndex, hidden)
10191     {
10192         /*
10193         this.cm.setHidden()
10194         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10195         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10196         
10197         this.CSS.updateRule(thSelector, "display", "");
10198         this.CSS.updateRule(tdSelector, "display", "");
10199         
10200         if(hidden){
10201             this.CSS.updateRule(thSelector, "display", "none");
10202             this.CSS.updateRule(tdSelector, "display", "none");
10203         }
10204         */
10205         // onload calls initCSS()
10206         this.onHeaderChange();
10207         this.onLoad();
10208     },
10209     
10210     setColumnWidth: function(col_index, width)
10211     {
10212         // width = "md-2 xs-2..."
10213         if(!this.colModel.config[col_index]) {
10214             return;
10215         }
10216         
10217         var w = width.split(" ");
10218         
10219         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10220         
10221         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10222         
10223         
10224         for(var j = 0; j < w.length; j++) {
10225             
10226             if(!w[j]) {
10227                 continue;
10228             }
10229             
10230             var size_cls = w[j].split("-");
10231             
10232             if(!Number.isInteger(size_cls[1] * 1)) {
10233                 continue;
10234             }
10235             
10236             if(!this.colModel.config[col_index][size_cls[0]]) {
10237                 continue;
10238             }
10239             
10240             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10241                 continue;
10242             }
10243             
10244             h_row[0].classList.replace(
10245                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10246                 "col-"+size_cls[0]+"-"+size_cls[1]
10247             );
10248             
10249             for(var i = 0; i < rows.length; i++) {
10250                 
10251                 var size_cls = w[j].split("-");
10252                 
10253                 if(!Number.isInteger(size_cls[1] * 1)) {
10254                     continue;
10255                 }
10256                 
10257                 if(!this.colModel.config[col_index][size_cls[0]]) {
10258                     continue;
10259                 }
10260                 
10261                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10262                     continue;
10263                 }
10264                 
10265                 rows[i].classList.replace(
10266                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10267                     "col-"+size_cls[0]+"-"+size_cls[1]
10268                 );
10269             }
10270             
10271             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10272         }
10273     }
10274 });
10275
10276 // currently only used to find the split on drag.. 
10277 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10278
10279 /**
10280  * @depricated
10281 */
10282 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10283 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10284 /*
10285  * - LGPL
10286  *
10287  * table cell
10288  * 
10289  */
10290
10291 /**
10292  * @class Roo.bootstrap.TableCell
10293  * @extends Roo.bootstrap.Component
10294  * Bootstrap TableCell class
10295  * @cfg {String} html cell contain text
10296  * @cfg {String} cls cell class
10297  * @cfg {String} tag cell tag (td|th) default td
10298  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10299  * @cfg {String} align Aligns the content in a cell
10300  * @cfg {String} axis Categorizes cells
10301  * @cfg {String} bgcolor Specifies the background color of a cell
10302  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10303  * @cfg {Number} colspan Specifies the number of columns a cell should span
10304  * @cfg {String} headers Specifies one or more header cells a cell is related to
10305  * @cfg {Number} height Sets the height of a cell
10306  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10307  * @cfg {Number} rowspan Sets the number of rows a cell should span
10308  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10309  * @cfg {String} valign Vertical aligns the content in a cell
10310  * @cfg {Number} width Specifies the width of a cell
10311  * 
10312  * @constructor
10313  * Create a new TableCell
10314  * @param {Object} config The config object
10315  */
10316
10317 Roo.bootstrap.TableCell = function(config){
10318     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10319 };
10320
10321 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10322     
10323     html: false,
10324     cls: false,
10325     tag: false,
10326     abbr: false,
10327     align: false,
10328     axis: false,
10329     bgcolor: false,
10330     charoff: false,
10331     colspan: false,
10332     headers: false,
10333     height: false,
10334     nowrap: false,
10335     rowspan: false,
10336     scope: false,
10337     valign: false,
10338     width: false,
10339     
10340     
10341     getAutoCreate : function(){
10342         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10343         
10344         cfg = {
10345             tag: 'td'
10346         };
10347         
10348         if(this.tag){
10349             cfg.tag = this.tag;
10350         }
10351         
10352         if (this.html) {
10353             cfg.html=this.html
10354         }
10355         if (this.cls) {
10356             cfg.cls=this.cls
10357         }
10358         if (this.abbr) {
10359             cfg.abbr=this.abbr
10360         }
10361         if (this.align) {
10362             cfg.align=this.align
10363         }
10364         if (this.axis) {
10365             cfg.axis=this.axis
10366         }
10367         if (this.bgcolor) {
10368             cfg.bgcolor=this.bgcolor
10369         }
10370         if (this.charoff) {
10371             cfg.charoff=this.charoff
10372         }
10373         if (this.colspan) {
10374             cfg.colspan=this.colspan
10375         }
10376         if (this.headers) {
10377             cfg.headers=this.headers
10378         }
10379         if (this.height) {
10380             cfg.height=this.height
10381         }
10382         if (this.nowrap) {
10383             cfg.nowrap=this.nowrap
10384         }
10385         if (this.rowspan) {
10386             cfg.rowspan=this.rowspan
10387         }
10388         if (this.scope) {
10389             cfg.scope=this.scope
10390         }
10391         if (this.valign) {
10392             cfg.valign=this.valign
10393         }
10394         if (this.width) {
10395             cfg.width=this.width
10396         }
10397         
10398         
10399         return cfg;
10400     }
10401    
10402 });
10403
10404  
10405
10406  /*
10407  * - LGPL
10408  *
10409  * table row
10410  * 
10411  */
10412
10413 /**
10414  * @class Roo.bootstrap.TableRow
10415  * @extends Roo.bootstrap.Component
10416  * Bootstrap TableRow class
10417  * @cfg {String} cls row class
10418  * @cfg {String} align Aligns the content in a table row
10419  * @cfg {String} bgcolor Specifies a background color for a table row
10420  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10421  * @cfg {String} valign Vertical aligns the content in a table row
10422  * 
10423  * @constructor
10424  * Create a new TableRow
10425  * @param {Object} config The config object
10426  */
10427
10428 Roo.bootstrap.TableRow = function(config){
10429     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10430 };
10431
10432 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10433     
10434     cls: false,
10435     align: false,
10436     bgcolor: false,
10437     charoff: false,
10438     valign: false,
10439     
10440     getAutoCreate : function(){
10441         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10442         
10443         cfg = {
10444             tag: 'tr'
10445         };
10446             
10447         if(this.cls){
10448             cfg.cls = this.cls;
10449         }
10450         if(this.align){
10451             cfg.align = this.align;
10452         }
10453         if(this.bgcolor){
10454             cfg.bgcolor = this.bgcolor;
10455         }
10456         if(this.charoff){
10457             cfg.charoff = this.charoff;
10458         }
10459         if(this.valign){
10460             cfg.valign = this.valign;
10461         }
10462         
10463         return cfg;
10464     }
10465    
10466 });
10467
10468  
10469
10470  /*
10471  * - LGPL
10472  *
10473  * table body
10474  * 
10475  */
10476
10477 /**
10478  * @class Roo.bootstrap.TableBody
10479  * @extends Roo.bootstrap.Component
10480  * Bootstrap TableBody class
10481  * @cfg {String} cls element class
10482  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10483  * @cfg {String} align Aligns the content inside the element
10484  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10485  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10486  * 
10487  * @constructor
10488  * Create a new TableBody
10489  * @param {Object} config The config object
10490  */
10491
10492 Roo.bootstrap.TableBody = function(config){
10493     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10494 };
10495
10496 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10497     
10498     cls: false,
10499     tag: false,
10500     align: false,
10501     charoff: false,
10502     valign: false,
10503     
10504     getAutoCreate : function(){
10505         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10506         
10507         cfg = {
10508             tag: 'tbody'
10509         };
10510             
10511         if (this.cls) {
10512             cfg.cls=this.cls
10513         }
10514         if(this.tag){
10515             cfg.tag = this.tag;
10516         }
10517         
10518         if(this.align){
10519             cfg.align = this.align;
10520         }
10521         if(this.charoff){
10522             cfg.charoff = this.charoff;
10523         }
10524         if(this.valign){
10525             cfg.valign = this.valign;
10526         }
10527         
10528         return cfg;
10529     }
10530     
10531     
10532 //    initEvents : function()
10533 //    {
10534 //        
10535 //        if(!this.store){
10536 //            return;
10537 //        }
10538 //        
10539 //        this.store = Roo.factory(this.store, Roo.data);
10540 //        this.store.on('load', this.onLoad, this);
10541 //        
10542 //        this.store.load();
10543 //        
10544 //    },
10545 //    
10546 //    onLoad: function () 
10547 //    {   
10548 //        this.fireEvent('load', this);
10549 //    }
10550 //    
10551 //   
10552 });
10553
10554  
10555
10556  /*
10557  * Based on:
10558  * Ext JS Library 1.1.1
10559  * Copyright(c) 2006-2007, Ext JS, LLC.
10560  *
10561  * Originally Released Under LGPL - original licence link has changed is not relivant.
10562  *
10563  * Fork - LGPL
10564  * <script type="text/javascript">
10565  */
10566
10567 // as we use this in bootstrap.
10568 Roo.namespace('Roo.form');
10569  /**
10570  * @class Roo.form.Action
10571  * Internal Class used to handle form actions
10572  * @constructor
10573  * @param {Roo.form.BasicForm} el The form element or its id
10574  * @param {Object} config Configuration options
10575  */
10576
10577  
10578  
10579 // define the action interface
10580 Roo.form.Action = function(form, options){
10581     this.form = form;
10582     this.options = options || {};
10583 };
10584 /**
10585  * Client Validation Failed
10586  * @const 
10587  */
10588 Roo.form.Action.CLIENT_INVALID = 'client';
10589 /**
10590  * Server Validation Failed
10591  * @const 
10592  */
10593 Roo.form.Action.SERVER_INVALID = 'server';
10594  /**
10595  * Connect to Server Failed
10596  * @const 
10597  */
10598 Roo.form.Action.CONNECT_FAILURE = 'connect';
10599 /**
10600  * Reading Data from Server Failed
10601  * @const 
10602  */
10603 Roo.form.Action.LOAD_FAILURE = 'load';
10604
10605 Roo.form.Action.prototype = {
10606     type : 'default',
10607     failureType : undefined,
10608     response : undefined,
10609     result : undefined,
10610
10611     // interface method
10612     run : function(options){
10613
10614     },
10615
10616     // interface method
10617     success : function(response){
10618
10619     },
10620
10621     // interface method
10622     handleResponse : function(response){
10623
10624     },
10625
10626     // default connection failure
10627     failure : function(response){
10628         
10629         this.response = response;
10630         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10631         this.form.afterAction(this, false);
10632     },
10633
10634     processResponse : function(response){
10635         this.response = response;
10636         if(!response.responseText){
10637             return true;
10638         }
10639         this.result = this.handleResponse(response);
10640         return this.result;
10641     },
10642
10643     // utility functions used internally
10644     getUrl : function(appendParams){
10645         var url = this.options.url || this.form.url || this.form.el.dom.action;
10646         if(appendParams){
10647             var p = this.getParams();
10648             if(p){
10649                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10650             }
10651         }
10652         return url;
10653     },
10654
10655     getMethod : function(){
10656         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10657     },
10658
10659     getParams : function(){
10660         var bp = this.form.baseParams;
10661         var p = this.options.params;
10662         if(p){
10663             if(typeof p == "object"){
10664                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10665             }else if(typeof p == 'string' && bp){
10666                 p += '&' + Roo.urlEncode(bp);
10667             }
10668         }else if(bp){
10669             p = Roo.urlEncode(bp);
10670         }
10671         return p;
10672     },
10673
10674     createCallback : function(){
10675         return {
10676             success: this.success,
10677             failure: this.failure,
10678             scope: this,
10679             timeout: (this.form.timeout*1000),
10680             upload: this.form.fileUpload ? this.success : undefined
10681         };
10682     }
10683 };
10684
10685 Roo.form.Action.Submit = function(form, options){
10686     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10687 };
10688
10689 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10690     type : 'submit',
10691
10692     haveProgress : false,
10693     uploadComplete : false,
10694     
10695     // uploadProgress indicator.
10696     uploadProgress : function()
10697     {
10698         if (!this.form.progressUrl) {
10699             return;
10700         }
10701         
10702         if (!this.haveProgress) {
10703             Roo.MessageBox.progress("Uploading", "Uploading");
10704         }
10705         if (this.uploadComplete) {
10706            Roo.MessageBox.hide();
10707            return;
10708         }
10709         
10710         this.haveProgress = true;
10711    
10712         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10713         
10714         var c = new Roo.data.Connection();
10715         c.request({
10716             url : this.form.progressUrl,
10717             params: {
10718                 id : uid
10719             },
10720             method: 'GET',
10721             success : function(req){
10722                //console.log(data);
10723                 var rdata = false;
10724                 var edata;
10725                 try  {
10726                    rdata = Roo.decode(req.responseText)
10727                 } catch (e) {
10728                     Roo.log("Invalid data from server..");
10729                     Roo.log(edata);
10730                     return;
10731                 }
10732                 if (!rdata || !rdata.success) {
10733                     Roo.log(rdata);
10734                     Roo.MessageBox.alert(Roo.encode(rdata));
10735                     return;
10736                 }
10737                 var data = rdata.data;
10738                 
10739                 if (this.uploadComplete) {
10740                    Roo.MessageBox.hide();
10741                    return;
10742                 }
10743                    
10744                 if (data){
10745                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10746                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10747                     );
10748                 }
10749                 this.uploadProgress.defer(2000,this);
10750             },
10751        
10752             failure: function(data) {
10753                 Roo.log('progress url failed ');
10754                 Roo.log(data);
10755             },
10756             scope : this
10757         });
10758            
10759     },
10760     
10761     
10762     run : function()
10763     {
10764         // run get Values on the form, so it syncs any secondary forms.
10765         this.form.getValues();
10766         
10767         var o = this.options;
10768         var method = this.getMethod();
10769         var isPost = method == 'POST';
10770         if(o.clientValidation === false || this.form.isValid()){
10771             
10772             if (this.form.progressUrl) {
10773                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10774                     (new Date() * 1) + '' + Math.random());
10775                     
10776             } 
10777             
10778             
10779             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10780                 form:this.form.el.dom,
10781                 url:this.getUrl(!isPost),
10782                 method: method,
10783                 params:isPost ? this.getParams() : null,
10784                 isUpload: this.form.fileUpload,
10785                 formData : this.form.formData
10786             }));
10787             
10788             this.uploadProgress();
10789
10790         }else if (o.clientValidation !== false){ // client validation failed
10791             this.failureType = Roo.form.Action.CLIENT_INVALID;
10792             this.form.afterAction(this, false);
10793         }
10794     },
10795
10796     success : function(response)
10797     {
10798         this.uploadComplete= true;
10799         if (this.haveProgress) {
10800             Roo.MessageBox.hide();
10801         }
10802         
10803         
10804         var result = this.processResponse(response);
10805         if(result === true || result.success){
10806             this.form.afterAction(this, true);
10807             return;
10808         }
10809         if(result.errors){
10810             this.form.markInvalid(result.errors);
10811             this.failureType = Roo.form.Action.SERVER_INVALID;
10812         }
10813         this.form.afterAction(this, false);
10814     },
10815     failure : function(response)
10816     {
10817         this.uploadComplete= true;
10818         if (this.haveProgress) {
10819             Roo.MessageBox.hide();
10820         }
10821         
10822         this.response = response;
10823         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10824         this.form.afterAction(this, false);
10825     },
10826     
10827     handleResponse : function(response){
10828         if(this.form.errorReader){
10829             var rs = this.form.errorReader.read(response);
10830             var errors = [];
10831             if(rs.records){
10832                 for(var i = 0, len = rs.records.length; i < len; i++) {
10833                     var r = rs.records[i];
10834                     errors[i] = r.data;
10835                 }
10836             }
10837             if(errors.length < 1){
10838                 errors = null;
10839             }
10840             return {
10841                 success : rs.success,
10842                 errors : errors
10843             };
10844         }
10845         var ret = false;
10846         try {
10847             ret = Roo.decode(response.responseText);
10848         } catch (e) {
10849             ret = {
10850                 success: false,
10851                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10852                 errors : []
10853             };
10854         }
10855         return ret;
10856         
10857     }
10858 });
10859
10860
10861 Roo.form.Action.Load = function(form, options){
10862     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10863     this.reader = this.form.reader;
10864 };
10865
10866 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10867     type : 'load',
10868
10869     run : function(){
10870         
10871         Roo.Ajax.request(Roo.apply(
10872                 this.createCallback(), {
10873                     method:this.getMethod(),
10874                     url:this.getUrl(false),
10875                     params:this.getParams()
10876         }));
10877     },
10878
10879     success : function(response){
10880         
10881         var result = this.processResponse(response);
10882         if(result === true || !result.success || !result.data){
10883             this.failureType = Roo.form.Action.LOAD_FAILURE;
10884             this.form.afterAction(this, false);
10885             return;
10886         }
10887         this.form.clearInvalid();
10888         this.form.setValues(result.data);
10889         this.form.afterAction(this, true);
10890     },
10891
10892     handleResponse : function(response){
10893         if(this.form.reader){
10894             var rs = this.form.reader.read(response);
10895             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10896             return {
10897                 success : rs.success,
10898                 data : data
10899             };
10900         }
10901         return Roo.decode(response.responseText);
10902     }
10903 });
10904
10905 Roo.form.Action.ACTION_TYPES = {
10906     'load' : Roo.form.Action.Load,
10907     'submit' : Roo.form.Action.Submit
10908 };/*
10909  * - LGPL
10910  *
10911  * form
10912  *
10913  */
10914
10915 /**
10916  * @class Roo.bootstrap.Form
10917  * @extends Roo.bootstrap.Component
10918  * Bootstrap Form class
10919  * @cfg {String} method  GET | POST (default POST)
10920  * @cfg {String} labelAlign top | left (default top)
10921  * @cfg {String} align left  | right - for navbars
10922  * @cfg {Boolean} loadMask load mask when submit (default true)
10923
10924  *
10925  * @constructor
10926  * Create a new Form
10927  * @param {Object} config The config object
10928  */
10929
10930
10931 Roo.bootstrap.Form = function(config){
10932     
10933     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10934     
10935     Roo.bootstrap.Form.popover.apply();
10936     
10937     this.addEvents({
10938         /**
10939          * @event clientvalidation
10940          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10941          * @param {Form} this
10942          * @param {Boolean} valid true if the form has passed client-side validation
10943          */
10944         clientvalidation: true,
10945         /**
10946          * @event beforeaction
10947          * Fires before any action is performed. Return false to cancel the action.
10948          * @param {Form} this
10949          * @param {Action} action The action to be performed
10950          */
10951         beforeaction: true,
10952         /**
10953          * @event actionfailed
10954          * Fires when an action fails.
10955          * @param {Form} this
10956          * @param {Action} action The action that failed
10957          */
10958         actionfailed : true,
10959         /**
10960          * @event actioncomplete
10961          * Fires when an action is completed.
10962          * @param {Form} this
10963          * @param {Action} action The action that completed
10964          */
10965         actioncomplete : true
10966     });
10967 };
10968
10969 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10970
10971      /**
10972      * @cfg {String} method
10973      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10974      */
10975     method : 'POST',
10976     /**
10977      * @cfg {String} url
10978      * The URL to use for form actions if one isn't supplied in the action options.
10979      */
10980     /**
10981      * @cfg {Boolean} fileUpload
10982      * Set to true if this form is a file upload.
10983      */
10984
10985     /**
10986      * @cfg {Object} baseParams
10987      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10988      */
10989
10990     /**
10991      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10992      */
10993     timeout: 30,
10994     /**
10995      * @cfg {Sting} align (left|right) for navbar forms
10996      */
10997     align : 'left',
10998
10999     // private
11000     activeAction : null,
11001
11002     /**
11003      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11004      * element by passing it or its id or mask the form itself by passing in true.
11005      * @type Mixed
11006      */
11007     waitMsgTarget : false,
11008
11009     loadMask : true,
11010     
11011     /**
11012      * @cfg {Boolean} errorMask (true|false) default false
11013      */
11014     errorMask : false,
11015     
11016     /**
11017      * @cfg {Number} maskOffset Default 100
11018      */
11019     maskOffset : 100,
11020     
11021     /**
11022      * @cfg {Boolean} maskBody
11023      */
11024     maskBody : false,
11025
11026     getAutoCreate : function(){
11027
11028         var cfg = {
11029             tag: 'form',
11030             method : this.method || 'POST',
11031             id : this.id || Roo.id(),
11032             cls : ''
11033         };
11034         if (this.parent().xtype.match(/^Nav/)) {
11035             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11036
11037         }
11038
11039         if (this.labelAlign == 'left' ) {
11040             cfg.cls += ' form-horizontal';
11041         }
11042
11043
11044         return cfg;
11045     },
11046     initEvents : function()
11047     {
11048         this.el.on('submit', this.onSubmit, this);
11049         // this was added as random key presses on the form where triggering form submit.
11050         this.el.on('keypress', function(e) {
11051             if (e.getCharCode() != 13) {
11052                 return true;
11053             }
11054             // we might need to allow it for textareas.. and some other items.
11055             // check e.getTarget().
11056
11057             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11058                 return true;
11059             }
11060
11061             Roo.log("keypress blocked");
11062
11063             e.preventDefault();
11064             return false;
11065         });
11066         
11067     },
11068     // private
11069     onSubmit : function(e){
11070         e.stopEvent();
11071     },
11072
11073      /**
11074      * Returns true if client-side validation on the form is successful.
11075      * @return Boolean
11076      */
11077     isValid : function(){
11078         var items = this.getItems();
11079         var valid = true;
11080         var target = false;
11081         
11082         items.each(function(f){
11083             
11084             if(f.validate()){
11085                 return;
11086             }
11087             
11088             Roo.log('invalid field: ' + f.name);
11089             
11090             valid = false;
11091
11092             if(!target && f.el.isVisible(true)){
11093                 target = f;
11094             }
11095            
11096         });
11097         
11098         if(this.errorMask && !valid){
11099             Roo.bootstrap.Form.popover.mask(this, target);
11100         }
11101         
11102         return valid;
11103     },
11104     
11105     /**
11106      * Returns true if any fields in this form have changed since their original load.
11107      * @return Boolean
11108      */
11109     isDirty : function(){
11110         var dirty = false;
11111         var items = this.getItems();
11112         items.each(function(f){
11113            if(f.isDirty()){
11114                dirty = true;
11115                return false;
11116            }
11117            return true;
11118         });
11119         return dirty;
11120     },
11121      /**
11122      * Performs a predefined action (submit or load) or custom actions you define on this form.
11123      * @param {String} actionName The name of the action type
11124      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11125      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11126      * accept other config options):
11127      * <pre>
11128 Property          Type             Description
11129 ----------------  ---------------  ----------------------------------------------------------------------------------
11130 url               String           The url for the action (defaults to the form's url)
11131 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11132 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11133 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11134                                    validate the form on the client (defaults to false)
11135      * </pre>
11136      * @return {BasicForm} this
11137      */
11138     doAction : function(action, options){
11139         if(typeof action == 'string'){
11140             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11141         }
11142         if(this.fireEvent('beforeaction', this, action) !== false){
11143             this.beforeAction(action);
11144             action.run.defer(100, action);
11145         }
11146         return this;
11147     },
11148
11149     // private
11150     beforeAction : function(action){
11151         var o = action.options;
11152         
11153         if(this.loadMask){
11154             
11155             if(this.maskBody){
11156                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11157             } else {
11158                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11159             }
11160         }
11161         // not really supported yet.. ??
11162
11163         //if(this.waitMsgTarget === true){
11164         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11165         //}else if(this.waitMsgTarget){
11166         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11167         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11168         //}else {
11169         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11170        // }
11171
11172     },
11173
11174     // private
11175     afterAction : function(action, success){
11176         this.activeAction = null;
11177         var o = action.options;
11178
11179         if(this.loadMask){
11180             
11181             if(this.maskBody){
11182                 Roo.get(document.body).unmask();
11183             } else {
11184                 this.el.unmask();
11185             }
11186         }
11187         
11188         //if(this.waitMsgTarget === true){
11189 //            this.el.unmask();
11190         //}else if(this.waitMsgTarget){
11191         //    this.waitMsgTarget.unmask();
11192         //}else{
11193         //    Roo.MessageBox.updateProgress(1);
11194         //    Roo.MessageBox.hide();
11195        // }
11196         //
11197         if(success){
11198             if(o.reset){
11199                 this.reset();
11200             }
11201             Roo.callback(o.success, o.scope, [this, action]);
11202             this.fireEvent('actioncomplete', this, action);
11203
11204         }else{
11205
11206             // failure condition..
11207             // we have a scenario where updates need confirming.
11208             // eg. if a locking scenario exists..
11209             // we look for { errors : { needs_confirm : true }} in the response.
11210             if (
11211                 (typeof(action.result) != 'undefined')  &&
11212                 (typeof(action.result.errors) != 'undefined')  &&
11213                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11214            ){
11215                 var _t = this;
11216                 Roo.log("not supported yet");
11217                  /*
11218
11219                 Roo.MessageBox.confirm(
11220                     "Change requires confirmation",
11221                     action.result.errorMsg,
11222                     function(r) {
11223                         if (r != 'yes') {
11224                             return;
11225                         }
11226                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11227                     }
11228
11229                 );
11230                 */
11231
11232
11233                 return;
11234             }
11235
11236             Roo.callback(o.failure, o.scope, [this, action]);
11237             // show an error message if no failed handler is set..
11238             if (!this.hasListener('actionfailed')) {
11239                 Roo.log("need to add dialog support");
11240                 /*
11241                 Roo.MessageBox.alert("Error",
11242                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11243                         action.result.errorMsg :
11244                         "Saving Failed, please check your entries or try again"
11245                 );
11246                 */
11247             }
11248
11249             this.fireEvent('actionfailed', this, action);
11250         }
11251
11252     },
11253     /**
11254      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11255      * @param {String} id The value to search for
11256      * @return Field
11257      */
11258     findField : function(id){
11259         var items = this.getItems();
11260         var field = items.get(id);
11261         if(!field){
11262              items.each(function(f){
11263                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11264                     field = f;
11265                     return false;
11266                 }
11267                 return true;
11268             });
11269         }
11270         return field || null;
11271     },
11272      /**
11273      * Mark fields in this form invalid in bulk.
11274      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11275      * @return {BasicForm} this
11276      */
11277     markInvalid : function(errors){
11278         if(errors instanceof Array){
11279             for(var i = 0, len = errors.length; i < len; i++){
11280                 var fieldError = errors[i];
11281                 var f = this.findField(fieldError.id);
11282                 if(f){
11283                     f.markInvalid(fieldError.msg);
11284                 }
11285             }
11286         }else{
11287             var field, id;
11288             for(id in errors){
11289                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11290                     field.markInvalid(errors[id]);
11291                 }
11292             }
11293         }
11294         //Roo.each(this.childForms || [], function (f) {
11295         //    f.markInvalid(errors);
11296         //});
11297
11298         return this;
11299     },
11300
11301     /**
11302      * Set values for fields in this form in bulk.
11303      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11304      * @return {BasicForm} this
11305      */
11306     setValues : function(values){
11307         if(values instanceof Array){ // array of objects
11308             for(var i = 0, len = values.length; i < len; i++){
11309                 var v = values[i];
11310                 var f = this.findField(v.id);
11311                 if(f){
11312                     f.setValue(v.value);
11313                     if(this.trackResetOnLoad){
11314                         f.originalValue = f.getValue();
11315                     }
11316                 }
11317             }
11318         }else{ // object hash
11319             var field, id;
11320             for(id in values){
11321                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11322
11323                     if (field.setFromData &&
11324                         field.valueField &&
11325                         field.displayField &&
11326                         // combos' with local stores can
11327                         // be queried via setValue()
11328                         // to set their value..
11329                         (field.store && !field.store.isLocal)
11330                         ) {
11331                         // it's a combo
11332                         var sd = { };
11333                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11334                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11335                         field.setFromData(sd);
11336
11337                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11338                         
11339                         field.setFromData(values);
11340                         
11341                     } else {
11342                         field.setValue(values[id]);
11343                     }
11344
11345
11346                     if(this.trackResetOnLoad){
11347                         field.originalValue = field.getValue();
11348                     }
11349                 }
11350             }
11351         }
11352
11353         //Roo.each(this.childForms || [], function (f) {
11354         //    f.setValues(values);
11355         //});
11356
11357         return this;
11358     },
11359
11360     /**
11361      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11362      * they are returned as an array.
11363      * @param {Boolean} asString
11364      * @return {Object}
11365      */
11366     getValues : function(asString){
11367         //if (this.childForms) {
11368             // copy values from the child forms
11369         //    Roo.each(this.childForms, function (f) {
11370         //        this.setValues(f.getValues());
11371         //    }, this);
11372         //}
11373
11374
11375
11376         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11377         if(asString === true){
11378             return fs;
11379         }
11380         return Roo.urlDecode(fs);
11381     },
11382
11383     /**
11384      * Returns the fields in this form as an object with key/value pairs.
11385      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11386      * @return {Object}
11387      */
11388     getFieldValues : function(with_hidden)
11389     {
11390         var items = this.getItems();
11391         var ret = {};
11392         items.each(function(f){
11393             
11394             if (!f.getName()) {
11395                 return;
11396             }
11397             
11398             var v = f.getValue();
11399             
11400             if (f.inputType =='radio') {
11401                 if (typeof(ret[f.getName()]) == 'undefined') {
11402                     ret[f.getName()] = ''; // empty..
11403                 }
11404
11405                 if (!f.el.dom.checked) {
11406                     return;
11407
11408                 }
11409                 v = f.el.dom.value;
11410
11411             }
11412             
11413             if(f.xtype == 'MoneyField'){
11414                 ret[f.currencyName] = f.getCurrency();
11415             }
11416
11417             // not sure if this supported any more..
11418             if ((typeof(v) == 'object') && f.getRawValue) {
11419                 v = f.getRawValue() ; // dates..
11420             }
11421             // combo boxes where name != hiddenName...
11422             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11423                 ret[f.name] = f.getRawValue();
11424             }
11425             ret[f.getName()] = v;
11426         });
11427
11428         return ret;
11429     },
11430
11431     /**
11432      * Clears all invalid messages in this form.
11433      * @return {BasicForm} this
11434      */
11435     clearInvalid : function(){
11436         var items = this.getItems();
11437
11438         items.each(function(f){
11439            f.clearInvalid();
11440         });
11441
11442         return this;
11443     },
11444
11445     /**
11446      * Resets this form.
11447      * @return {BasicForm} this
11448      */
11449     reset : function(){
11450         var items = this.getItems();
11451         items.each(function(f){
11452             f.reset();
11453         });
11454
11455         Roo.each(this.childForms || [], function (f) {
11456             f.reset();
11457         });
11458
11459
11460         return this;
11461     },
11462     
11463     getItems : function()
11464     {
11465         var r=new Roo.util.MixedCollection(false, function(o){
11466             return o.id || (o.id = Roo.id());
11467         });
11468         var iter = function(el) {
11469             if (el.inputEl) {
11470                 r.add(el);
11471             }
11472             if (!el.items) {
11473                 return;
11474             }
11475             Roo.each(el.items,function(e) {
11476                 iter(e);
11477             });
11478         };
11479
11480         iter(this);
11481         return r;
11482     },
11483     
11484     hideFields : function(items)
11485     {
11486         Roo.each(items, function(i){
11487             
11488             var f = this.findField(i);
11489             
11490             if(!f){
11491                 return;
11492             }
11493             
11494             f.hide();
11495             
11496         }, this);
11497     },
11498     
11499     showFields : function(items)
11500     {
11501         Roo.each(items, function(i){
11502             
11503             var f = this.findField(i);
11504             
11505             if(!f){
11506                 return;
11507             }
11508             
11509             f.show();
11510             
11511         }, this);
11512     }
11513
11514 });
11515
11516 Roo.apply(Roo.bootstrap.Form, {
11517     
11518     popover : {
11519         
11520         padding : 5,
11521         
11522         isApplied : false,
11523         
11524         isMasked : false,
11525         
11526         form : false,
11527         
11528         target : false,
11529         
11530         toolTip : false,
11531         
11532         intervalID : false,
11533         
11534         maskEl : false,
11535         
11536         apply : function()
11537         {
11538             if(this.isApplied){
11539                 return;
11540             }
11541             
11542             this.maskEl = {
11543                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11544                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11545                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11546                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11547             };
11548             
11549             this.maskEl.top.enableDisplayMode("block");
11550             this.maskEl.left.enableDisplayMode("block");
11551             this.maskEl.bottom.enableDisplayMode("block");
11552             this.maskEl.right.enableDisplayMode("block");
11553             
11554             this.toolTip = new Roo.bootstrap.Tooltip({
11555                 cls : 'roo-form-error-popover',
11556                 alignment : {
11557                     'left' : ['r-l', [-2,0], 'right'],
11558                     'right' : ['l-r', [2,0], 'left'],
11559                     'bottom' : ['tl-bl', [0,2], 'top'],
11560                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11561                 }
11562             });
11563             
11564             this.toolTip.render(Roo.get(document.body));
11565
11566             this.toolTip.el.enableDisplayMode("block");
11567             
11568             Roo.get(document.body).on('click', function(){
11569                 this.unmask();
11570             }, this);
11571             
11572             Roo.get(document.body).on('touchstart', function(){
11573                 this.unmask();
11574             }, this);
11575             
11576             this.isApplied = true
11577         },
11578         
11579         mask : function(form, target)
11580         {
11581             this.form = form;
11582             
11583             this.target = target;
11584             
11585             if(!this.form.errorMask || !target.el){
11586                 return;
11587             }
11588             
11589             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11590             
11591             Roo.log(scrollable);
11592             
11593             var ot = this.target.el.calcOffsetsTo(scrollable);
11594             
11595             var scrollTo = ot[1] - this.form.maskOffset;
11596             
11597             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11598             
11599             scrollable.scrollTo('top', scrollTo);
11600             
11601             var box = this.target.el.getBox();
11602             Roo.log(box);
11603             var zIndex = Roo.bootstrap.Modal.zIndex++;
11604
11605             
11606             this.maskEl.top.setStyle('position', 'absolute');
11607             this.maskEl.top.setStyle('z-index', zIndex);
11608             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11609             this.maskEl.top.setLeft(0);
11610             this.maskEl.top.setTop(0);
11611             this.maskEl.top.show();
11612             
11613             this.maskEl.left.setStyle('position', 'absolute');
11614             this.maskEl.left.setStyle('z-index', zIndex);
11615             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11616             this.maskEl.left.setLeft(0);
11617             this.maskEl.left.setTop(box.y - this.padding);
11618             this.maskEl.left.show();
11619
11620             this.maskEl.bottom.setStyle('position', 'absolute');
11621             this.maskEl.bottom.setStyle('z-index', zIndex);
11622             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11623             this.maskEl.bottom.setLeft(0);
11624             this.maskEl.bottom.setTop(box.bottom + this.padding);
11625             this.maskEl.bottom.show();
11626
11627             this.maskEl.right.setStyle('position', 'absolute');
11628             this.maskEl.right.setStyle('z-index', zIndex);
11629             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11630             this.maskEl.right.setLeft(box.right + this.padding);
11631             this.maskEl.right.setTop(box.y - this.padding);
11632             this.maskEl.right.show();
11633
11634             this.toolTip.bindEl = this.target.el;
11635
11636             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11637
11638             var tip = this.target.blankText;
11639
11640             if(this.target.getValue() !== '' ) {
11641                 
11642                 if (this.target.invalidText.length) {
11643                     tip = this.target.invalidText;
11644                 } else if (this.target.regexText.length){
11645                     tip = this.target.regexText;
11646                 }
11647             }
11648
11649             this.toolTip.show(tip);
11650
11651             this.intervalID = window.setInterval(function() {
11652                 Roo.bootstrap.Form.popover.unmask();
11653             }, 10000);
11654
11655             window.onwheel = function(){ return false;};
11656             
11657             (function(){ this.isMasked = true; }).defer(500, this);
11658             
11659         },
11660         
11661         unmask : function()
11662         {
11663             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11664                 return;
11665             }
11666             
11667             this.maskEl.top.setStyle('position', 'absolute');
11668             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11669             this.maskEl.top.hide();
11670
11671             this.maskEl.left.setStyle('position', 'absolute');
11672             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11673             this.maskEl.left.hide();
11674
11675             this.maskEl.bottom.setStyle('position', 'absolute');
11676             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11677             this.maskEl.bottom.hide();
11678
11679             this.maskEl.right.setStyle('position', 'absolute');
11680             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11681             this.maskEl.right.hide();
11682             
11683             this.toolTip.hide();
11684             
11685             this.toolTip.el.hide();
11686             
11687             window.onwheel = function(){ return true;};
11688             
11689             if(this.intervalID){
11690                 window.clearInterval(this.intervalID);
11691                 this.intervalID = false;
11692             }
11693             
11694             this.isMasked = false;
11695             
11696         }
11697         
11698     }
11699     
11700 });
11701
11702 /*
11703  * Based on:
11704  * Ext JS Library 1.1.1
11705  * Copyright(c) 2006-2007, Ext JS, LLC.
11706  *
11707  * Originally Released Under LGPL - original licence link has changed is not relivant.
11708  *
11709  * Fork - LGPL
11710  * <script type="text/javascript">
11711  */
11712 /**
11713  * @class Roo.form.VTypes
11714  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11715  * @singleton
11716  */
11717 Roo.form.VTypes = function(){
11718     // closure these in so they are only created once.
11719     var alpha = /^[a-zA-Z_]+$/;
11720     var alphanum = /^[a-zA-Z0-9_]+$/;
11721     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11722     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11723
11724     // All these messages and functions are configurable
11725     return {
11726         /**
11727          * The function used to validate email addresses
11728          * @param {String} value The email address
11729          */
11730         'email' : function(v){
11731             return email.test(v);
11732         },
11733         /**
11734          * The error text to display when the email validation function returns false
11735          * @type String
11736          */
11737         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11738         /**
11739          * The keystroke filter mask to be applied on email input
11740          * @type RegExp
11741          */
11742         'emailMask' : /[a-z0-9_\.\-@]/i,
11743
11744         /**
11745          * The function used to validate URLs
11746          * @param {String} value The URL
11747          */
11748         'url' : function(v){
11749             return url.test(v);
11750         },
11751         /**
11752          * The error text to display when the url validation function returns false
11753          * @type String
11754          */
11755         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11756         
11757         /**
11758          * The function used to validate alpha values
11759          * @param {String} value The value
11760          */
11761         'alpha' : function(v){
11762             return alpha.test(v);
11763         },
11764         /**
11765          * The error text to display when the alpha validation function returns false
11766          * @type String
11767          */
11768         'alphaText' : 'This field should only contain letters and _',
11769         /**
11770          * The keystroke filter mask to be applied on alpha input
11771          * @type RegExp
11772          */
11773         'alphaMask' : /[a-z_]/i,
11774
11775         /**
11776          * The function used to validate alphanumeric values
11777          * @param {String} value The value
11778          */
11779         'alphanum' : function(v){
11780             return alphanum.test(v);
11781         },
11782         /**
11783          * The error text to display when the alphanumeric validation function returns false
11784          * @type String
11785          */
11786         'alphanumText' : 'This field should only contain letters, numbers and _',
11787         /**
11788          * The keystroke filter mask to be applied on alphanumeric input
11789          * @type RegExp
11790          */
11791         'alphanumMask' : /[a-z0-9_]/i
11792     };
11793 }();/*
11794  * - LGPL
11795  *
11796  * Input
11797  * 
11798  */
11799
11800 /**
11801  * @class Roo.bootstrap.Input
11802  * @extends Roo.bootstrap.Component
11803  * Bootstrap Input class
11804  * @cfg {Boolean} disabled is it disabled
11805  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11806  * @cfg {String} name name of the input
11807  * @cfg {string} fieldLabel - the label associated
11808  * @cfg {string} placeholder - placeholder to put in text.
11809  * @cfg {string}  before - input group add on before
11810  * @cfg {string} after - input group add on after
11811  * @cfg {string} size - (lg|sm) or leave empty..
11812  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11813  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11814  * @cfg {Number} md colspan out of 12 for computer-sized screens
11815  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11816  * @cfg {string} value default value of the input
11817  * @cfg {Number} labelWidth set the width of label 
11818  * @cfg {Number} labellg set the width of label (1-12)
11819  * @cfg {Number} labelmd set the width of label (1-12)
11820  * @cfg {Number} labelsm set the width of label (1-12)
11821  * @cfg {Number} labelxs set the width of label (1-12)
11822  * @cfg {String} labelAlign (top|left)
11823  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11824  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11825  * @cfg {String} indicatorpos (left|right) default left
11826  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11827  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11828  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11829
11830  * @cfg {String} align (left|center|right) Default left
11831  * @cfg {Boolean} forceFeedback (true|false) Default false
11832  * 
11833  * @constructor
11834  * Create a new Input
11835  * @param {Object} config The config object
11836  */
11837
11838 Roo.bootstrap.Input = function(config){
11839     
11840     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11841     
11842     this.addEvents({
11843         /**
11844          * @event focus
11845          * Fires when this field receives input focus.
11846          * @param {Roo.form.Field} this
11847          */
11848         focus : true,
11849         /**
11850          * @event blur
11851          * Fires when this field loses input focus.
11852          * @param {Roo.form.Field} this
11853          */
11854         blur : true,
11855         /**
11856          * @event specialkey
11857          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11858          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11859          * @param {Roo.form.Field} this
11860          * @param {Roo.EventObject} e The event object
11861          */
11862         specialkey : true,
11863         /**
11864          * @event change
11865          * Fires just before the field blurs if the field value has changed.
11866          * @param {Roo.form.Field} this
11867          * @param {Mixed} newValue The new value
11868          * @param {Mixed} oldValue The original value
11869          */
11870         change : true,
11871         /**
11872          * @event invalid
11873          * Fires after the field has been marked as invalid.
11874          * @param {Roo.form.Field} this
11875          * @param {String} msg The validation message
11876          */
11877         invalid : true,
11878         /**
11879          * @event valid
11880          * Fires after the field has been validated with no errors.
11881          * @param {Roo.form.Field} this
11882          */
11883         valid : true,
11884          /**
11885          * @event keyup
11886          * Fires after the key up
11887          * @param {Roo.form.Field} this
11888          * @param {Roo.EventObject}  e The event Object
11889          */
11890         keyup : true,
11891         /**
11892          * @event paste
11893          * Fires after the user pastes into input
11894          * @param {Roo.form.Field} this
11895          * @param {Roo.EventObject}  e The event Object
11896          */
11897         paste : true
11898     });
11899 };
11900
11901 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11902      /**
11903      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11904       automatic validation (defaults to "keyup").
11905      */
11906     validationEvent : "keyup",
11907      /**
11908      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11909      */
11910     validateOnBlur : true,
11911     /**
11912      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11913      */
11914     validationDelay : 250,
11915      /**
11916      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11917      */
11918     focusClass : "x-form-focus",  // not needed???
11919     
11920        
11921     /**
11922      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11923      */
11924     invalidClass : "has-warning",
11925     
11926     /**
11927      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11928      */
11929     validClass : "has-success",
11930     
11931     /**
11932      * @cfg {Boolean} hasFeedback (true|false) default true
11933      */
11934     hasFeedback : true,
11935     
11936     /**
11937      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11938      */
11939     invalidFeedbackClass : "glyphicon-warning-sign",
11940     
11941     /**
11942      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11943      */
11944     validFeedbackClass : "glyphicon-ok",
11945     
11946     /**
11947      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11948      */
11949     selectOnFocus : false,
11950     
11951      /**
11952      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11953      */
11954     maskRe : null,
11955        /**
11956      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11957      */
11958     vtype : null,
11959     
11960       /**
11961      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11962      */
11963     disableKeyFilter : false,
11964     
11965        /**
11966      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11967      */
11968     disabled : false,
11969      /**
11970      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11971      */
11972     allowBlank : true,
11973     /**
11974      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11975      */
11976     blankText : "Please complete this mandatory field",
11977     
11978      /**
11979      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11980      */
11981     minLength : 0,
11982     /**
11983      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11984      */
11985     maxLength : Number.MAX_VALUE,
11986     /**
11987      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11988      */
11989     minLengthText : "The minimum length for this field is {0}",
11990     /**
11991      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11992      */
11993     maxLengthText : "The maximum length for this field is {0}",
11994   
11995     
11996     /**
11997      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11998      * If available, this function will be called only after the basic validators all return true, and will be passed the
11999      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12000      */
12001     validator : null,
12002     /**
12003      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12004      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12005      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12006      */
12007     regex : null,
12008     /**
12009      * @cfg {String} regexText -- Depricated - use Invalid Text
12010      */
12011     regexText : "",
12012     
12013     /**
12014      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12015      */
12016     invalidText : "",
12017     
12018     
12019     
12020     autocomplete: false,
12021     
12022     
12023     fieldLabel : '',
12024     inputType : 'text',
12025     
12026     name : false,
12027     placeholder: false,
12028     before : false,
12029     after : false,
12030     size : false,
12031     hasFocus : false,
12032     preventMark: false,
12033     isFormField : true,
12034     value : '',
12035     labelWidth : 2,
12036     labelAlign : false,
12037     readOnly : false,
12038     align : false,
12039     formatedValue : false,
12040     forceFeedback : false,
12041     
12042     indicatorpos : 'left',
12043     
12044     labellg : 0,
12045     labelmd : 0,
12046     labelsm : 0,
12047     labelxs : 0,
12048     
12049     capture : '',
12050     accept : '',
12051     
12052     parentLabelAlign : function()
12053     {
12054         var parent = this;
12055         while (parent.parent()) {
12056             parent = parent.parent();
12057             if (typeof(parent.labelAlign) !='undefined') {
12058                 return parent.labelAlign;
12059             }
12060         }
12061         return 'left';
12062         
12063     },
12064     
12065     getAutoCreate : function()
12066     {
12067         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12068         
12069         var id = Roo.id();
12070         
12071         var cfg = {};
12072         
12073         if(this.inputType != 'hidden'){
12074             cfg.cls = 'form-group' //input-group
12075         }
12076         
12077         var input =  {
12078             tag: 'input',
12079             id : id,
12080             type : this.inputType,
12081             value : this.value,
12082             cls : 'form-control',
12083             placeholder : this.placeholder || '',
12084             autocomplete : this.autocomplete || 'new-password'
12085         };
12086         if (this.inputType == 'file') {
12087             input.style = 'overflow:hidden'; // why not in CSS?
12088         }
12089         
12090         if(this.capture.length){
12091             input.capture = this.capture;
12092         }
12093         
12094         if(this.accept.length){
12095             input.accept = this.accept + "/*";
12096         }
12097         
12098         if(this.align){
12099             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12100         }
12101         
12102         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12103             input.maxLength = this.maxLength;
12104         }
12105         
12106         if (this.disabled) {
12107             input.disabled=true;
12108         }
12109         
12110         if (this.readOnly) {
12111             input.readonly=true;
12112         }
12113         
12114         if (this.name) {
12115             input.name = this.name;
12116         }
12117         
12118         if (this.size) {
12119             input.cls += ' input-' + this.size;
12120         }
12121         
12122         var settings=this;
12123         ['xs','sm','md','lg'].map(function(size){
12124             if (settings[size]) {
12125                 cfg.cls += ' col-' + size + '-' + settings[size];
12126             }
12127         });
12128         
12129         var inputblock = input;
12130         
12131         var feedback = {
12132             tag: 'span',
12133             cls: 'glyphicon form-control-feedback'
12134         };
12135             
12136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12137             
12138             inputblock = {
12139                 cls : 'has-feedback',
12140                 cn :  [
12141                     input,
12142                     feedback
12143                 ] 
12144             };  
12145         }
12146         
12147         if (this.before || this.after) {
12148             
12149             inputblock = {
12150                 cls : 'input-group',
12151                 cn :  [] 
12152             };
12153             
12154             if (this.before && typeof(this.before) == 'string') {
12155                 
12156                 inputblock.cn.push({
12157                     tag :'span',
12158                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12159                     html : this.before
12160                 });
12161             }
12162             if (this.before && typeof(this.before) == 'object') {
12163                 this.before = Roo.factory(this.before);
12164                 
12165                 inputblock.cn.push({
12166                     tag :'span',
12167                     cls : 'roo-input-before input-group-prepend   input-group-' +
12168                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12169                 });
12170             }
12171             
12172             inputblock.cn.push(input);
12173             
12174             if (this.after && typeof(this.after) == 'string') {
12175                 inputblock.cn.push({
12176                     tag :'span',
12177                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12178                     html : this.after
12179                 });
12180             }
12181             if (this.after && typeof(this.after) == 'object') {
12182                 this.after = Roo.factory(this.after);
12183                 
12184                 inputblock.cn.push({
12185                     tag :'span',
12186                     cls : 'roo-input-after input-group-append  input-group-' +
12187                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12188                 });
12189             }
12190             
12191             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12192                 inputblock.cls += ' has-feedback';
12193                 inputblock.cn.push(feedback);
12194             }
12195         };
12196         var indicator = {
12197             tag : 'i',
12198             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12199             tooltip : 'This field is required'
12200         };
12201         if (this.allowBlank ) {
12202             indicator.style = this.allowBlank ? ' display:none' : '';
12203         }
12204         if (align ==='left' && this.fieldLabel.length) {
12205             
12206             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12207             
12208             cfg.cn = [
12209                 indicator,
12210                 {
12211                     tag: 'label',
12212                     'for' :  id,
12213                     cls : 'control-label col-form-label',
12214                     html : this.fieldLabel
12215
12216                 },
12217                 {
12218                     cls : "", 
12219                     cn: [
12220                         inputblock
12221                     ]
12222                 }
12223             ];
12224             
12225             var labelCfg = cfg.cn[1];
12226             var contentCfg = cfg.cn[2];
12227             
12228             if(this.indicatorpos == 'right'){
12229                 cfg.cn = [
12230                     {
12231                         tag: 'label',
12232                         'for' :  id,
12233                         cls : 'control-label col-form-label',
12234                         cn : [
12235                             {
12236                                 tag : 'span',
12237                                 html : this.fieldLabel
12238                             },
12239                             indicator
12240                         ]
12241                     },
12242                     {
12243                         cls : "",
12244                         cn: [
12245                             inputblock
12246                         ]
12247                     }
12248
12249                 ];
12250                 
12251                 labelCfg = cfg.cn[0];
12252                 contentCfg = cfg.cn[1];
12253             
12254             }
12255             
12256             if(this.labelWidth > 12){
12257                 labelCfg.style = "width: " + this.labelWidth + 'px';
12258             }
12259             
12260             if(this.labelWidth < 13 && this.labelmd == 0){
12261                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12262             }
12263             
12264             if(this.labellg > 0){
12265                 labelCfg.cls += ' col-lg-' + this.labellg;
12266                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12267             }
12268             
12269             if(this.labelmd > 0){
12270                 labelCfg.cls += ' col-md-' + this.labelmd;
12271                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12272             }
12273             
12274             if(this.labelsm > 0){
12275                 labelCfg.cls += ' col-sm-' + this.labelsm;
12276                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12277             }
12278             
12279             if(this.labelxs > 0){
12280                 labelCfg.cls += ' col-xs-' + this.labelxs;
12281                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12282             }
12283             
12284             
12285         } else if ( this.fieldLabel.length) {
12286                 
12287             
12288             
12289             cfg.cn = [
12290                 {
12291                     tag : 'i',
12292                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12293                     tooltip : 'This field is required',
12294                     style : this.allowBlank ? ' display:none' : '' 
12295                 },
12296                 {
12297                     tag: 'label',
12298                    //cls : 'input-group-addon',
12299                     html : this.fieldLabel
12300
12301                 },
12302
12303                inputblock
12304
12305            ];
12306            
12307            if(this.indicatorpos == 'right'){
12308        
12309                 cfg.cn = [
12310                     {
12311                         tag: 'label',
12312                        //cls : 'input-group-addon',
12313                         html : this.fieldLabel
12314
12315                     },
12316                     {
12317                         tag : 'i',
12318                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12319                         tooltip : 'This field is required',
12320                         style : this.allowBlank ? ' display:none' : '' 
12321                     },
12322
12323                    inputblock
12324
12325                ];
12326
12327             }
12328
12329         } else {
12330             
12331             cfg.cn = [
12332
12333                     inputblock
12334
12335             ];
12336                 
12337                 
12338         };
12339         
12340         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12341            cfg.cls += ' navbar-form';
12342         }
12343         
12344         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12345             // on BS4 we do this only if not form 
12346             cfg.cls += ' navbar-form';
12347             cfg.tag = 'li';
12348         }
12349         
12350         return cfg;
12351         
12352     },
12353     /**
12354      * return the real input element.
12355      */
12356     inputEl: function ()
12357     {
12358         return this.el.select('input.form-control',true).first();
12359     },
12360     
12361     tooltipEl : function()
12362     {
12363         return this.inputEl();
12364     },
12365     
12366     indicatorEl : function()
12367     {
12368         if (Roo.bootstrap.version == 4) {
12369             return false; // not enabled in v4 yet.
12370         }
12371         
12372         var indicator = this.el.select('i.roo-required-indicator',true).first();
12373         
12374         if(!indicator){
12375             return false;
12376         }
12377         
12378         return indicator;
12379         
12380     },
12381     
12382     setDisabled : function(v)
12383     {
12384         var i  = this.inputEl().dom;
12385         if (!v) {
12386             i.removeAttribute('disabled');
12387             return;
12388             
12389         }
12390         i.setAttribute('disabled','true');
12391     },
12392     initEvents : function()
12393     {
12394           
12395         this.inputEl().on("keydown" , this.fireKey,  this);
12396         this.inputEl().on("focus", this.onFocus,  this);
12397         this.inputEl().on("blur", this.onBlur,  this);
12398         
12399         this.inputEl().relayEvent('keyup', this);
12400         this.inputEl().relayEvent('paste', this);
12401         
12402         this.indicator = this.indicatorEl();
12403         
12404         if(this.indicator){
12405             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12406         }
12407  
12408         // reference to original value for reset
12409         this.originalValue = this.getValue();
12410         //Roo.form.TextField.superclass.initEvents.call(this);
12411         if(this.validationEvent == 'keyup'){
12412             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12413             this.inputEl().on('keyup', this.filterValidation, this);
12414         }
12415         else if(this.validationEvent !== false){
12416             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12417         }
12418         
12419         if(this.selectOnFocus){
12420             this.on("focus", this.preFocus, this);
12421             
12422         }
12423         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12424             this.inputEl().on("keypress", this.filterKeys, this);
12425         } else {
12426             this.inputEl().relayEvent('keypress', this);
12427         }
12428        /* if(this.grow){
12429             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12430             this.el.on("click", this.autoSize,  this);
12431         }
12432         */
12433         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12434             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12435         }
12436         
12437         if (typeof(this.before) == 'object') {
12438             this.before.render(this.el.select('.roo-input-before',true).first());
12439         }
12440         if (typeof(this.after) == 'object') {
12441             this.after.render(this.el.select('.roo-input-after',true).first());
12442         }
12443         
12444         this.inputEl().on('change', this.onChange, this);
12445         
12446     },
12447     filterValidation : function(e){
12448         if(!e.isNavKeyPress()){
12449             this.validationTask.delay(this.validationDelay);
12450         }
12451     },
12452      /**
12453      * Validates the field value
12454      * @return {Boolean} True if the value is valid, else false
12455      */
12456     validate : function(){
12457         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12458         if(this.disabled || this.validateValue(this.getRawValue())){
12459             this.markValid();
12460             return true;
12461         }
12462         
12463         this.markInvalid();
12464         return false;
12465     },
12466     
12467     
12468     /**
12469      * Validates a value according to the field's validation rules and marks the field as invalid
12470      * if the validation fails
12471      * @param {Mixed} value The value to validate
12472      * @return {Boolean} True if the value is valid, else false
12473      */
12474     validateValue : function(value)
12475     {
12476         if(this.getVisibilityEl().hasClass('hidden')){
12477             return true;
12478         }
12479         
12480         if(value.length < 1)  { // if it's blank
12481             if(this.allowBlank){
12482                 return true;
12483             }
12484             return false;
12485         }
12486         
12487         if(value.length < this.minLength){
12488             return false;
12489         }
12490         if(value.length > this.maxLength){
12491             return false;
12492         }
12493         if(this.vtype){
12494             var vt = Roo.form.VTypes;
12495             if(!vt[this.vtype](value, this)){
12496                 return false;
12497             }
12498         }
12499         if(typeof this.validator == "function"){
12500             var msg = this.validator(value);
12501             if(msg !== true){
12502                 return false;
12503             }
12504             if (typeof(msg) == 'string') {
12505                 this.invalidText = msg;
12506             }
12507         }
12508         
12509         if(this.regex && !this.regex.test(value)){
12510             return false;
12511         }
12512         
12513         return true;
12514     },
12515     
12516      // private
12517     fireKey : function(e){
12518         //Roo.log('field ' + e.getKey());
12519         if(e.isNavKeyPress()){
12520             this.fireEvent("specialkey", this, e);
12521         }
12522     },
12523     focus : function (selectText){
12524         if(this.rendered){
12525             this.inputEl().focus();
12526             if(selectText === true){
12527                 this.inputEl().dom.select();
12528             }
12529         }
12530         return this;
12531     } ,
12532     
12533     onFocus : function(){
12534         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12535            // this.el.addClass(this.focusClass);
12536         }
12537         if(!this.hasFocus){
12538             this.hasFocus = true;
12539             this.startValue = this.getValue();
12540             this.fireEvent("focus", this);
12541         }
12542     },
12543     
12544     beforeBlur : Roo.emptyFn,
12545
12546     
12547     // private
12548     onBlur : function(){
12549         this.beforeBlur();
12550         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12551             //this.el.removeClass(this.focusClass);
12552         }
12553         this.hasFocus = false;
12554         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12555             this.validate();
12556         }
12557         var v = this.getValue();
12558         if(String(v) !== String(this.startValue)){
12559             this.fireEvent('change', this, v, this.startValue);
12560         }
12561         this.fireEvent("blur", this);
12562     },
12563     
12564     onChange : function(e)
12565     {
12566         var v = this.getValue();
12567         if(String(v) !== String(this.startValue)){
12568             this.fireEvent('change', this, v, this.startValue);
12569         }
12570         
12571     },
12572     
12573     /**
12574      * Resets the current field value to the originally loaded value and clears any validation messages
12575      */
12576     reset : function(){
12577         this.setValue(this.originalValue);
12578         this.validate();
12579     },
12580      /**
12581      * Returns the name of the field
12582      * @return {Mixed} name The name field
12583      */
12584     getName: function(){
12585         return this.name;
12586     },
12587      /**
12588      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12589      * @return {Mixed} value The field value
12590      */
12591     getValue : function(){
12592         
12593         var v = this.inputEl().getValue();
12594         
12595         return v;
12596     },
12597     /**
12598      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12599      * @return {Mixed} value The field value
12600      */
12601     getRawValue : function(){
12602         var v = this.inputEl().getValue();
12603         
12604         return v;
12605     },
12606     
12607     /**
12608      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12609      * @param {Mixed} value The value to set
12610      */
12611     setRawValue : function(v){
12612         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12613     },
12614     
12615     selectText : function(start, end){
12616         var v = this.getRawValue();
12617         if(v.length > 0){
12618             start = start === undefined ? 0 : start;
12619             end = end === undefined ? v.length : end;
12620             var d = this.inputEl().dom;
12621             if(d.setSelectionRange){
12622                 d.setSelectionRange(start, end);
12623             }else if(d.createTextRange){
12624                 var range = d.createTextRange();
12625                 range.moveStart("character", start);
12626                 range.moveEnd("character", v.length-end);
12627                 range.select();
12628             }
12629         }
12630     },
12631     
12632     /**
12633      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12634      * @param {Mixed} value The value to set
12635      */
12636     setValue : function(v){
12637         this.value = v;
12638         if(this.rendered){
12639             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12640             this.validate();
12641         }
12642     },
12643     
12644     /*
12645     processValue : function(value){
12646         if(this.stripCharsRe){
12647             var newValue = value.replace(this.stripCharsRe, '');
12648             if(newValue !== value){
12649                 this.setRawValue(newValue);
12650                 return newValue;
12651             }
12652         }
12653         return value;
12654     },
12655   */
12656     preFocus : function(){
12657         
12658         if(this.selectOnFocus){
12659             this.inputEl().dom.select();
12660         }
12661     },
12662     filterKeys : function(e){
12663         var k = e.getKey();
12664         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12665             return;
12666         }
12667         var c = e.getCharCode(), cc = String.fromCharCode(c);
12668         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12669             return;
12670         }
12671         if(!this.maskRe.test(cc)){
12672             e.stopEvent();
12673         }
12674     },
12675      /**
12676      * Clear any invalid styles/messages for this field
12677      */
12678     clearInvalid : function(){
12679         
12680         if(!this.el || this.preventMark){ // not rendered
12681             return;
12682         }
12683         
12684         
12685         this.el.removeClass([this.invalidClass, 'is-invalid']);
12686         
12687         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12688             
12689             var feedback = this.el.select('.form-control-feedback', true).first();
12690             
12691             if(feedback){
12692                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12693             }
12694             
12695         }
12696         
12697         if(this.indicator){
12698             this.indicator.removeClass('visible');
12699             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12700         }
12701         
12702         this.fireEvent('valid', this);
12703     },
12704     
12705      /**
12706      * Mark this field as valid
12707      */
12708     markValid : function()
12709     {
12710         if(!this.el  || this.preventMark){ // not rendered...
12711             return;
12712         }
12713         
12714         this.el.removeClass([this.invalidClass, this.validClass]);
12715         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12716
12717         var feedback = this.el.select('.form-control-feedback', true).first();
12718             
12719         if(feedback){
12720             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12721         }
12722         
12723         if(this.indicator){
12724             this.indicator.removeClass('visible');
12725             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12726         }
12727         
12728         if(this.disabled){
12729             return;
12730         }
12731         
12732            
12733         if(this.allowBlank && !this.getRawValue().length){
12734             return;
12735         }
12736         if (Roo.bootstrap.version == 3) {
12737             this.el.addClass(this.validClass);
12738         } else {
12739             this.inputEl().addClass('is-valid');
12740         }
12741
12742         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12743             
12744             var feedback = this.el.select('.form-control-feedback', true).first();
12745             
12746             if(feedback){
12747                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12748                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12749             }
12750             
12751         }
12752         
12753         this.fireEvent('valid', this);
12754     },
12755     
12756      /**
12757      * Mark this field as invalid
12758      * @param {String} msg The validation message
12759      */
12760     markInvalid : function(msg)
12761     {
12762         if(!this.el  || this.preventMark){ // not rendered
12763             return;
12764         }
12765         
12766         this.el.removeClass([this.invalidClass, this.validClass]);
12767         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12768         
12769         var feedback = this.el.select('.form-control-feedback', true).first();
12770             
12771         if(feedback){
12772             this.el.select('.form-control-feedback', true).first().removeClass(
12773                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12774         }
12775
12776         if(this.disabled){
12777             return;
12778         }
12779         
12780         if(this.allowBlank && !this.getRawValue().length){
12781             return;
12782         }
12783         
12784         if(this.indicator){
12785             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12786             this.indicator.addClass('visible');
12787         }
12788         if (Roo.bootstrap.version == 3) {
12789             this.el.addClass(this.invalidClass);
12790         } else {
12791             this.inputEl().addClass('is-invalid');
12792         }
12793         
12794         
12795         
12796         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12797             
12798             var feedback = this.el.select('.form-control-feedback', true).first();
12799             
12800             if(feedback){
12801                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12802                 
12803                 if(this.getValue().length || this.forceFeedback){
12804                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12805                 }
12806                 
12807             }
12808             
12809         }
12810         
12811         this.fireEvent('invalid', this, msg);
12812     },
12813     // private
12814     SafariOnKeyDown : function(event)
12815     {
12816         // this is a workaround for a password hang bug on chrome/ webkit.
12817         if (this.inputEl().dom.type != 'password') {
12818             return;
12819         }
12820         
12821         var isSelectAll = false;
12822         
12823         if(this.inputEl().dom.selectionEnd > 0){
12824             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12825         }
12826         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12827             event.preventDefault();
12828             this.setValue('');
12829             return;
12830         }
12831         
12832         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12833             
12834             event.preventDefault();
12835             // this is very hacky as keydown always get's upper case.
12836             //
12837             var cc = String.fromCharCode(event.getCharCode());
12838             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12839             
12840         }
12841     },
12842     adjustWidth : function(tag, w){
12843         tag = tag.toLowerCase();
12844         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12845             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12846                 if(tag == 'input'){
12847                     return w + 2;
12848                 }
12849                 if(tag == 'textarea'){
12850                     return w-2;
12851                 }
12852             }else if(Roo.isOpera){
12853                 if(tag == 'input'){
12854                     return w + 2;
12855                 }
12856                 if(tag == 'textarea'){
12857                     return w-2;
12858                 }
12859             }
12860         }
12861         return w;
12862     },
12863     
12864     setFieldLabel : function(v)
12865     {
12866         if(!this.rendered){
12867             return;
12868         }
12869         
12870         if(this.indicatorEl()){
12871             var ar = this.el.select('label > span',true);
12872             
12873             if (ar.elements.length) {
12874                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12875                 this.fieldLabel = v;
12876                 return;
12877             }
12878             
12879             var br = this.el.select('label',true);
12880             
12881             if(br.elements.length) {
12882                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12883                 this.fieldLabel = v;
12884                 return;
12885             }
12886             
12887             Roo.log('Cannot Found any of label > span || label in input');
12888             return;
12889         }
12890         
12891         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12892         this.fieldLabel = v;
12893         
12894         
12895     }
12896 });
12897
12898  
12899 /*
12900  * - LGPL
12901  *
12902  * Input
12903  * 
12904  */
12905
12906 /**
12907  * @class Roo.bootstrap.TextArea
12908  * @extends Roo.bootstrap.Input
12909  * Bootstrap TextArea class
12910  * @cfg {Number} cols Specifies the visible width of a text area
12911  * @cfg {Number} rows Specifies the visible number of lines in a text area
12912  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12913  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12914  * @cfg {string} html text
12915  * 
12916  * @constructor
12917  * Create a new TextArea
12918  * @param {Object} config The config object
12919  */
12920
12921 Roo.bootstrap.TextArea = function(config){
12922     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12923    
12924 };
12925
12926 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12927      
12928     cols : false,
12929     rows : 5,
12930     readOnly : false,
12931     warp : 'soft',
12932     resize : false,
12933     value: false,
12934     html: false,
12935     
12936     getAutoCreate : function(){
12937         
12938         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12939         
12940         var id = Roo.id();
12941         
12942         var cfg = {};
12943         
12944         if(this.inputType != 'hidden'){
12945             cfg.cls = 'form-group' //input-group
12946         }
12947         
12948         var input =  {
12949             tag: 'textarea',
12950             id : id,
12951             warp : this.warp,
12952             rows : this.rows,
12953             value : this.value || '',
12954             html: this.html || '',
12955             cls : 'form-control',
12956             placeholder : this.placeholder || '' 
12957             
12958         };
12959         
12960         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12961             input.maxLength = this.maxLength;
12962         }
12963         
12964         if(this.resize){
12965             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12966         }
12967         
12968         if(this.cols){
12969             input.cols = this.cols;
12970         }
12971         
12972         if (this.readOnly) {
12973             input.readonly = true;
12974         }
12975         
12976         if (this.name) {
12977             input.name = this.name;
12978         }
12979         
12980         if (this.size) {
12981             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12982         }
12983         
12984         var settings=this;
12985         ['xs','sm','md','lg'].map(function(size){
12986             if (settings[size]) {
12987                 cfg.cls += ' col-' + size + '-' + settings[size];
12988             }
12989         });
12990         
12991         var inputblock = input;
12992         
12993         if(this.hasFeedback && !this.allowBlank){
12994             
12995             var feedback = {
12996                 tag: 'span',
12997                 cls: 'glyphicon form-control-feedback'
12998             };
12999
13000             inputblock = {
13001                 cls : 'has-feedback',
13002                 cn :  [
13003                     input,
13004                     feedback
13005                 ] 
13006             };  
13007         }
13008         
13009         
13010         if (this.before || this.after) {
13011             
13012             inputblock = {
13013                 cls : 'input-group',
13014                 cn :  [] 
13015             };
13016             if (this.before) {
13017                 inputblock.cn.push({
13018                     tag :'span',
13019                     cls : 'input-group-addon',
13020                     html : this.before
13021                 });
13022             }
13023             
13024             inputblock.cn.push(input);
13025             
13026             if(this.hasFeedback && !this.allowBlank){
13027                 inputblock.cls += ' has-feedback';
13028                 inputblock.cn.push(feedback);
13029             }
13030             
13031             if (this.after) {
13032                 inputblock.cn.push({
13033                     tag :'span',
13034                     cls : 'input-group-addon',
13035                     html : this.after
13036                 });
13037             }
13038             
13039         }
13040         
13041         if (align ==='left' && this.fieldLabel.length) {
13042             cfg.cn = [
13043                 {
13044                     tag: 'label',
13045                     'for' :  id,
13046                     cls : 'control-label',
13047                     html : this.fieldLabel
13048                 },
13049                 {
13050                     cls : "",
13051                     cn: [
13052                         inputblock
13053                     ]
13054                 }
13055
13056             ];
13057             
13058             if(this.labelWidth > 12){
13059                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13060             }
13061
13062             if(this.labelWidth < 13 && this.labelmd == 0){
13063                 this.labelmd = this.labelWidth;
13064             }
13065
13066             if(this.labellg > 0){
13067                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13068                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13069             }
13070
13071             if(this.labelmd > 0){
13072                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13073                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13074             }
13075
13076             if(this.labelsm > 0){
13077                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13078                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13079             }
13080
13081             if(this.labelxs > 0){
13082                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13083                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13084             }
13085             
13086         } else if ( this.fieldLabel.length) {
13087             cfg.cn = [
13088
13089                {
13090                    tag: 'label',
13091                    //cls : 'input-group-addon',
13092                    html : this.fieldLabel
13093
13094                },
13095
13096                inputblock
13097
13098            ];
13099
13100         } else {
13101
13102             cfg.cn = [
13103
13104                 inputblock
13105
13106             ];
13107                 
13108         }
13109         
13110         if (this.disabled) {
13111             input.disabled=true;
13112         }
13113         
13114         return cfg;
13115         
13116     },
13117     /**
13118      * return the real textarea element.
13119      */
13120     inputEl: function ()
13121     {
13122         return this.el.select('textarea.form-control',true).first();
13123     },
13124     
13125     /**
13126      * Clear any invalid styles/messages for this field
13127      */
13128     clearInvalid : function()
13129     {
13130         
13131         if(!this.el || this.preventMark){ // not rendered
13132             return;
13133         }
13134         
13135         var label = this.el.select('label', true).first();
13136         var icon = this.el.select('i.fa-star', true).first();
13137         
13138         if(label && icon){
13139             icon.remove();
13140         }
13141         this.el.removeClass( this.validClass);
13142         this.inputEl().removeClass('is-invalid');
13143          
13144         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13145             
13146             var feedback = this.el.select('.form-control-feedback', true).first();
13147             
13148             if(feedback){
13149                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13150             }
13151             
13152         }
13153         
13154         this.fireEvent('valid', this);
13155     },
13156     
13157      /**
13158      * Mark this field as valid
13159      */
13160     markValid : function()
13161     {
13162         if(!this.el  || this.preventMark){ // not rendered
13163             return;
13164         }
13165         
13166         this.el.removeClass([this.invalidClass, this.validClass]);
13167         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13168         
13169         var feedback = this.el.select('.form-control-feedback', true).first();
13170             
13171         if(feedback){
13172             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13173         }
13174
13175         if(this.disabled || this.allowBlank){
13176             return;
13177         }
13178         
13179         var label = this.el.select('label', true).first();
13180         var icon = this.el.select('i.fa-star', true).first();
13181         
13182         if(label && icon){
13183             icon.remove();
13184         }
13185         if (Roo.bootstrap.version == 3) {
13186             this.el.addClass(this.validClass);
13187         } else {
13188             this.inputEl().addClass('is-valid');
13189         }
13190         
13191         
13192         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13193             
13194             var feedback = this.el.select('.form-control-feedback', true).first();
13195             
13196             if(feedback){
13197                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13198                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13199             }
13200             
13201         }
13202         
13203         this.fireEvent('valid', this);
13204     },
13205     
13206      /**
13207      * Mark this field as invalid
13208      * @param {String} msg The validation message
13209      */
13210     markInvalid : function(msg)
13211     {
13212         if(!this.el  || this.preventMark){ // not rendered
13213             return;
13214         }
13215         
13216         this.el.removeClass([this.invalidClass, this.validClass]);
13217         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13218         
13219         var feedback = this.el.select('.form-control-feedback', true).first();
13220             
13221         if(feedback){
13222             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13223         }
13224
13225         if(this.disabled || this.allowBlank){
13226             return;
13227         }
13228         
13229         var label = this.el.select('label', true).first();
13230         var icon = this.el.select('i.fa-star', true).first();
13231         
13232         if(!this.getValue().length && label && !icon){
13233             this.el.createChild({
13234                 tag : 'i',
13235                 cls : 'text-danger fa fa-lg fa-star',
13236                 tooltip : 'This field is required',
13237                 style : 'margin-right:5px;'
13238             }, label, true);
13239         }
13240         
13241         if (Roo.bootstrap.version == 3) {
13242             this.el.addClass(this.invalidClass);
13243         } else {
13244             this.inputEl().addClass('is-invalid');
13245         }
13246         
13247         // fixme ... this may be depricated need to test..
13248         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13249             
13250             var feedback = this.el.select('.form-control-feedback', true).first();
13251             
13252             if(feedback){
13253                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13254                 
13255                 if(this.getValue().length || this.forceFeedback){
13256                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13257                 }
13258                 
13259             }
13260             
13261         }
13262         
13263         this.fireEvent('invalid', this, msg);
13264     }
13265 });
13266
13267  
13268 /*
13269  * - LGPL
13270  *
13271  * trigger field - base class for combo..
13272  * 
13273  */
13274  
13275 /**
13276  * @class Roo.bootstrap.TriggerField
13277  * @extends Roo.bootstrap.Input
13278  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13279  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13280  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13281  * for which you can provide a custom implementation.  For example:
13282  * <pre><code>
13283 var trigger = new Roo.bootstrap.TriggerField();
13284 trigger.onTriggerClick = myTriggerFn;
13285 trigger.applyTo('my-field');
13286 </code></pre>
13287  *
13288  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13289  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13290  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13291  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13292  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13293
13294  * @constructor
13295  * Create a new TriggerField.
13296  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13297  * to the base TextField)
13298  */
13299 Roo.bootstrap.TriggerField = function(config){
13300     this.mimicing = false;
13301     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13302 };
13303
13304 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13305     /**
13306      * @cfg {String} triggerClass A CSS class to apply to the trigger
13307      */
13308      /**
13309      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13310      */
13311     hideTrigger:false,
13312
13313     /**
13314      * @cfg {Boolean} removable (true|false) special filter default false
13315      */
13316     removable : false,
13317     
13318     /** @cfg {Boolean} grow @hide */
13319     /** @cfg {Number} growMin @hide */
13320     /** @cfg {Number} growMax @hide */
13321
13322     /**
13323      * @hide 
13324      * @method
13325      */
13326     autoSize: Roo.emptyFn,
13327     // private
13328     monitorTab : true,
13329     // private
13330     deferHeight : true,
13331
13332     
13333     actionMode : 'wrap',
13334     
13335     caret : false,
13336     
13337     
13338     getAutoCreate : function(){
13339        
13340         var align = this.labelAlign || this.parentLabelAlign();
13341         
13342         var id = Roo.id();
13343         
13344         var cfg = {
13345             cls: 'form-group' //input-group
13346         };
13347         
13348         
13349         var input =  {
13350             tag: 'input',
13351             id : id,
13352             type : this.inputType,
13353             cls : 'form-control',
13354             autocomplete: 'new-password',
13355             placeholder : this.placeholder || '' 
13356             
13357         };
13358         if (this.name) {
13359             input.name = this.name;
13360         }
13361         if (this.size) {
13362             input.cls += ' input-' + this.size;
13363         }
13364         
13365         if (this.disabled) {
13366             input.disabled=true;
13367         }
13368         
13369         var inputblock = input;
13370         
13371         if(this.hasFeedback && !this.allowBlank){
13372             
13373             var feedback = {
13374                 tag: 'span',
13375                 cls: 'glyphicon form-control-feedback'
13376             };
13377             
13378             if(this.removable && !this.editable  ){
13379                 inputblock = {
13380                     cls : 'has-feedback',
13381                     cn :  [
13382                         inputblock,
13383                         {
13384                             tag: 'button',
13385                             html : 'x',
13386                             cls : 'roo-combo-removable-btn close'
13387                         },
13388                         feedback
13389                     ] 
13390                 };
13391             } else {
13392                 inputblock = {
13393                     cls : 'has-feedback',
13394                     cn :  [
13395                         inputblock,
13396                         feedback
13397                     ] 
13398                 };
13399             }
13400
13401         } else {
13402             if(this.removable && !this.editable ){
13403                 inputblock = {
13404                     cls : 'roo-removable',
13405                     cn :  [
13406                         inputblock,
13407                         {
13408                             tag: 'button',
13409                             html : 'x',
13410                             cls : 'roo-combo-removable-btn close'
13411                         }
13412                     ] 
13413                 };
13414             }
13415         }
13416         
13417         if (this.before || this.after) {
13418             
13419             inputblock = {
13420                 cls : 'input-group',
13421                 cn :  [] 
13422             };
13423             if (this.before) {
13424                 inputblock.cn.push({
13425                     tag :'span',
13426                     cls : 'input-group-addon input-group-prepend input-group-text',
13427                     html : this.before
13428                 });
13429             }
13430             
13431             inputblock.cn.push(input);
13432             
13433             if(this.hasFeedback && !this.allowBlank){
13434                 inputblock.cls += ' has-feedback';
13435                 inputblock.cn.push(feedback);
13436             }
13437             
13438             if (this.after) {
13439                 inputblock.cn.push({
13440                     tag :'span',
13441                     cls : 'input-group-addon input-group-append input-group-text',
13442                     html : this.after
13443                 });
13444             }
13445             
13446         };
13447         
13448       
13449         
13450         var ibwrap = inputblock;
13451         
13452         if(this.multiple){
13453             ibwrap = {
13454                 tag: 'ul',
13455                 cls: 'roo-select2-choices',
13456                 cn:[
13457                     {
13458                         tag: 'li',
13459                         cls: 'roo-select2-search-field',
13460                         cn: [
13461
13462                             inputblock
13463                         ]
13464                     }
13465                 ]
13466             };
13467                 
13468         }
13469         
13470         var combobox = {
13471             cls: 'roo-select2-container input-group',
13472             cn: [
13473                  {
13474                     tag: 'input',
13475                     type : 'hidden',
13476                     cls: 'form-hidden-field'
13477                 },
13478                 ibwrap
13479             ]
13480         };
13481         
13482         if(!this.multiple && this.showToggleBtn){
13483             
13484             var caret = {
13485                         tag: 'span',
13486                         cls: 'caret'
13487              };
13488             if (this.caret != false) {
13489                 caret = {
13490                      tag: 'i',
13491                      cls: 'fa fa-' + this.caret
13492                 };
13493                 
13494             }
13495             
13496             combobox.cn.push({
13497                 tag :'span',
13498                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13499                 cn : [
13500                     Roo.bootstrap.version == 3 ? caret : '',
13501                     {
13502                         tag: 'span',
13503                         cls: 'combobox-clear',
13504                         cn  : [
13505                             {
13506                                 tag : 'i',
13507                                 cls: 'icon-remove'
13508                             }
13509                         ]
13510                     }
13511                 ]
13512
13513             })
13514         }
13515         
13516         if(this.multiple){
13517             combobox.cls += ' roo-select2-container-multi';
13518         }
13519          var indicator = {
13520             tag : 'i',
13521             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13522             tooltip : 'This field is required'
13523         };
13524         if (Roo.bootstrap.version == 4) {
13525             indicator = {
13526                 tag : 'i',
13527                 style : 'display:none'
13528             };
13529         }
13530         
13531         
13532         if (align ==='left' && this.fieldLabel.length) {
13533             
13534             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13535
13536             cfg.cn = [
13537                 indicator,
13538                 {
13539                     tag: 'label',
13540                     'for' :  id,
13541                     cls : 'control-label',
13542                     html : this.fieldLabel
13543
13544                 },
13545                 {
13546                     cls : "", 
13547                     cn: [
13548                         combobox
13549                     ]
13550                 }
13551
13552             ];
13553             
13554             var labelCfg = cfg.cn[1];
13555             var contentCfg = cfg.cn[2];
13556             
13557             if(this.indicatorpos == 'right'){
13558                 cfg.cn = [
13559                     {
13560                         tag: 'label',
13561                         'for' :  id,
13562                         cls : 'control-label',
13563                         cn : [
13564                             {
13565                                 tag : 'span',
13566                                 html : this.fieldLabel
13567                             },
13568                             indicator
13569                         ]
13570                     },
13571                     {
13572                         cls : "", 
13573                         cn: [
13574                             combobox
13575                         ]
13576                     }
13577
13578                 ];
13579                 
13580                 labelCfg = cfg.cn[0];
13581                 contentCfg = cfg.cn[1];
13582             }
13583             
13584             if(this.labelWidth > 12){
13585                 labelCfg.style = "width: " + this.labelWidth + 'px';
13586             }
13587             
13588             if(this.labelWidth < 13 && this.labelmd == 0){
13589                 this.labelmd = this.labelWidth;
13590             }
13591             
13592             if(this.labellg > 0){
13593                 labelCfg.cls += ' col-lg-' + this.labellg;
13594                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13595             }
13596             
13597             if(this.labelmd > 0){
13598                 labelCfg.cls += ' col-md-' + this.labelmd;
13599                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13600             }
13601             
13602             if(this.labelsm > 0){
13603                 labelCfg.cls += ' col-sm-' + this.labelsm;
13604                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13605             }
13606             
13607             if(this.labelxs > 0){
13608                 labelCfg.cls += ' col-xs-' + this.labelxs;
13609                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13610             }
13611             
13612         } else if ( this.fieldLabel.length) {
13613 //                Roo.log(" label");
13614             cfg.cn = [
13615                 indicator,
13616                {
13617                    tag: 'label',
13618                    //cls : 'input-group-addon',
13619                    html : this.fieldLabel
13620
13621                },
13622
13623                combobox
13624
13625             ];
13626             
13627             if(this.indicatorpos == 'right'){
13628                 
13629                 cfg.cn = [
13630                     {
13631                        tag: 'label',
13632                        cn : [
13633                            {
13634                                tag : 'span',
13635                                html : this.fieldLabel
13636                            },
13637                            indicator
13638                        ]
13639
13640                     },
13641                     combobox
13642
13643                 ];
13644
13645             }
13646
13647         } else {
13648             
13649 //                Roo.log(" no label && no align");
13650                 cfg = combobox
13651                      
13652                 
13653         }
13654         
13655         var settings=this;
13656         ['xs','sm','md','lg'].map(function(size){
13657             if (settings[size]) {
13658                 cfg.cls += ' col-' + size + '-' + settings[size];
13659             }
13660         });
13661         
13662         return cfg;
13663         
13664     },
13665     
13666     
13667     
13668     // private
13669     onResize : function(w, h){
13670 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13671 //        if(typeof w == 'number'){
13672 //            var x = w - this.trigger.getWidth();
13673 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13674 //            this.trigger.setStyle('left', x+'px');
13675 //        }
13676     },
13677
13678     // private
13679     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13680
13681     // private
13682     getResizeEl : function(){
13683         return this.inputEl();
13684     },
13685
13686     // private
13687     getPositionEl : function(){
13688         return this.inputEl();
13689     },
13690
13691     // private
13692     alignErrorIcon : function(){
13693         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13694     },
13695
13696     // private
13697     initEvents : function(){
13698         
13699         this.createList();
13700         
13701         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13702         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13703         if(!this.multiple && this.showToggleBtn){
13704             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13705             if(this.hideTrigger){
13706                 this.trigger.setDisplayed(false);
13707             }
13708             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13709         }
13710         
13711         if(this.multiple){
13712             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13713         }
13714         
13715         if(this.removable && !this.editable && !this.tickable){
13716             var close = this.closeTriggerEl();
13717             
13718             if(close){
13719                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13720                 close.on('click', this.removeBtnClick, this, close);
13721             }
13722         }
13723         
13724         //this.trigger.addClassOnOver('x-form-trigger-over');
13725         //this.trigger.addClassOnClick('x-form-trigger-click');
13726         
13727         //if(!this.width){
13728         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13729         //}
13730     },
13731     
13732     closeTriggerEl : function()
13733     {
13734         var close = this.el.select('.roo-combo-removable-btn', true).first();
13735         return close ? close : false;
13736     },
13737     
13738     removeBtnClick : function(e, h, el)
13739     {
13740         e.preventDefault();
13741         
13742         if(this.fireEvent("remove", this) !== false){
13743             this.reset();
13744             this.fireEvent("afterremove", this)
13745         }
13746     },
13747     
13748     createList : function()
13749     {
13750         this.list = Roo.get(document.body).createChild({
13751             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13752             cls: 'typeahead typeahead-long dropdown-menu shadow',
13753             style: 'display:none'
13754         });
13755         
13756         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13757         
13758     },
13759
13760     // private
13761     initTrigger : function(){
13762        
13763     },
13764
13765     // private
13766     onDestroy : function(){
13767         if(this.trigger){
13768             this.trigger.removeAllListeners();
13769           //  this.trigger.remove();
13770         }
13771         //if(this.wrap){
13772         //    this.wrap.remove();
13773         //}
13774         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13775     },
13776
13777     // private
13778     onFocus : function(){
13779         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13780         /*
13781         if(!this.mimicing){
13782             this.wrap.addClass('x-trigger-wrap-focus');
13783             this.mimicing = true;
13784             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13785             if(this.monitorTab){
13786                 this.el.on("keydown", this.checkTab, this);
13787             }
13788         }
13789         */
13790     },
13791
13792     // private
13793     checkTab : function(e){
13794         if(e.getKey() == e.TAB){
13795             this.triggerBlur();
13796         }
13797     },
13798
13799     // private
13800     onBlur : function(){
13801         // do nothing
13802     },
13803
13804     // private
13805     mimicBlur : function(e, t){
13806         /*
13807         if(!this.wrap.contains(t) && this.validateBlur()){
13808             this.triggerBlur();
13809         }
13810         */
13811     },
13812
13813     // private
13814     triggerBlur : function(){
13815         this.mimicing = false;
13816         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13817         if(this.monitorTab){
13818             this.el.un("keydown", this.checkTab, this);
13819         }
13820         //this.wrap.removeClass('x-trigger-wrap-focus');
13821         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13822     },
13823
13824     // private
13825     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13826     validateBlur : function(e, t){
13827         return true;
13828     },
13829
13830     // private
13831     onDisable : function(){
13832         this.inputEl().dom.disabled = true;
13833         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13834         //if(this.wrap){
13835         //    this.wrap.addClass('x-item-disabled');
13836         //}
13837     },
13838
13839     // private
13840     onEnable : function(){
13841         this.inputEl().dom.disabled = false;
13842         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13843         //if(this.wrap){
13844         //    this.el.removeClass('x-item-disabled');
13845         //}
13846     },
13847
13848     // private
13849     onShow : function(){
13850         var ae = this.getActionEl();
13851         
13852         if(ae){
13853             ae.dom.style.display = '';
13854             ae.dom.style.visibility = 'visible';
13855         }
13856     },
13857
13858     // private
13859     
13860     onHide : function(){
13861         var ae = this.getActionEl();
13862         ae.dom.style.display = 'none';
13863     },
13864
13865     /**
13866      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13867      * by an implementing function.
13868      * @method
13869      * @param {EventObject} e
13870      */
13871     onTriggerClick : Roo.emptyFn
13872 });
13873  
13874 /*
13875 * Licence: LGPL
13876 */
13877
13878 /**
13879  * @class Roo.bootstrap.CardUploader
13880  * @extends Roo.bootstrap.Button
13881  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13882  * @cfg {Number} errorTimeout default 3000
13883  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13884  * @cfg {Array}  html The button text.
13885
13886  *
13887  * @constructor
13888  * Create a new CardUploader
13889  * @param {Object} config The config object
13890  */
13891
13892 Roo.bootstrap.CardUploader = function(config){
13893     
13894  
13895     
13896     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13897     
13898     
13899     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13900         return r.data.id
13901      });
13902     
13903      this.addEvents({
13904          // raw events
13905         /**
13906          * @event preview
13907          * When a image is clicked on - and needs to display a slideshow or similar..
13908          * @param {Roo.bootstrap.Card} this
13909          * @param {Object} The image information data 
13910          *
13911          */
13912         'preview' : true,
13913          /**
13914          * @event download
13915          * When a the download link is clicked
13916          * @param {Roo.bootstrap.Card} this
13917          * @param {Object} The image information data  contains 
13918          */
13919         'download' : true
13920         
13921     });
13922 };
13923  
13924 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13925     
13926      
13927     errorTimeout : 3000,
13928      
13929     images : false,
13930    
13931     fileCollection : false,
13932     allowBlank : true,
13933     
13934     getAutoCreate : function()
13935     {
13936         
13937         var cfg =  {
13938             cls :'form-group' ,
13939             cn : [
13940                
13941                 {
13942                     tag: 'label',
13943                    //cls : 'input-group-addon',
13944                     html : this.fieldLabel
13945
13946                 },
13947
13948                 {
13949                     tag: 'input',
13950                     type : 'hidden',
13951                     name : this.name,
13952                     value : this.value,
13953                     cls : 'd-none  form-control'
13954                 },
13955                 
13956                 {
13957                     tag: 'input',
13958                     multiple : 'multiple',
13959                     type : 'file',
13960                     cls : 'd-none  roo-card-upload-selector'
13961                 },
13962                 
13963                 {
13964                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13965                 },
13966                 {
13967                     cls : 'card-columns roo-card-uploader-container'
13968                 }
13969
13970             ]
13971         };
13972            
13973          
13974         return cfg;
13975     },
13976     
13977     getChildContainer : function() /// what children are added to.
13978     {
13979         return this.containerEl;
13980     },
13981    
13982     getButtonContainer : function() /// what children are added to.
13983     {
13984         return this.el.select(".roo-card-uploader-button-container").first();
13985     },
13986    
13987     initEvents : function()
13988     {
13989         
13990         Roo.bootstrap.Input.prototype.initEvents.call(this);
13991         
13992         var t = this;
13993         this.addxtype({
13994             xns: Roo.bootstrap,
13995
13996             xtype : 'Button',
13997             container_method : 'getButtonContainer' ,            
13998             html :  this.html, // fix changable?
13999             cls : 'w-100 ',
14000             listeners : {
14001                 'click' : function(btn, e) {
14002                     t.onClick(e);
14003                 }
14004             }
14005         });
14006         
14007         
14008         
14009         
14010         this.urlAPI = (window.createObjectURL && window) || 
14011                                 (window.URL && URL.revokeObjectURL && URL) || 
14012                                 (window.webkitURL && webkitURL);
14013                         
14014          
14015          
14016          
14017         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14018         
14019         this.selectorEl.on('change', this.onFileSelected, this);
14020         if (this.images) {
14021             var t = this;
14022             this.images.forEach(function(img) {
14023                 t.addCard(img)
14024             });
14025             this.images = false;
14026         }
14027         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14028          
14029        
14030     },
14031     
14032    
14033     onClick : function(e)
14034     {
14035         e.preventDefault();
14036          
14037         this.selectorEl.dom.click();
14038          
14039     },
14040     
14041     onFileSelected : function(e)
14042     {
14043         e.preventDefault();
14044         
14045         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14046             return;
14047         }
14048         
14049         Roo.each(this.selectorEl.dom.files, function(file){    
14050             this.addFile(file);
14051         }, this);
14052          
14053     },
14054     
14055       
14056     
14057       
14058     
14059     addFile : function(file)
14060     {
14061            
14062         if(typeof(file) === 'string'){
14063             throw "Add file by name?"; // should not happen
14064             return;
14065         }
14066         
14067         if(!file || !this.urlAPI){
14068             return;
14069         }
14070         
14071         // file;
14072         // file.type;
14073         
14074         var _this = this;
14075         
14076         
14077         var url = _this.urlAPI.createObjectURL( file);
14078            
14079         this.addCard({
14080             id : Roo.bootstrap.CardUploader.ID--,
14081             is_uploaded : false,
14082             src : url,
14083             srcfile : file,
14084             title : file.name,
14085             mimetype : file.type,
14086             preview : false,
14087             is_deleted : 0
14088         });
14089         
14090     },
14091     
14092     /**
14093      * addCard - add an Attachment to the uploader
14094      * @param data - the data about the image to upload
14095      *
14096      * {
14097           id : 123
14098           title : "Title of file",
14099           is_uploaded : false,
14100           src : "http://.....",
14101           srcfile : { the File upload object },
14102           mimetype : file.type,
14103           preview : false,
14104           is_deleted : 0
14105           .. any other data...
14106         }
14107      *
14108      * 
14109     */
14110     
14111     addCard : function (data)
14112     {
14113         // hidden input element?
14114         // if the file is not an image...
14115         //then we need to use something other that and header_image
14116         var t = this;
14117         //   remove.....
14118         var footer = [
14119             {
14120                 xns : Roo.bootstrap,
14121                 xtype : 'CardFooter',
14122                  items: [
14123                     {
14124                         xns : Roo.bootstrap,
14125                         xtype : 'Element',
14126                         cls : 'd-flex',
14127                         items : [
14128                             
14129                             {
14130                                 xns : Roo.bootstrap,
14131                                 xtype : 'Button',
14132                                 html : String.format("<small>{0}</small>", data.title),
14133                                 cls : 'col-10 text-left',
14134                                 size: 'sm',
14135                                 weight: 'link',
14136                                 fa : 'download',
14137                                 listeners : {
14138                                     click : function() {
14139                                      
14140                                         t.fireEvent( "download", t, data );
14141                                     }
14142                                 }
14143                             },
14144                           
14145                             {
14146                                 xns : Roo.bootstrap,
14147                                 xtype : 'Button',
14148                                 style: 'max-height: 28px; ',
14149                                 size : 'sm',
14150                                 weight: 'danger',
14151                                 cls : 'col-2',
14152                                 fa : 'times',
14153                                 listeners : {
14154                                     click : function() {
14155                                         t.removeCard(data.id)
14156                                     }
14157                                 }
14158                             }
14159                         ]
14160                     }
14161                     
14162                 ] 
14163             }
14164             
14165         ];
14166         
14167         var cn = this.addxtype(
14168             {
14169                  
14170                 xns : Roo.bootstrap,
14171                 xtype : 'Card',
14172                 closeable : true,
14173                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14174                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14175                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14176                 data : data,
14177                 html : false,
14178                  
14179                 items : footer,
14180                 initEvents : function() {
14181                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14182                     var card = this;
14183                     this.imgEl = this.el.select('.card-img-top').first();
14184                     if (this.imgEl) {
14185                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14186                         this.imgEl.set({ 'pointer' : 'cursor' });
14187                                   
14188                     }
14189                     this.getCardFooter().addClass('p-1');
14190                     
14191                   
14192                 }
14193                 
14194             }
14195         );
14196         // dont' really need ot update items.
14197         // this.items.push(cn);
14198         this.fileCollection.add(cn);
14199         
14200         if (!data.srcfile) {
14201             this.updateInput();
14202             return;
14203         }
14204             
14205         var _t = this;
14206         var reader = new FileReader();
14207         reader.addEventListener("load", function() {  
14208             data.srcdata =  reader.result;
14209             _t.updateInput();
14210         });
14211         reader.readAsDataURL(data.srcfile);
14212         
14213         
14214         
14215     },
14216     removeCard : function(id)
14217     {
14218         
14219         var card  = this.fileCollection.get(id);
14220         card.data.is_deleted = 1;
14221         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14222         //this.fileCollection.remove(card);
14223         //this.items = this.items.filter(function(e) { return e != card });
14224         // dont' really need ot update items.
14225         card.el.dom.parentNode.removeChild(card.el.dom);
14226         this.updateInput();
14227
14228         
14229     },
14230     reset: function()
14231     {
14232         this.fileCollection.each(function(card) {
14233             if (card.el.dom && card.el.dom.parentNode) {
14234                 card.el.dom.parentNode.removeChild(card.el.dom);
14235             }
14236         });
14237         this.fileCollection.clear();
14238         this.updateInput();
14239     },
14240     
14241     updateInput : function()
14242     {
14243          var data = [];
14244         this.fileCollection.each(function(e) {
14245             data.push(e.data);
14246             
14247         });
14248         this.inputEl().dom.value = JSON.stringify(data);
14249         
14250         
14251         
14252     }
14253     
14254     
14255 });
14256
14257
14258 Roo.bootstrap.CardUploader.ID = -1;/*
14259  * Based on:
14260  * Ext JS Library 1.1.1
14261  * Copyright(c) 2006-2007, Ext JS, LLC.
14262  *
14263  * Originally Released Under LGPL - original licence link has changed is not relivant.
14264  *
14265  * Fork - LGPL
14266  * <script type="text/javascript">
14267  */
14268
14269
14270 /**
14271  * @class Roo.data.SortTypes
14272  * @singleton
14273  * Defines the default sorting (casting?) comparison functions used when sorting data.
14274  */
14275 Roo.data.SortTypes = {
14276     /**
14277      * Default sort that does nothing
14278      * @param {Mixed} s The value being converted
14279      * @return {Mixed} The comparison value
14280      */
14281     none : function(s){
14282         return s;
14283     },
14284     
14285     /**
14286      * The regular expression used to strip tags
14287      * @type {RegExp}
14288      * @property
14289      */
14290     stripTagsRE : /<\/?[^>]+>/gi,
14291     
14292     /**
14293      * Strips all HTML tags to sort on text only
14294      * @param {Mixed} s The value being converted
14295      * @return {String} The comparison value
14296      */
14297     asText : function(s){
14298         return String(s).replace(this.stripTagsRE, "");
14299     },
14300     
14301     /**
14302      * Strips all HTML tags to sort on text only - Case insensitive
14303      * @param {Mixed} s The value being converted
14304      * @return {String} The comparison value
14305      */
14306     asUCText : function(s){
14307         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14308     },
14309     
14310     /**
14311      * Case insensitive string
14312      * @param {Mixed} s The value being converted
14313      * @return {String} The comparison value
14314      */
14315     asUCString : function(s) {
14316         return String(s).toUpperCase();
14317     },
14318     
14319     /**
14320      * Date sorting
14321      * @param {Mixed} s The value being converted
14322      * @return {Number} The comparison value
14323      */
14324     asDate : function(s) {
14325         if(!s){
14326             return 0;
14327         }
14328         if(s instanceof Date){
14329             return s.getTime();
14330         }
14331         return Date.parse(String(s));
14332     },
14333     
14334     /**
14335      * Float sorting
14336      * @param {Mixed} s The value being converted
14337      * @return {Float} The comparison value
14338      */
14339     asFloat : function(s) {
14340         var val = parseFloat(String(s).replace(/,/g, ""));
14341         if(isNaN(val)) {
14342             val = 0;
14343         }
14344         return val;
14345     },
14346     
14347     /**
14348      * Integer sorting
14349      * @param {Mixed} s The value being converted
14350      * @return {Number} The comparison value
14351      */
14352     asInt : function(s) {
14353         var val = parseInt(String(s).replace(/,/g, ""));
14354         if(isNaN(val)) {
14355             val = 0;
14356         }
14357         return val;
14358     }
14359 };/*
14360  * Based on:
14361  * Ext JS Library 1.1.1
14362  * Copyright(c) 2006-2007, Ext JS, LLC.
14363  *
14364  * Originally Released Under LGPL - original licence link has changed is not relivant.
14365  *
14366  * Fork - LGPL
14367  * <script type="text/javascript">
14368  */
14369
14370 /**
14371 * @class Roo.data.Record
14372  * Instances of this class encapsulate both record <em>definition</em> information, and record
14373  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14374  * to access Records cached in an {@link Roo.data.Store} object.<br>
14375  * <p>
14376  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14377  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14378  * objects.<br>
14379  * <p>
14380  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14381  * @constructor
14382  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14383  * {@link #create}. The parameters are the same.
14384  * @param {Array} data An associative Array of data values keyed by the field name.
14385  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14386  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14387  * not specified an integer id is generated.
14388  */
14389 Roo.data.Record = function(data, id){
14390     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14391     this.data = data;
14392 };
14393
14394 /**
14395  * Generate a constructor for a specific record layout.
14396  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14397  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14398  * Each field definition object may contain the following properties: <ul>
14399  * <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,
14400  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14401  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14402  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14403  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14404  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14405  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14406  * this may be omitted.</p></li>
14407  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14408  * <ul><li>auto (Default, implies no conversion)</li>
14409  * <li>string</li>
14410  * <li>int</li>
14411  * <li>float</li>
14412  * <li>boolean</li>
14413  * <li>date</li></ul></p></li>
14414  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14415  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14416  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14417  * by the Reader into an object that will be stored in the Record. It is passed the
14418  * following parameters:<ul>
14419  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14420  * </ul></p></li>
14421  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14422  * </ul>
14423  * <br>usage:<br><pre><code>
14424 var TopicRecord = Roo.data.Record.create(
14425     {name: 'title', mapping: 'topic_title'},
14426     {name: 'author', mapping: 'username'},
14427     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14428     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14429     {name: 'lastPoster', mapping: 'user2'},
14430     {name: 'excerpt', mapping: 'post_text'}
14431 );
14432
14433 var myNewRecord = new TopicRecord({
14434     title: 'Do my job please',
14435     author: 'noobie',
14436     totalPosts: 1,
14437     lastPost: new Date(),
14438     lastPoster: 'Animal',
14439     excerpt: 'No way dude!'
14440 });
14441 myStore.add(myNewRecord);
14442 </code></pre>
14443  * @method create
14444  * @static
14445  */
14446 Roo.data.Record.create = function(o){
14447     var f = function(){
14448         f.superclass.constructor.apply(this, arguments);
14449     };
14450     Roo.extend(f, Roo.data.Record);
14451     var p = f.prototype;
14452     p.fields = new Roo.util.MixedCollection(false, function(field){
14453         return field.name;
14454     });
14455     for(var i = 0, len = o.length; i < len; i++){
14456         p.fields.add(new Roo.data.Field(o[i]));
14457     }
14458     f.getField = function(name){
14459         return p.fields.get(name);  
14460     };
14461     return f;
14462 };
14463
14464 Roo.data.Record.AUTO_ID = 1000;
14465 Roo.data.Record.EDIT = 'edit';
14466 Roo.data.Record.REJECT = 'reject';
14467 Roo.data.Record.COMMIT = 'commit';
14468
14469 Roo.data.Record.prototype = {
14470     /**
14471      * Readonly flag - true if this record has been modified.
14472      * @type Boolean
14473      */
14474     dirty : false,
14475     editing : false,
14476     error: null,
14477     modified: null,
14478
14479     // private
14480     join : function(store){
14481         this.store = store;
14482     },
14483
14484     /**
14485      * Set the named field to the specified value.
14486      * @param {String} name The name of the field to set.
14487      * @param {Object} value The value to set the field to.
14488      */
14489     set : function(name, value){
14490         if(this.data[name] == value){
14491             return;
14492         }
14493         this.dirty = true;
14494         if(!this.modified){
14495             this.modified = {};
14496         }
14497         if(typeof this.modified[name] == 'undefined'){
14498             this.modified[name] = this.data[name];
14499         }
14500         this.data[name] = value;
14501         if(!this.editing && this.store){
14502             this.store.afterEdit(this);
14503         }       
14504     },
14505
14506     /**
14507      * Get the value of the named field.
14508      * @param {String} name The name of the field to get the value of.
14509      * @return {Object} The value of the field.
14510      */
14511     get : function(name){
14512         return this.data[name]; 
14513     },
14514
14515     // private
14516     beginEdit : function(){
14517         this.editing = true;
14518         this.modified = {}; 
14519     },
14520
14521     // private
14522     cancelEdit : function(){
14523         this.editing = false;
14524         delete this.modified;
14525     },
14526
14527     // private
14528     endEdit : function(){
14529         this.editing = false;
14530         if(this.dirty && this.store){
14531             this.store.afterEdit(this);
14532         }
14533     },
14534
14535     /**
14536      * Usually called by the {@link Roo.data.Store} which owns the Record.
14537      * Rejects all changes made to the Record since either creation, or the last commit operation.
14538      * Modified fields are reverted to their original values.
14539      * <p>
14540      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14541      * of reject operations.
14542      */
14543     reject : function(){
14544         var m = this.modified;
14545         for(var n in m){
14546             if(typeof m[n] != "function"){
14547                 this.data[n] = m[n];
14548             }
14549         }
14550         this.dirty = false;
14551         delete this.modified;
14552         this.editing = false;
14553         if(this.store){
14554             this.store.afterReject(this);
14555         }
14556     },
14557
14558     /**
14559      * Usually called by the {@link Roo.data.Store} which owns the Record.
14560      * Commits all changes made to the Record since either creation, or the last commit operation.
14561      * <p>
14562      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14563      * of commit operations.
14564      */
14565     commit : function(){
14566         this.dirty = false;
14567         delete this.modified;
14568         this.editing = false;
14569         if(this.store){
14570             this.store.afterCommit(this);
14571         }
14572     },
14573
14574     // private
14575     hasError : function(){
14576         return this.error != null;
14577     },
14578
14579     // private
14580     clearError : function(){
14581         this.error = null;
14582     },
14583
14584     /**
14585      * Creates a copy of this record.
14586      * @param {String} id (optional) A new record id if you don't want to use this record's id
14587      * @return {Record}
14588      */
14589     copy : function(newId) {
14590         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14591     }
14592 };/*
14593  * Based on:
14594  * Ext JS Library 1.1.1
14595  * Copyright(c) 2006-2007, Ext JS, LLC.
14596  *
14597  * Originally Released Under LGPL - original licence link has changed is not relivant.
14598  *
14599  * Fork - LGPL
14600  * <script type="text/javascript">
14601  */
14602
14603
14604
14605 /**
14606  * @class Roo.data.Store
14607  * @extends Roo.util.Observable
14608  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14609  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14610  * <p>
14611  * 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
14612  * has no knowledge of the format of the data returned by the Proxy.<br>
14613  * <p>
14614  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14615  * instances from the data object. These records are cached and made available through accessor functions.
14616  * @constructor
14617  * Creates a new Store.
14618  * @param {Object} config A config object containing the objects needed for the Store to access data,
14619  * and read the data into Records.
14620  */
14621 Roo.data.Store = function(config){
14622     this.data = new Roo.util.MixedCollection(false);
14623     this.data.getKey = function(o){
14624         return o.id;
14625     };
14626     this.baseParams = {};
14627     // private
14628     this.paramNames = {
14629         "start" : "start",
14630         "limit" : "limit",
14631         "sort" : "sort",
14632         "dir" : "dir",
14633         "multisort" : "_multisort"
14634     };
14635
14636     if(config && config.data){
14637         this.inlineData = config.data;
14638         delete config.data;
14639     }
14640
14641     Roo.apply(this, config);
14642     
14643     if(this.reader){ // reader passed
14644         this.reader = Roo.factory(this.reader, Roo.data);
14645         this.reader.xmodule = this.xmodule || false;
14646         if(!this.recordType){
14647             this.recordType = this.reader.recordType;
14648         }
14649         if(this.reader.onMetaChange){
14650             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14651         }
14652     }
14653
14654     if(this.recordType){
14655         this.fields = this.recordType.prototype.fields;
14656     }
14657     this.modified = [];
14658
14659     this.addEvents({
14660         /**
14661          * @event datachanged
14662          * Fires when the data cache has changed, and a widget which is using this Store
14663          * as a Record cache should refresh its view.
14664          * @param {Store} this
14665          */
14666         datachanged : true,
14667         /**
14668          * @event metachange
14669          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14670          * @param {Store} this
14671          * @param {Object} meta The JSON metadata
14672          */
14673         metachange : true,
14674         /**
14675          * @event add
14676          * Fires when Records have been added to the Store
14677          * @param {Store} this
14678          * @param {Roo.data.Record[]} records The array of Records added
14679          * @param {Number} index The index at which the record(s) were added
14680          */
14681         add : true,
14682         /**
14683          * @event remove
14684          * Fires when a Record has been removed from the Store
14685          * @param {Store} this
14686          * @param {Roo.data.Record} record The Record that was removed
14687          * @param {Number} index The index at which the record was removed
14688          */
14689         remove : true,
14690         /**
14691          * @event update
14692          * Fires when a Record has been updated
14693          * @param {Store} this
14694          * @param {Roo.data.Record} record The Record that was updated
14695          * @param {String} operation The update operation being performed.  Value may be one of:
14696          * <pre><code>
14697  Roo.data.Record.EDIT
14698  Roo.data.Record.REJECT
14699  Roo.data.Record.COMMIT
14700          * </code></pre>
14701          */
14702         update : true,
14703         /**
14704          * @event clear
14705          * Fires when the data cache has been cleared.
14706          * @param {Store} this
14707          */
14708         clear : true,
14709         /**
14710          * @event beforeload
14711          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14712          * the load action will be canceled.
14713          * @param {Store} this
14714          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14715          */
14716         beforeload : true,
14717         /**
14718          * @event beforeloadadd
14719          * Fires after a new set of Records has been loaded.
14720          * @param {Store} this
14721          * @param {Roo.data.Record[]} records The Records that were loaded
14722          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14723          */
14724         beforeloadadd : true,
14725         /**
14726          * @event load
14727          * Fires after a new set of Records has been loaded, before they are added to the store.
14728          * @param {Store} this
14729          * @param {Roo.data.Record[]} records The Records that were loaded
14730          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14731          * @params {Object} return from reader
14732          */
14733         load : true,
14734         /**
14735          * @event loadexception
14736          * Fires if an exception occurs in the Proxy during loading.
14737          * Called with the signature of the Proxy's "loadexception" event.
14738          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14739          * 
14740          * @param {Proxy} 
14741          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14742          * @param {Object} load options 
14743          * @param {Object} jsonData from your request (normally this contains the Exception)
14744          */
14745         loadexception : true
14746     });
14747     
14748     if(this.proxy){
14749         this.proxy = Roo.factory(this.proxy, Roo.data);
14750         this.proxy.xmodule = this.xmodule || false;
14751         this.relayEvents(this.proxy,  ["loadexception"]);
14752     }
14753     this.sortToggle = {};
14754     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14755
14756     Roo.data.Store.superclass.constructor.call(this);
14757
14758     if(this.inlineData){
14759         this.loadData(this.inlineData);
14760         delete this.inlineData;
14761     }
14762 };
14763
14764 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14765      /**
14766     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14767     * without a remote query - used by combo/forms at present.
14768     */
14769     
14770     /**
14771     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14772     */
14773     /**
14774     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14775     */
14776     /**
14777     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14778     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14779     */
14780     /**
14781     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14782     * on any HTTP request
14783     */
14784     /**
14785     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14786     */
14787     /**
14788     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14789     */
14790     multiSort: false,
14791     /**
14792     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14793     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14794     */
14795     remoteSort : false,
14796
14797     /**
14798     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14799      * loaded or when a record is removed. (defaults to false).
14800     */
14801     pruneModifiedRecords : false,
14802
14803     // private
14804     lastOptions : null,
14805
14806     /**
14807      * Add Records to the Store and fires the add event.
14808      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14809      */
14810     add : function(records){
14811         records = [].concat(records);
14812         for(var i = 0, len = records.length; i < len; i++){
14813             records[i].join(this);
14814         }
14815         var index = this.data.length;
14816         this.data.addAll(records);
14817         this.fireEvent("add", this, records, index);
14818     },
14819
14820     /**
14821      * Remove a Record from the Store and fires the remove event.
14822      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14823      */
14824     remove : function(record){
14825         var index = this.data.indexOf(record);
14826         this.data.removeAt(index);
14827  
14828         if(this.pruneModifiedRecords){
14829             this.modified.remove(record);
14830         }
14831         this.fireEvent("remove", this, record, index);
14832     },
14833
14834     /**
14835      * Remove all Records from the Store and fires the clear event.
14836      */
14837     removeAll : function(){
14838         this.data.clear();
14839         if(this.pruneModifiedRecords){
14840             this.modified = [];
14841         }
14842         this.fireEvent("clear", this);
14843     },
14844
14845     /**
14846      * Inserts Records to the Store at the given index and fires the add event.
14847      * @param {Number} index The start index at which to insert the passed Records.
14848      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14849      */
14850     insert : function(index, records){
14851         records = [].concat(records);
14852         for(var i = 0, len = records.length; i < len; i++){
14853             this.data.insert(index, records[i]);
14854             records[i].join(this);
14855         }
14856         this.fireEvent("add", this, records, index);
14857     },
14858
14859     /**
14860      * Get the index within the cache of the passed Record.
14861      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14862      * @return {Number} The index of the passed Record. Returns -1 if not found.
14863      */
14864     indexOf : function(record){
14865         return this.data.indexOf(record);
14866     },
14867
14868     /**
14869      * Get the index within the cache of the Record with the passed id.
14870      * @param {String} id The id of the Record to find.
14871      * @return {Number} The index of the Record. Returns -1 if not found.
14872      */
14873     indexOfId : function(id){
14874         return this.data.indexOfKey(id);
14875     },
14876
14877     /**
14878      * Get the Record with the specified id.
14879      * @param {String} id The id of the Record to find.
14880      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14881      */
14882     getById : function(id){
14883         return this.data.key(id);
14884     },
14885
14886     /**
14887      * Get the Record at the specified index.
14888      * @param {Number} index The index of the Record to find.
14889      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14890      */
14891     getAt : function(index){
14892         return this.data.itemAt(index);
14893     },
14894
14895     /**
14896      * Returns a range of Records between specified indices.
14897      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14898      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14899      * @return {Roo.data.Record[]} An array of Records
14900      */
14901     getRange : function(start, end){
14902         return this.data.getRange(start, end);
14903     },
14904
14905     // private
14906     storeOptions : function(o){
14907         o = Roo.apply({}, o);
14908         delete o.callback;
14909         delete o.scope;
14910         this.lastOptions = o;
14911     },
14912
14913     /**
14914      * Loads the Record cache from the configured Proxy using the configured Reader.
14915      * <p>
14916      * If using remote paging, then the first load call must specify the <em>start</em>
14917      * and <em>limit</em> properties in the options.params property to establish the initial
14918      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14919      * <p>
14920      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14921      * and this call will return before the new data has been loaded. Perform any post-processing
14922      * in a callback function, or in a "load" event handler.</strong>
14923      * <p>
14924      * @param {Object} options An object containing properties which control loading options:<ul>
14925      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14926      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14927      * passed the following arguments:<ul>
14928      * <li>r : Roo.data.Record[]</li>
14929      * <li>options: Options object from the load call</li>
14930      * <li>success: Boolean success indicator</li></ul></li>
14931      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14932      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14933      * </ul>
14934      */
14935     load : function(options){
14936         options = options || {};
14937         if(this.fireEvent("beforeload", this, options) !== false){
14938             this.storeOptions(options);
14939             var p = Roo.apply(options.params || {}, this.baseParams);
14940             // if meta was not loaded from remote source.. try requesting it.
14941             if (!this.reader.metaFromRemote) {
14942                 p._requestMeta = 1;
14943             }
14944             if(this.sortInfo && this.remoteSort){
14945                 var pn = this.paramNames;
14946                 p[pn["sort"]] = this.sortInfo.field;
14947                 p[pn["dir"]] = this.sortInfo.direction;
14948             }
14949             if (this.multiSort) {
14950                 var pn = this.paramNames;
14951                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14952             }
14953             
14954             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14955         }
14956     },
14957
14958     /**
14959      * Reloads the Record cache from the configured Proxy using the configured Reader and
14960      * the options from the last load operation performed.
14961      * @param {Object} options (optional) An object containing properties which may override the options
14962      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14963      * the most recently used options are reused).
14964      */
14965     reload : function(options){
14966         this.load(Roo.applyIf(options||{}, this.lastOptions));
14967     },
14968
14969     // private
14970     // Called as a callback by the Reader during a load operation.
14971     loadRecords : function(o, options, success){
14972         if(!o || success === false){
14973             if(success !== false){
14974                 this.fireEvent("load", this, [], options, o);
14975             }
14976             if(options.callback){
14977                 options.callback.call(options.scope || this, [], options, false);
14978             }
14979             return;
14980         }
14981         // if data returned failure - throw an exception.
14982         if (o.success === false) {
14983             // show a message if no listener is registered.
14984             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14985                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14986             }
14987             // loadmask wil be hooked into this..
14988             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14989             return;
14990         }
14991         var r = o.records, t = o.totalRecords || r.length;
14992         
14993         this.fireEvent("beforeloadadd", this, r, options, o);
14994         
14995         if(!options || options.add !== true){
14996             if(this.pruneModifiedRecords){
14997                 this.modified = [];
14998             }
14999             for(var i = 0, len = r.length; i < len; i++){
15000                 r[i].join(this);
15001             }
15002             if(this.snapshot){
15003                 this.data = this.snapshot;
15004                 delete this.snapshot;
15005             }
15006             this.data.clear();
15007             this.data.addAll(r);
15008             this.totalLength = t;
15009             this.applySort();
15010             this.fireEvent("datachanged", this);
15011         }else{
15012             this.totalLength = Math.max(t, this.data.length+r.length);
15013             this.add(r);
15014         }
15015         
15016         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15017                 
15018             var e = new Roo.data.Record({});
15019
15020             e.set(this.parent.displayField, this.parent.emptyTitle);
15021             e.set(this.parent.valueField, '');
15022
15023             this.insert(0, e);
15024         }
15025             
15026         this.fireEvent("load", this, r, options, o);
15027         if(options.callback){
15028             options.callback.call(options.scope || this, r, options, true);
15029         }
15030     },
15031
15032
15033     /**
15034      * Loads data from a passed data block. A Reader which understands the format of the data
15035      * must have been configured in the constructor.
15036      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15037      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15038      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15039      */
15040     loadData : function(o, append){
15041         var r = this.reader.readRecords(o);
15042         this.loadRecords(r, {add: append}, true);
15043     },
15044     
15045      /**
15046      * using 'cn' the nested child reader read the child array into it's child stores.
15047      * @param {Object} rec The record with a 'children array
15048      */
15049     loadDataFromChildren : function(rec)
15050     {
15051         this.loadData(this.reader.toLoadData(rec));
15052     },
15053     
15054
15055     /**
15056      * Gets the number of cached records.
15057      * <p>
15058      * <em>If using paging, this may not be the total size of the dataset. If the data object
15059      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15060      * the data set size</em>
15061      */
15062     getCount : function(){
15063         return this.data.length || 0;
15064     },
15065
15066     /**
15067      * Gets the total number of records in the dataset as returned by the server.
15068      * <p>
15069      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15070      * the dataset size</em>
15071      */
15072     getTotalCount : function(){
15073         return this.totalLength || 0;
15074     },
15075
15076     /**
15077      * Returns the sort state of the Store as an object with two properties:
15078      * <pre><code>
15079  field {String} The name of the field by which the Records are sorted
15080  direction {String} The sort order, "ASC" or "DESC"
15081      * </code></pre>
15082      */
15083     getSortState : function(){
15084         return this.sortInfo;
15085     },
15086
15087     // private
15088     applySort : function(){
15089         if(this.sortInfo && !this.remoteSort){
15090             var s = this.sortInfo, f = s.field;
15091             var st = this.fields.get(f).sortType;
15092             var fn = function(r1, r2){
15093                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15094                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15095             };
15096             this.data.sort(s.direction, fn);
15097             if(this.snapshot && this.snapshot != this.data){
15098                 this.snapshot.sort(s.direction, fn);
15099             }
15100         }
15101     },
15102
15103     /**
15104      * Sets the default sort column and order to be used by the next load operation.
15105      * @param {String} fieldName The name of the field to sort by.
15106      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15107      */
15108     setDefaultSort : function(field, dir){
15109         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15110     },
15111
15112     /**
15113      * Sort the Records.
15114      * If remote sorting is used, the sort is performed on the server, and the cache is
15115      * reloaded. If local sorting is used, the cache is sorted internally.
15116      * @param {String} fieldName The name of the field to sort by.
15117      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15118      */
15119     sort : function(fieldName, dir){
15120         var f = this.fields.get(fieldName);
15121         if(!dir){
15122             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15123             
15124             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15125                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15126             }else{
15127                 dir = f.sortDir;
15128             }
15129         }
15130         this.sortToggle[f.name] = dir;
15131         this.sortInfo = {field: f.name, direction: dir};
15132         if(!this.remoteSort){
15133             this.applySort();
15134             this.fireEvent("datachanged", this);
15135         }else{
15136             this.load(this.lastOptions);
15137         }
15138     },
15139
15140     /**
15141      * Calls the specified function for each of the Records in the cache.
15142      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15143      * Returning <em>false</em> aborts and exits the iteration.
15144      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15145      */
15146     each : function(fn, scope){
15147         this.data.each(fn, scope);
15148     },
15149
15150     /**
15151      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15152      * (e.g., during paging).
15153      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15154      */
15155     getModifiedRecords : function(){
15156         return this.modified;
15157     },
15158
15159     // private
15160     createFilterFn : function(property, value, anyMatch){
15161         if(!value.exec){ // not a regex
15162             value = String(value);
15163             if(value.length == 0){
15164                 return false;
15165             }
15166             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15167         }
15168         return function(r){
15169             return value.test(r.data[property]);
15170         };
15171     },
15172
15173     /**
15174      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15175      * @param {String} property A field on your records
15176      * @param {Number} start The record index to start at (defaults to 0)
15177      * @param {Number} end The last record index to include (defaults to length - 1)
15178      * @return {Number} The sum
15179      */
15180     sum : function(property, start, end){
15181         var rs = this.data.items, v = 0;
15182         start = start || 0;
15183         end = (end || end === 0) ? end : rs.length-1;
15184
15185         for(var i = start; i <= end; i++){
15186             v += (rs[i].data[property] || 0);
15187         }
15188         return v;
15189     },
15190
15191     /**
15192      * Filter the records by a specified property.
15193      * @param {String} field A field on your records
15194      * @param {String/RegExp} value Either a string that the field
15195      * should start with or a RegExp to test against the field
15196      * @param {Boolean} anyMatch True to match any part not just the beginning
15197      */
15198     filter : function(property, value, anyMatch){
15199         var fn = this.createFilterFn(property, value, anyMatch);
15200         return fn ? this.filterBy(fn) : this.clearFilter();
15201     },
15202
15203     /**
15204      * Filter by a function. The specified function will be called with each
15205      * record in this data source. If the function returns true the record is included,
15206      * otherwise it is filtered.
15207      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15208      * @param {Object} scope (optional) The scope of the function (defaults to this)
15209      */
15210     filterBy : function(fn, scope){
15211         this.snapshot = this.snapshot || this.data;
15212         this.data = this.queryBy(fn, scope||this);
15213         this.fireEvent("datachanged", this);
15214     },
15215
15216     /**
15217      * Query the records by a specified property.
15218      * @param {String} field A field on your records
15219      * @param {String/RegExp} value Either a string that the field
15220      * should start with or a RegExp to test against the field
15221      * @param {Boolean} anyMatch True to match any part not just the beginning
15222      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15223      */
15224     query : function(property, value, anyMatch){
15225         var fn = this.createFilterFn(property, value, anyMatch);
15226         return fn ? this.queryBy(fn) : this.data.clone();
15227     },
15228
15229     /**
15230      * Query by a function. The specified function will be called with each
15231      * record in this data source. If the function returns true the record is included
15232      * in the results.
15233      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15234      * @param {Object} scope (optional) The scope of the function (defaults to this)
15235       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15236      **/
15237     queryBy : function(fn, scope){
15238         var data = this.snapshot || this.data;
15239         return data.filterBy(fn, scope||this);
15240     },
15241
15242     /**
15243      * Collects unique values for a particular dataIndex from this store.
15244      * @param {String} dataIndex The property to collect
15245      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15246      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15247      * @return {Array} An array of the unique values
15248      **/
15249     collect : function(dataIndex, allowNull, bypassFilter){
15250         var d = (bypassFilter === true && this.snapshot) ?
15251                 this.snapshot.items : this.data.items;
15252         var v, sv, r = [], l = {};
15253         for(var i = 0, len = d.length; i < len; i++){
15254             v = d[i].data[dataIndex];
15255             sv = String(v);
15256             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15257                 l[sv] = true;
15258                 r[r.length] = v;
15259             }
15260         }
15261         return r;
15262     },
15263
15264     /**
15265      * Revert to a view of the Record cache with no filtering applied.
15266      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15267      */
15268     clearFilter : function(suppressEvent){
15269         if(this.snapshot && this.snapshot != this.data){
15270             this.data = this.snapshot;
15271             delete this.snapshot;
15272             if(suppressEvent !== true){
15273                 this.fireEvent("datachanged", this);
15274             }
15275         }
15276     },
15277
15278     // private
15279     afterEdit : function(record){
15280         if(this.modified.indexOf(record) == -1){
15281             this.modified.push(record);
15282         }
15283         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15284     },
15285     
15286     // private
15287     afterReject : function(record){
15288         this.modified.remove(record);
15289         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15290     },
15291
15292     // private
15293     afterCommit : function(record){
15294         this.modified.remove(record);
15295         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15296     },
15297
15298     /**
15299      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15300      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15301      */
15302     commitChanges : function(){
15303         var m = this.modified.slice(0);
15304         this.modified = [];
15305         for(var i = 0, len = m.length; i < len; i++){
15306             m[i].commit();
15307         }
15308     },
15309
15310     /**
15311      * Cancel outstanding changes on all changed records.
15312      */
15313     rejectChanges : function(){
15314         var m = this.modified.slice(0);
15315         this.modified = [];
15316         for(var i = 0, len = m.length; i < len; i++){
15317             m[i].reject();
15318         }
15319     },
15320
15321     onMetaChange : function(meta, rtype, o){
15322         this.recordType = rtype;
15323         this.fields = rtype.prototype.fields;
15324         delete this.snapshot;
15325         this.sortInfo = meta.sortInfo || this.sortInfo;
15326         this.modified = [];
15327         this.fireEvent('metachange', this, this.reader.meta);
15328     },
15329     
15330     moveIndex : function(data, type)
15331     {
15332         var index = this.indexOf(data);
15333         
15334         var newIndex = index + type;
15335         
15336         this.remove(data);
15337         
15338         this.insert(newIndex, data);
15339         
15340     }
15341 });/*
15342  * Based on:
15343  * Ext JS Library 1.1.1
15344  * Copyright(c) 2006-2007, Ext JS, LLC.
15345  *
15346  * Originally Released Under LGPL - original licence link has changed is not relivant.
15347  *
15348  * Fork - LGPL
15349  * <script type="text/javascript">
15350  */
15351
15352 /**
15353  * @class Roo.data.SimpleStore
15354  * @extends Roo.data.Store
15355  * Small helper class to make creating Stores from Array data easier.
15356  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15357  * @cfg {Array} fields An array of field definition objects, or field name strings.
15358  * @cfg {Object} an existing reader (eg. copied from another store)
15359  * @cfg {Array} data The multi-dimensional array of data
15360  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15361  * @cfg {Roo.data.Reader} reader  [not-required] 
15362  * @constructor
15363  * @param {Object} config
15364  */
15365 Roo.data.SimpleStore = function(config)
15366 {
15367     Roo.data.SimpleStore.superclass.constructor.call(this, {
15368         isLocal : true,
15369         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15370                 id: config.id
15371             },
15372             Roo.data.Record.create(config.fields)
15373         ),
15374         proxy : new Roo.data.MemoryProxy(config.data)
15375     });
15376     this.load();
15377 };
15378 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15379  * Based on:
15380  * Ext JS Library 1.1.1
15381  * Copyright(c) 2006-2007, Ext JS, LLC.
15382  *
15383  * Originally Released Under LGPL - original licence link has changed is not relivant.
15384  *
15385  * Fork - LGPL
15386  * <script type="text/javascript">
15387  */
15388
15389 /**
15390 /**
15391  * @extends Roo.data.Store
15392  * @class Roo.data.JsonStore
15393  * Small helper class to make creating Stores for JSON data easier. <br/>
15394 <pre><code>
15395 var store = new Roo.data.JsonStore({
15396     url: 'get-images.php',
15397     root: 'images',
15398     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15399 });
15400 </code></pre>
15401  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15402  * JsonReader and HttpProxy (unless inline data is provided).</b>
15403  * @cfg {Array} fields An array of field definition objects, or field name strings.
15404  * @constructor
15405  * @param {Object} config
15406  */
15407 Roo.data.JsonStore = function(c){
15408     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15409         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15410         reader: new Roo.data.JsonReader(c, c.fields)
15411     }));
15412 };
15413 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15414  * Based on:
15415  * Ext JS Library 1.1.1
15416  * Copyright(c) 2006-2007, Ext JS, LLC.
15417  *
15418  * Originally Released Under LGPL - original licence link has changed is not relivant.
15419  *
15420  * Fork - LGPL
15421  * <script type="text/javascript">
15422  */
15423
15424  
15425 Roo.data.Field = function(config){
15426     if(typeof config == "string"){
15427         config = {name: config};
15428     }
15429     Roo.apply(this, config);
15430     
15431     if(!this.type){
15432         this.type = "auto";
15433     }
15434     
15435     var st = Roo.data.SortTypes;
15436     // named sortTypes are supported, here we look them up
15437     if(typeof this.sortType == "string"){
15438         this.sortType = st[this.sortType];
15439     }
15440     
15441     // set default sortType for strings and dates
15442     if(!this.sortType){
15443         switch(this.type){
15444             case "string":
15445                 this.sortType = st.asUCString;
15446                 break;
15447             case "date":
15448                 this.sortType = st.asDate;
15449                 break;
15450             default:
15451                 this.sortType = st.none;
15452         }
15453     }
15454
15455     // define once
15456     var stripRe = /[\$,%]/g;
15457
15458     // prebuilt conversion function for this field, instead of
15459     // switching every time we're reading a value
15460     if(!this.convert){
15461         var cv, dateFormat = this.dateFormat;
15462         switch(this.type){
15463             case "":
15464             case "auto":
15465             case undefined:
15466                 cv = function(v){ return v; };
15467                 break;
15468             case "string":
15469                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15470                 break;
15471             case "int":
15472                 cv = function(v){
15473                     return v !== undefined && v !== null && v !== '' ?
15474                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15475                     };
15476                 break;
15477             case "float":
15478                 cv = function(v){
15479                     return v !== undefined && v !== null && v !== '' ?
15480                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15481                     };
15482                 break;
15483             case "bool":
15484             case "boolean":
15485                 cv = function(v){ return v === true || v === "true" || v == 1; };
15486                 break;
15487             case "date":
15488                 cv = function(v){
15489                     if(!v){
15490                         return '';
15491                     }
15492                     if(v instanceof Date){
15493                         return v;
15494                     }
15495                     if(dateFormat){
15496                         if(dateFormat == "timestamp"){
15497                             return new Date(v*1000);
15498                         }
15499                         return Date.parseDate(v, dateFormat);
15500                     }
15501                     var parsed = Date.parse(v);
15502                     return parsed ? new Date(parsed) : null;
15503                 };
15504              break;
15505             
15506         }
15507         this.convert = cv;
15508     }
15509 };
15510
15511 Roo.data.Field.prototype = {
15512     dateFormat: null,
15513     defaultValue: "",
15514     mapping: null,
15515     sortType : null,
15516     sortDir : "ASC"
15517 };/*
15518  * Based on:
15519  * Ext JS Library 1.1.1
15520  * Copyright(c) 2006-2007, Ext JS, LLC.
15521  *
15522  * Originally Released Under LGPL - original licence link has changed is not relivant.
15523  *
15524  * Fork - LGPL
15525  * <script type="text/javascript">
15526  */
15527  
15528 // Base class for reading structured data from a data source.  This class is intended to be
15529 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15530
15531 /**
15532  * @class Roo.data.DataReader
15533  * @abstract
15534  * Base class for reading structured data from a data source.  This class is intended to be
15535  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15536  */
15537
15538 Roo.data.DataReader = function(meta, recordType){
15539     
15540     this.meta = meta;
15541     
15542     this.recordType = recordType instanceof Array ? 
15543         Roo.data.Record.create(recordType) : recordType;
15544 };
15545
15546 Roo.data.DataReader.prototype = {
15547     
15548     
15549     readerType : 'Data',
15550      /**
15551      * Create an empty record
15552      * @param {Object} data (optional) - overlay some values
15553      * @return {Roo.data.Record} record created.
15554      */
15555     newRow :  function(d) {
15556         var da =  {};
15557         this.recordType.prototype.fields.each(function(c) {
15558             switch( c.type) {
15559                 case 'int' : da[c.name] = 0; break;
15560                 case 'date' : da[c.name] = new Date(); break;
15561                 case 'float' : da[c.name] = 0.0; break;
15562                 case 'boolean' : da[c.name] = false; break;
15563                 default : da[c.name] = ""; break;
15564             }
15565             
15566         });
15567         return new this.recordType(Roo.apply(da, d));
15568     }
15569     
15570     
15571 };/*
15572  * Based on:
15573  * Ext JS Library 1.1.1
15574  * Copyright(c) 2006-2007, Ext JS, LLC.
15575  *
15576  * Originally Released Under LGPL - original licence link has changed is not relivant.
15577  *
15578  * Fork - LGPL
15579  * <script type="text/javascript">
15580  */
15581
15582 /**
15583  * @class Roo.data.DataProxy
15584  * @extends Roo.data.Observable
15585  * @abstract
15586  * This class is an abstract base class for implementations which provide retrieval of
15587  * unformatted data objects.<br>
15588  * <p>
15589  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15590  * (of the appropriate type which knows how to parse the data object) to provide a block of
15591  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15592  * <p>
15593  * Custom implementations must implement the load method as described in
15594  * {@link Roo.data.HttpProxy#load}.
15595  */
15596 Roo.data.DataProxy = function(){
15597     this.addEvents({
15598         /**
15599          * @event beforeload
15600          * Fires before a network request is made to retrieve a data object.
15601          * @param {Object} This DataProxy object.
15602          * @param {Object} params The params parameter to the load function.
15603          */
15604         beforeload : true,
15605         /**
15606          * @event load
15607          * Fires before the load method's callback is called.
15608          * @param {Object} This DataProxy object.
15609          * @param {Object} o The data object.
15610          * @param {Object} arg The callback argument object passed to the load function.
15611          */
15612         load : true,
15613         /**
15614          * @event loadexception
15615          * Fires if an Exception occurs during data retrieval.
15616          * @param {Object} This DataProxy object.
15617          * @param {Object} o The data object.
15618          * @param {Object} arg The callback argument object passed to the load function.
15619          * @param {Object} e The Exception.
15620          */
15621         loadexception : true
15622     });
15623     Roo.data.DataProxy.superclass.constructor.call(this);
15624 };
15625
15626 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15627
15628     /**
15629      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15630      */
15631 /*
15632  * Based on:
15633  * Ext JS Library 1.1.1
15634  * Copyright(c) 2006-2007, Ext JS, LLC.
15635  *
15636  * Originally Released Under LGPL - original licence link has changed is not relivant.
15637  *
15638  * Fork - LGPL
15639  * <script type="text/javascript">
15640  */
15641 /**
15642  * @class Roo.data.MemoryProxy
15643  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15644  * to the Reader when its load method is called.
15645  * @constructor
15646  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15647  */
15648 Roo.data.MemoryProxy = function(data){
15649     if (data.data) {
15650         data = data.data;
15651     }
15652     Roo.data.MemoryProxy.superclass.constructor.call(this);
15653     this.data = data;
15654 };
15655
15656 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15657     
15658     /**
15659      * Load data from the requested source (in this case an in-memory
15660      * data object passed to the constructor), read the data object into
15661      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15662      * process that block using the passed callback.
15663      * @param {Object} params This parameter is not used by the MemoryProxy class.
15664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15665      * object into a block of Roo.data.Records.
15666      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15667      * The function must be passed <ul>
15668      * <li>The Record block object</li>
15669      * <li>The "arg" argument from the load function</li>
15670      * <li>A boolean success indicator</li>
15671      * </ul>
15672      * @param {Object} scope The scope in which to call the callback
15673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15674      */
15675     load : function(params, reader, callback, scope, arg){
15676         params = params || {};
15677         var result;
15678         try {
15679             result = reader.readRecords(params.data ? params.data :this.data);
15680         }catch(e){
15681             this.fireEvent("loadexception", this, arg, null, e);
15682             callback.call(scope, null, arg, false);
15683             return;
15684         }
15685         callback.call(scope, result, arg, true);
15686     },
15687     
15688     // private
15689     update : function(params, records){
15690         
15691     }
15692 });/*
15693  * Based on:
15694  * Ext JS Library 1.1.1
15695  * Copyright(c) 2006-2007, Ext JS, LLC.
15696  *
15697  * Originally Released Under LGPL - original licence link has changed is not relivant.
15698  *
15699  * Fork - LGPL
15700  * <script type="text/javascript">
15701  */
15702 /**
15703  * @class Roo.data.HttpProxy
15704  * @extends Roo.data.DataProxy
15705  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15706  * configured to reference a certain URL.<br><br>
15707  * <p>
15708  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15709  * from which the running page was served.<br><br>
15710  * <p>
15711  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15712  * <p>
15713  * Be aware that to enable the browser to parse an XML document, the server must set
15714  * the Content-Type header in the HTTP response to "text/xml".
15715  * @constructor
15716  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15717  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15718  * will be used to make the request.
15719  */
15720 Roo.data.HttpProxy = function(conn){
15721     Roo.data.HttpProxy.superclass.constructor.call(this);
15722     // is conn a conn config or a real conn?
15723     this.conn = conn;
15724     this.useAjax = !conn || !conn.events;
15725   
15726 };
15727
15728 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15729     // thse are take from connection...
15730     
15731     /**
15732      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15733      */
15734     /**
15735      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15736      * extra parameters to each request made by this object. (defaults to undefined)
15737      */
15738     /**
15739      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15740      *  to each request made by this object. (defaults to undefined)
15741      */
15742     /**
15743      * @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)
15744      */
15745     /**
15746      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15747      */
15748      /**
15749      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15750      * @type Boolean
15751      */
15752   
15753
15754     /**
15755      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15756      * @type Boolean
15757      */
15758     /**
15759      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15760      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15761      * a finer-grained basis than the DataProxy events.
15762      */
15763     getConnection : function(){
15764         return this.useAjax ? Roo.Ajax : this.conn;
15765     },
15766
15767     /**
15768      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15769      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15770      * process that block using the passed callback.
15771      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15772      * for the request to the remote server.
15773      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15774      * object into a block of Roo.data.Records.
15775      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15776      * The function must be passed <ul>
15777      * <li>The Record block object</li>
15778      * <li>The "arg" argument from the load function</li>
15779      * <li>A boolean success indicator</li>
15780      * </ul>
15781      * @param {Object} scope The scope in which to call the callback
15782      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15783      */
15784     load : function(params, reader, callback, scope, arg){
15785         if(this.fireEvent("beforeload", this, params) !== false){
15786             var  o = {
15787                 params : params || {},
15788                 request: {
15789                     callback : callback,
15790                     scope : scope,
15791                     arg : arg
15792                 },
15793                 reader: reader,
15794                 callback : this.loadResponse,
15795                 scope: this
15796             };
15797             if(this.useAjax){
15798                 Roo.applyIf(o, this.conn);
15799                 if(this.activeRequest){
15800                     Roo.Ajax.abort(this.activeRequest);
15801                 }
15802                 this.activeRequest = Roo.Ajax.request(o);
15803             }else{
15804                 this.conn.request(o);
15805             }
15806         }else{
15807             callback.call(scope||this, null, arg, false);
15808         }
15809     },
15810
15811     // private
15812     loadResponse : function(o, success, response){
15813         delete this.activeRequest;
15814         if(!success){
15815             this.fireEvent("loadexception", this, o, response);
15816             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15817             return;
15818         }
15819         var result;
15820         try {
15821             result = o.reader.read(response);
15822         }catch(e){
15823             this.fireEvent("loadexception", this, o, response, e);
15824             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15825             return;
15826         }
15827         
15828         this.fireEvent("load", this, o, o.request.arg);
15829         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15830     },
15831
15832     // private
15833     update : function(dataSet){
15834
15835     },
15836
15837     // private
15838     updateResponse : function(dataSet){
15839
15840     }
15841 });/*
15842  * Based on:
15843  * Ext JS Library 1.1.1
15844  * Copyright(c) 2006-2007, Ext JS, LLC.
15845  *
15846  * Originally Released Under LGPL - original licence link has changed is not relivant.
15847  *
15848  * Fork - LGPL
15849  * <script type="text/javascript">
15850  */
15851
15852 /**
15853  * @class Roo.data.ScriptTagProxy
15854  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15855  * other than the originating domain of the running page.<br><br>
15856  * <p>
15857  * <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
15858  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15859  * <p>
15860  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15861  * source code that is used as the source inside a &lt;script> tag.<br><br>
15862  * <p>
15863  * In order for the browser to process the returned data, the server must wrap the data object
15864  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15865  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15866  * depending on whether the callback name was passed:
15867  * <p>
15868  * <pre><code>
15869 boolean scriptTag = false;
15870 String cb = request.getParameter("callback");
15871 if (cb != null) {
15872     scriptTag = true;
15873     response.setContentType("text/javascript");
15874 } else {
15875     response.setContentType("application/x-json");
15876 }
15877 Writer out = response.getWriter();
15878 if (scriptTag) {
15879     out.write(cb + "(");
15880 }
15881 out.print(dataBlock.toJsonString());
15882 if (scriptTag) {
15883     out.write(");");
15884 }
15885 </pre></code>
15886  *
15887  * @constructor
15888  * @param {Object} config A configuration object.
15889  */
15890 Roo.data.ScriptTagProxy = function(config){
15891     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15892     Roo.apply(this, config);
15893     this.head = document.getElementsByTagName("head")[0];
15894 };
15895
15896 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15897
15898 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15899     /**
15900      * @cfg {String} url The URL from which to request the data object.
15901      */
15902     /**
15903      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15904      */
15905     timeout : 30000,
15906     /**
15907      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15908      * the server the name of the callback function set up by the load call to process the returned data object.
15909      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15910      * javascript output which calls this named function passing the data object as its only parameter.
15911      */
15912     callbackParam : "callback",
15913     /**
15914      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15915      * name to the request.
15916      */
15917     nocache : true,
15918
15919     /**
15920      * Load data from the configured URL, read the data object into
15921      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15922      * process that block using the passed callback.
15923      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15924      * for the request to the remote server.
15925      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15926      * object into a block of Roo.data.Records.
15927      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15928      * The function must be passed <ul>
15929      * <li>The Record block object</li>
15930      * <li>The "arg" argument from the load function</li>
15931      * <li>A boolean success indicator</li>
15932      * </ul>
15933      * @param {Object} scope The scope in which to call the callback
15934      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15935      */
15936     load : function(params, reader, callback, scope, arg){
15937         if(this.fireEvent("beforeload", this, params) !== false){
15938
15939             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15940
15941             var url = this.url;
15942             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15943             if(this.nocache){
15944                 url += "&_dc=" + (new Date().getTime());
15945             }
15946             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15947             var trans = {
15948                 id : transId,
15949                 cb : "stcCallback"+transId,
15950                 scriptId : "stcScript"+transId,
15951                 params : params,
15952                 arg : arg,
15953                 url : url,
15954                 callback : callback,
15955                 scope : scope,
15956                 reader : reader
15957             };
15958             var conn = this;
15959
15960             window[trans.cb] = function(o){
15961                 conn.handleResponse(o, trans);
15962             };
15963
15964             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15965
15966             if(this.autoAbort !== false){
15967                 this.abort();
15968             }
15969
15970             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15971
15972             var script = document.createElement("script");
15973             script.setAttribute("src", url);
15974             script.setAttribute("type", "text/javascript");
15975             script.setAttribute("id", trans.scriptId);
15976             this.head.appendChild(script);
15977
15978             this.trans = trans;
15979         }else{
15980             callback.call(scope||this, null, arg, false);
15981         }
15982     },
15983
15984     // private
15985     isLoading : function(){
15986         return this.trans ? true : false;
15987     },
15988
15989     /**
15990      * Abort the current server request.
15991      */
15992     abort : function(){
15993         if(this.isLoading()){
15994             this.destroyTrans(this.trans);
15995         }
15996     },
15997
15998     // private
15999     destroyTrans : function(trans, isLoaded){
16000         this.head.removeChild(document.getElementById(trans.scriptId));
16001         clearTimeout(trans.timeoutId);
16002         if(isLoaded){
16003             window[trans.cb] = undefined;
16004             try{
16005                 delete window[trans.cb];
16006             }catch(e){}
16007         }else{
16008             // if hasn't been loaded, wait for load to remove it to prevent script error
16009             window[trans.cb] = function(){
16010                 window[trans.cb] = undefined;
16011                 try{
16012                     delete window[trans.cb];
16013                 }catch(e){}
16014             };
16015         }
16016     },
16017
16018     // private
16019     handleResponse : function(o, trans){
16020         this.trans = false;
16021         this.destroyTrans(trans, true);
16022         var result;
16023         try {
16024             result = trans.reader.readRecords(o);
16025         }catch(e){
16026             this.fireEvent("loadexception", this, o, trans.arg, e);
16027             trans.callback.call(trans.scope||window, null, trans.arg, false);
16028             return;
16029         }
16030         this.fireEvent("load", this, o, trans.arg);
16031         trans.callback.call(trans.scope||window, result, trans.arg, true);
16032     },
16033
16034     // private
16035     handleFailure : function(trans){
16036         this.trans = false;
16037         this.destroyTrans(trans, false);
16038         this.fireEvent("loadexception", this, null, trans.arg);
16039         trans.callback.call(trans.scope||window, null, trans.arg, false);
16040     }
16041 });/*
16042  * Based on:
16043  * Ext JS Library 1.1.1
16044  * Copyright(c) 2006-2007, Ext JS, LLC.
16045  *
16046  * Originally Released Under LGPL - original licence link has changed is not relivant.
16047  *
16048  * Fork - LGPL
16049  * <script type="text/javascript">
16050  */
16051
16052 /**
16053  * @class Roo.data.JsonReader
16054  * @extends Roo.data.DataReader
16055  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16056  * based on mappings in a provided Roo.data.Record constructor.
16057  * 
16058  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16059  * in the reply previously. 
16060  * 
16061  * <p>
16062  * Example code:
16063  * <pre><code>
16064 var RecordDef = Roo.data.Record.create([
16065     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16066     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16067 ]);
16068 var myReader = new Roo.data.JsonReader({
16069     totalProperty: "results",    // The property which contains the total dataset size (optional)
16070     root: "rows",                // The property which contains an Array of row objects
16071     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16072 }, RecordDef);
16073 </code></pre>
16074  * <p>
16075  * This would consume a JSON file like this:
16076  * <pre><code>
16077 { 'results': 2, 'rows': [
16078     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16079     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16080 }
16081 </code></pre>
16082  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16083  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16084  * paged from the remote server.
16085  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16086  * @cfg {String} root name of the property which contains the Array of row objects.
16087  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16088  * @cfg {Array} fields Array of field definition objects
16089  * @constructor
16090  * Create a new JsonReader
16091  * @param {Object} meta Metadata configuration options
16092  * @param {Object} recordType Either an Array of field definition objects,
16093  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16094  */
16095 Roo.data.JsonReader = function(meta, recordType){
16096     
16097     meta = meta || {};
16098     // set some defaults:
16099     Roo.applyIf(meta, {
16100         totalProperty: 'total',
16101         successProperty : 'success',
16102         root : 'data',
16103         id : 'id'
16104     });
16105     
16106     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16107 };
16108 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16109     
16110     readerType : 'Json',
16111     
16112     /**
16113      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16114      * Used by Store query builder to append _requestMeta to params.
16115      * 
16116      */
16117     metaFromRemote : false,
16118     /**
16119      * This method is only used by a DataProxy which has retrieved data from a remote server.
16120      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16121      * @return {Object} data A data block which is used by an Roo.data.Store object as
16122      * a cache of Roo.data.Records.
16123      */
16124     read : function(response){
16125         var json = response.responseText;
16126        
16127         var o = /* eval:var:o */ eval("("+json+")");
16128         if(!o) {
16129             throw {message: "JsonReader.read: Json object not found"};
16130         }
16131         
16132         if(o.metaData){
16133             
16134             delete this.ef;
16135             this.metaFromRemote = true;
16136             this.meta = o.metaData;
16137             this.recordType = Roo.data.Record.create(o.metaData.fields);
16138             this.onMetaChange(this.meta, this.recordType, o);
16139         }
16140         return this.readRecords(o);
16141     },
16142
16143     // private function a store will implement
16144     onMetaChange : function(meta, recordType, o){
16145
16146     },
16147
16148     /**
16149          * @ignore
16150          */
16151     simpleAccess: function(obj, subsc) {
16152         return obj[subsc];
16153     },
16154
16155         /**
16156          * @ignore
16157          */
16158     getJsonAccessor: function(){
16159         var re = /[\[\.]/;
16160         return function(expr) {
16161             try {
16162                 return(re.test(expr))
16163                     ? new Function("obj", "return obj." + expr)
16164                     : function(obj){
16165                         return obj[expr];
16166                     };
16167             } catch(e){}
16168             return Roo.emptyFn;
16169         };
16170     }(),
16171
16172     /**
16173      * Create a data block containing Roo.data.Records from an XML document.
16174      * @param {Object} o An object which contains an Array of row objects in the property specified
16175      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16176      * which contains the total size of the dataset.
16177      * @return {Object} data A data block which is used by an Roo.data.Store object as
16178      * a cache of Roo.data.Records.
16179      */
16180     readRecords : function(o){
16181         /**
16182          * After any data loads, the raw JSON data is available for further custom processing.
16183          * @type Object
16184          */
16185         this.o = o;
16186         var s = this.meta, Record = this.recordType,
16187             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16188
16189 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16190         if (!this.ef) {
16191             if(s.totalProperty) {
16192                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16193                 }
16194                 if(s.successProperty) {
16195                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16196                 }
16197                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16198                 if (s.id) {
16199                         var g = this.getJsonAccessor(s.id);
16200                         this.getId = function(rec) {
16201                                 var r = g(rec);  
16202                                 return (r === undefined || r === "") ? null : r;
16203                         };
16204                 } else {
16205                         this.getId = function(){return null;};
16206                 }
16207             this.ef = [];
16208             for(var jj = 0; jj < fl; jj++){
16209                 f = fi[jj];
16210                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16211                 this.ef[jj] = this.getJsonAccessor(map);
16212             }
16213         }
16214
16215         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16216         if(s.totalProperty){
16217             var vt = parseInt(this.getTotal(o), 10);
16218             if(!isNaN(vt)){
16219                 totalRecords = vt;
16220             }
16221         }
16222         if(s.successProperty){
16223             var vs = this.getSuccess(o);
16224             if(vs === false || vs === 'false'){
16225                 success = false;
16226             }
16227         }
16228         var records = [];
16229         for(var i = 0; i < c; i++){
16230                 var n = root[i];
16231             var values = {};
16232             var id = this.getId(n);
16233             for(var j = 0; j < fl; j++){
16234                 f = fi[j];
16235             var v = this.ef[j](n);
16236             if (!f.convert) {
16237                 Roo.log('missing convert for ' + f.name);
16238                 Roo.log(f);
16239                 continue;
16240             }
16241             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16242             }
16243             var record = new Record(values, id);
16244             record.json = n;
16245             records[i] = record;
16246         }
16247         return {
16248             raw : o,
16249             success : success,
16250             records : records,
16251             totalRecords : totalRecords
16252         };
16253     },
16254     // used when loading children.. @see loadDataFromChildren
16255     toLoadData: function(rec)
16256     {
16257         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16258         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16259         return { data : data, total : data.length };
16260         
16261     }
16262 });/*
16263  * Based on:
16264  * Ext JS Library 1.1.1
16265  * Copyright(c) 2006-2007, Ext JS, LLC.
16266  *
16267  * Originally Released Under LGPL - original licence link has changed is not relivant.
16268  *
16269  * Fork - LGPL
16270  * <script type="text/javascript">
16271  */
16272
16273 /**
16274  * @class Roo.data.ArrayReader
16275  * @extends Roo.data.DataReader
16276  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16277  * Each element of that Array represents a row of data fields. The
16278  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16279  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16280  * <p>
16281  * Example code:.
16282  * <pre><code>
16283 var RecordDef = Roo.data.Record.create([
16284     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16285     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16286 ]);
16287 var myReader = new Roo.data.ArrayReader({
16288     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16289 }, RecordDef);
16290 </code></pre>
16291  * <p>
16292  * This would consume an Array like this:
16293  * <pre><code>
16294 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16295   </code></pre>
16296  
16297  * @constructor
16298  * Create a new JsonReader
16299  * @param {Object} meta Metadata configuration options.
16300  * @param {Object|Array} recordType Either an Array of field definition objects
16301  * 
16302  * @cfg {Array} fields Array of field definition objects
16303  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16304  * as specified to {@link Roo.data.Record#create},
16305  * or an {@link Roo.data.Record} object
16306  *
16307  * 
16308  * created using {@link Roo.data.Record#create}.
16309  */
16310 Roo.data.ArrayReader = function(meta, recordType)
16311 {    
16312     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16313 };
16314
16315 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16316     
16317       /**
16318      * Create a data block containing Roo.data.Records from an XML document.
16319      * @param {Object} o An Array of row objects which represents the dataset.
16320      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16321      * a cache of Roo.data.Records.
16322      */
16323     readRecords : function(o)
16324     {
16325         var sid = this.meta ? this.meta.id : null;
16326         var recordType = this.recordType, fields = recordType.prototype.fields;
16327         var records = [];
16328         var root = o;
16329         for(var i = 0; i < root.length; i++){
16330             var n = root[i];
16331             var values = {};
16332             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16333             for(var j = 0, jlen = fields.length; j < jlen; j++){
16334                 var f = fields.items[j];
16335                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16336                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16337                 v = f.convert(v);
16338                 values[f.name] = v;
16339             }
16340             var record = new recordType(values, id);
16341             record.json = n;
16342             records[records.length] = record;
16343         }
16344         return {
16345             records : records,
16346             totalRecords : records.length
16347         };
16348     },
16349     // used when loading children.. @see loadDataFromChildren
16350     toLoadData: function(rec)
16351     {
16352         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16353         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16354         
16355     }
16356     
16357     
16358 });/*
16359  * - LGPL
16360  * * 
16361  */
16362
16363 /**
16364  * @class Roo.bootstrap.ComboBox
16365  * @extends Roo.bootstrap.TriggerField
16366  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16367  * @cfg {Boolean} append (true|false) default false
16368  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16369  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16370  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16371  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16372  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16373  * @cfg {Boolean} animate default true
16374  * @cfg {Boolean} emptyResultText only for touch device
16375  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16376  * @cfg {String} emptyTitle default ''
16377  * @cfg {Number} width fixed with? experimental
16378  * @constructor
16379  * Create a new ComboBox.
16380  * @param {Object} config Configuration options
16381  */
16382 Roo.bootstrap.ComboBox = function(config){
16383     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16384     this.addEvents({
16385         /**
16386          * @event expand
16387          * Fires when the dropdown list is expanded
16388         * @param {Roo.bootstrap.ComboBox} combo This combo box
16389         */
16390         'expand' : true,
16391         /**
16392          * @event collapse
16393          * Fires when the dropdown list is collapsed
16394         * @param {Roo.bootstrap.ComboBox} combo This combo box
16395         */
16396         'collapse' : true,
16397         /**
16398          * @event beforeselect
16399          * Fires before a list item is selected. Return false to cancel the selection.
16400         * @param {Roo.bootstrap.ComboBox} combo This combo box
16401         * @param {Roo.data.Record} record The data record returned from the underlying store
16402         * @param {Number} index The index of the selected item in the dropdown list
16403         */
16404         'beforeselect' : true,
16405         /**
16406          * @event select
16407          * Fires when a list item is selected
16408         * @param {Roo.bootstrap.ComboBox} combo This combo box
16409         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16410         * @param {Number} index The index of the selected item in the dropdown list
16411         */
16412         'select' : true,
16413         /**
16414          * @event beforequery
16415          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16416          * The event object passed has these properties:
16417         * @param {Roo.bootstrap.ComboBox} combo This combo box
16418         * @param {String} query The query
16419         * @param {Boolean} forceAll true to force "all" query
16420         * @param {Boolean} cancel true to cancel the query
16421         * @param {Object} e The query event object
16422         */
16423         'beforequery': true,
16424          /**
16425          * @event add
16426          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16427         * @param {Roo.bootstrap.ComboBox} combo This combo box
16428         */
16429         'add' : true,
16430         /**
16431          * @event edit
16432          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16433         * @param {Roo.bootstrap.ComboBox} combo This combo box
16434         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16435         */
16436         'edit' : true,
16437         /**
16438          * @event remove
16439          * Fires when the remove value from the combobox array
16440         * @param {Roo.bootstrap.ComboBox} combo This combo box
16441         */
16442         'remove' : true,
16443         /**
16444          * @event afterremove
16445          * Fires when the remove value from the combobox array
16446         * @param {Roo.bootstrap.ComboBox} combo This combo box
16447         */
16448         'afterremove' : true,
16449         /**
16450          * @event specialfilter
16451          * Fires when specialfilter
16452             * @param {Roo.bootstrap.ComboBox} combo This combo box
16453             */
16454         'specialfilter' : true,
16455         /**
16456          * @event tick
16457          * Fires when tick the element
16458             * @param {Roo.bootstrap.ComboBox} combo This combo box
16459             */
16460         'tick' : true,
16461         /**
16462          * @event touchviewdisplay
16463          * Fires when touch view require special display (default is using displayField)
16464             * @param {Roo.bootstrap.ComboBox} combo This combo box
16465             * @param {Object} cfg set html .
16466             */
16467         'touchviewdisplay' : true
16468         
16469     });
16470     
16471     this.item = [];
16472     this.tickItems = [];
16473     
16474     this.selectedIndex = -1;
16475     if(this.mode == 'local'){
16476         if(config.queryDelay === undefined){
16477             this.queryDelay = 10;
16478         }
16479         if(config.minChars === undefined){
16480             this.minChars = 0;
16481         }
16482     }
16483 };
16484
16485 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16486      
16487     /**
16488      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16489      * rendering into an Roo.Editor, defaults to false)
16490      */
16491     /**
16492      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16493      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16494      */
16495     /**
16496      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16497      */
16498     /**
16499      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16500      * the dropdown list (defaults to undefined, with no header element)
16501      */
16502
16503      /**
16504      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16505      */
16506      
16507      /**
16508      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16509      */
16510     listWidth: undefined,
16511     /**
16512      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16513      * mode = 'remote' or 'text' if mode = 'local')
16514      */
16515     displayField: undefined,
16516     
16517     /**
16518      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16519      * mode = 'remote' or 'value' if mode = 'local'). 
16520      * Note: use of a valueField requires the user make a selection
16521      * in order for a value to be mapped.
16522      */
16523     valueField: undefined,
16524     /**
16525      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16526      */
16527     modalTitle : '',
16528     
16529     /**
16530      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16531      * field's data value (defaults to the underlying DOM element's name)
16532      */
16533     hiddenName: undefined,
16534     /**
16535      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16536      */
16537     listClass: '',
16538     /**
16539      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16540      */
16541     selectedClass: 'active',
16542     
16543     /**
16544      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16545      */
16546     shadow:'sides',
16547     /**
16548      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16549      * anchor positions (defaults to 'tl-bl')
16550      */
16551     listAlign: 'tl-bl?',
16552     /**
16553      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16554      */
16555     maxHeight: 300,
16556     /**
16557      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16558      * query specified by the allQuery config option (defaults to 'query')
16559      */
16560     triggerAction: 'query',
16561     /**
16562      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16563      * (defaults to 4, does not apply if editable = false)
16564      */
16565     minChars : 4,
16566     /**
16567      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16568      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16569      */
16570     typeAhead: false,
16571     /**
16572      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16573      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16574      */
16575     queryDelay: 500,
16576     /**
16577      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16578      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16579      */
16580     pageSize: 0,
16581     /**
16582      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16583      * when editable = true (defaults to false)
16584      */
16585     selectOnFocus:false,
16586     /**
16587      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16588      */
16589     queryParam: 'query',
16590     /**
16591      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16592      * when mode = 'remote' (defaults to 'Loading...')
16593      */
16594     loadingText: 'Loading...',
16595     /**
16596      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16597      */
16598     resizable: false,
16599     /**
16600      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16601      */
16602     handleHeight : 8,
16603     /**
16604      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16605      * traditional select (defaults to true)
16606      */
16607     editable: true,
16608     /**
16609      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16610      */
16611     allQuery: '',
16612     /**
16613      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16614      */
16615     mode: 'remote',
16616     /**
16617      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16618      * listWidth has a higher value)
16619      */
16620     minListWidth : 70,
16621     /**
16622      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16623      * allow the user to set arbitrary text into the field (defaults to false)
16624      */
16625     forceSelection:false,
16626     /**
16627      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16628      * if typeAhead = true (defaults to 250)
16629      */
16630     typeAheadDelay : 250,
16631     /**
16632      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16633      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16634      */
16635     valueNotFoundText : undefined,
16636     /**
16637      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16638      */
16639     blockFocus : false,
16640     
16641     /**
16642      * @cfg {Boolean} disableClear Disable showing of clear button.
16643      */
16644     disableClear : false,
16645     /**
16646      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16647      */
16648     alwaysQuery : false,
16649     
16650     /**
16651      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16652      */
16653     multiple : false,
16654     
16655     /**
16656      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16657      */
16658     invalidClass : "has-warning",
16659     
16660     /**
16661      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16662      */
16663     validClass : "has-success",
16664     
16665     /**
16666      * @cfg {Boolean} specialFilter (true|false) special filter default false
16667      */
16668     specialFilter : false,
16669     
16670     /**
16671      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16672      */
16673     mobileTouchView : true,
16674     
16675     /**
16676      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16677      */
16678     useNativeIOS : false,
16679     
16680     /**
16681      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16682      */
16683     mobile_restrict_height : false,
16684     
16685     ios_options : false,
16686     
16687     //private
16688     addicon : false,
16689     editicon: false,
16690     
16691     page: 0,
16692     hasQuery: false,
16693     append: false,
16694     loadNext: false,
16695     autoFocus : true,
16696     tickable : false,
16697     btnPosition : 'right',
16698     triggerList : true,
16699     showToggleBtn : true,
16700     animate : true,
16701     emptyResultText: 'Empty',
16702     triggerText : 'Select',
16703     emptyTitle : '',
16704     width : false,
16705     
16706     // element that contains real text value.. (when hidden is used..)
16707     
16708     getAutoCreate : function()
16709     {   
16710         var cfg = false;
16711         //render
16712         /*
16713          * Render classic select for iso
16714          */
16715         
16716         if(Roo.isIOS && this.useNativeIOS){
16717             cfg = this.getAutoCreateNativeIOS();
16718             return cfg;
16719         }
16720         
16721         /*
16722          * Touch Devices
16723          */
16724         
16725         if(Roo.isTouch && this.mobileTouchView){
16726             cfg = this.getAutoCreateTouchView();
16727             return cfg;;
16728         }
16729         
16730         /*
16731          *  Normal ComboBox
16732          */
16733         if(!this.tickable){
16734             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16735             return cfg;
16736         }
16737         
16738         /*
16739          *  ComboBox with tickable selections
16740          */
16741              
16742         var align = this.labelAlign || this.parentLabelAlign();
16743         
16744         cfg = {
16745             cls : 'form-group roo-combobox-tickable' //input-group
16746         };
16747         
16748         var btn_text_select = '';
16749         var btn_text_done = '';
16750         var btn_text_cancel = '';
16751         
16752         if (this.btn_text_show) {
16753             btn_text_select = 'Select';
16754             btn_text_done = 'Done';
16755             btn_text_cancel = 'Cancel'; 
16756         }
16757         
16758         var buttons = {
16759             tag : 'div',
16760             cls : 'tickable-buttons',
16761             cn : [
16762                 {
16763                     tag : 'button',
16764                     type : 'button',
16765                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16766                     //html : this.triggerText
16767                     html: btn_text_select
16768                 },
16769                 {
16770                     tag : 'button',
16771                     type : 'button',
16772                     name : 'ok',
16773                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16774                     //html : 'Done'
16775                     html: btn_text_done
16776                 },
16777                 {
16778                     tag : 'button',
16779                     type : 'button',
16780                     name : 'cancel',
16781                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16782                     //html : 'Cancel'
16783                     html: btn_text_cancel
16784                 }
16785             ]
16786         };
16787         
16788         if(this.editable){
16789             buttons.cn.unshift({
16790                 tag: 'input',
16791                 cls: 'roo-select2-search-field-input'
16792             });
16793         }
16794         
16795         var _this = this;
16796         
16797         Roo.each(buttons.cn, function(c){
16798             if (_this.size) {
16799                 c.cls += ' btn-' + _this.size;
16800             }
16801
16802             if (_this.disabled) {
16803                 c.disabled = true;
16804             }
16805         });
16806         
16807         var box = {
16808             tag: 'div',
16809             style : 'display: contents',
16810             cn: [
16811                 {
16812                     tag: 'input',
16813                     type : 'hidden',
16814                     cls: 'form-hidden-field'
16815                 },
16816                 {
16817                     tag: 'ul',
16818                     cls: 'roo-select2-choices',
16819                     cn:[
16820                         {
16821                             tag: 'li',
16822                             cls: 'roo-select2-search-field',
16823                             cn: [
16824                                 buttons
16825                             ]
16826                         }
16827                     ]
16828                 }
16829             ]
16830         };
16831         
16832         var combobox = {
16833             cls: 'roo-select2-container input-group roo-select2-container-multi',
16834             cn: [
16835                 
16836                 box
16837 //                {
16838 //                    tag: 'ul',
16839 //                    cls: 'typeahead typeahead-long dropdown-menu',
16840 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16841 //                }
16842             ]
16843         };
16844         
16845         if(this.hasFeedback && !this.allowBlank){
16846             
16847             var feedback = {
16848                 tag: 'span',
16849                 cls: 'glyphicon form-control-feedback'
16850             };
16851
16852             combobox.cn.push(feedback);
16853         }
16854         
16855         
16856         
16857         var indicator = {
16858             tag : 'i',
16859             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16860             tooltip : 'This field is required'
16861         };
16862         if (Roo.bootstrap.version == 4) {
16863             indicator = {
16864                 tag : 'i',
16865                 style : 'display:none'
16866             };
16867         }
16868         if (align ==='left' && this.fieldLabel.length) {
16869             
16870             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16871             
16872             cfg.cn = [
16873                 indicator,
16874                 {
16875                     tag: 'label',
16876                     'for' :  id,
16877                     cls : 'control-label col-form-label',
16878                     html : this.fieldLabel
16879
16880                 },
16881                 {
16882                     cls : "", 
16883                     cn: [
16884                         combobox
16885                     ]
16886                 }
16887
16888             ];
16889             
16890             var labelCfg = cfg.cn[1];
16891             var contentCfg = cfg.cn[2];
16892             
16893
16894             if(this.indicatorpos == 'right'){
16895                 
16896                 cfg.cn = [
16897                     {
16898                         tag: 'label',
16899                         'for' :  id,
16900                         cls : 'control-label col-form-label',
16901                         cn : [
16902                             {
16903                                 tag : 'span',
16904                                 html : this.fieldLabel
16905                             },
16906                             indicator
16907                         ]
16908                     },
16909                     {
16910                         cls : "",
16911                         cn: [
16912                             combobox
16913                         ]
16914                     }
16915
16916                 ];
16917                 
16918                 
16919                 
16920                 labelCfg = cfg.cn[0];
16921                 contentCfg = cfg.cn[1];
16922             
16923             }
16924             
16925             if(this.labelWidth > 12){
16926                 labelCfg.style = "width: " + this.labelWidth + 'px';
16927             }
16928             if(this.width * 1 > 0){
16929                 contentCfg.style = "width: " + this.width + 'px';
16930             }
16931             if(this.labelWidth < 13 && this.labelmd == 0){
16932                 this.labelmd = this.labelWidth;
16933             }
16934             
16935             if(this.labellg > 0){
16936                 labelCfg.cls += ' col-lg-' + this.labellg;
16937                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16938             }
16939             
16940             if(this.labelmd > 0){
16941                 labelCfg.cls += ' col-md-' + this.labelmd;
16942                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16943             }
16944             
16945             if(this.labelsm > 0){
16946                 labelCfg.cls += ' col-sm-' + this.labelsm;
16947                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16948             }
16949             
16950             if(this.labelxs > 0){
16951                 labelCfg.cls += ' col-xs-' + this.labelxs;
16952                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16953             }
16954                 
16955                 
16956         } else if ( this.fieldLabel.length) {
16957 //                Roo.log(" label");
16958                  cfg.cn = [
16959                    indicator,
16960                     {
16961                         tag: 'label',
16962                         //cls : 'input-group-addon',
16963                         html : this.fieldLabel
16964                     },
16965                     combobox
16966                 ];
16967                 
16968                 if(this.indicatorpos == 'right'){
16969                     cfg.cn = [
16970                         {
16971                             tag: 'label',
16972                             //cls : 'input-group-addon',
16973                             html : this.fieldLabel
16974                         },
16975                         indicator,
16976                         combobox
16977                     ];
16978                     
16979                 }
16980
16981         } else {
16982             
16983 //                Roo.log(" no label && no align");
16984                 cfg = combobox
16985                      
16986                 
16987         }
16988          
16989         var settings=this;
16990         ['xs','sm','md','lg'].map(function(size){
16991             if (settings[size]) {
16992                 cfg.cls += ' col-' + size + '-' + settings[size];
16993             }
16994         });
16995         
16996         return cfg;
16997         
16998     },
16999     
17000     _initEventsCalled : false,
17001     
17002     // private
17003     initEvents: function()
17004     {   
17005         if (this._initEventsCalled) { // as we call render... prevent looping...
17006             return;
17007         }
17008         this._initEventsCalled = true;
17009         
17010         if (!this.store) {
17011             throw "can not find store for combo";
17012         }
17013         
17014         this.indicator = this.indicatorEl();
17015         
17016         this.store = Roo.factory(this.store, Roo.data);
17017         this.store.parent = this;
17018         
17019         // if we are building from html. then this element is so complex, that we can not really
17020         // use the rendered HTML.
17021         // so we have to trash and replace the previous code.
17022         if (Roo.XComponent.build_from_html) {
17023             // remove this element....
17024             var e = this.el.dom, k=0;
17025             while (e ) { e = e.previousSibling;  ++k;}
17026
17027             this.el.remove();
17028             
17029             this.el=false;
17030             this.rendered = false;
17031             
17032             this.render(this.parent().getChildContainer(true), k);
17033         }
17034         
17035         if(Roo.isIOS && this.useNativeIOS){
17036             this.initIOSView();
17037             return;
17038         }
17039         
17040         /*
17041          * Touch Devices
17042          */
17043         
17044         if(Roo.isTouch && this.mobileTouchView){
17045             this.initTouchView();
17046             return;
17047         }
17048         
17049         if(this.tickable){
17050             this.initTickableEvents();
17051             return;
17052         }
17053         
17054         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17055         
17056         if(this.hiddenName){
17057             
17058             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17059             
17060             this.hiddenField.dom.value =
17061                 this.hiddenValue !== undefined ? this.hiddenValue :
17062                 this.value !== undefined ? this.value : '';
17063
17064             // prevent input submission
17065             this.el.dom.removeAttribute('name');
17066             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17067              
17068              
17069         }
17070         //if(Roo.isGecko){
17071         //    this.el.dom.setAttribute('autocomplete', 'off');
17072         //}
17073         
17074         var cls = 'x-combo-list';
17075         
17076         //this.list = new Roo.Layer({
17077         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17078         //});
17079         
17080         var _this = this;
17081         
17082         (function(){
17083             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17084             _this.list.setWidth(lw);
17085         }).defer(100);
17086         
17087         this.list.on('mouseover', this.onViewOver, this);
17088         this.list.on('mousemove', this.onViewMove, this);
17089         this.list.on('scroll', this.onViewScroll, this);
17090         
17091         /*
17092         this.list.swallowEvent('mousewheel');
17093         this.assetHeight = 0;
17094
17095         if(this.title){
17096             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17097             this.assetHeight += this.header.getHeight();
17098         }
17099
17100         this.innerList = this.list.createChild({cls:cls+'-inner'});
17101         this.innerList.on('mouseover', this.onViewOver, this);
17102         this.innerList.on('mousemove', this.onViewMove, this);
17103         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17104         
17105         if(this.allowBlank && !this.pageSize && !this.disableClear){
17106             this.footer = this.list.createChild({cls:cls+'-ft'});
17107             this.pageTb = new Roo.Toolbar(this.footer);
17108            
17109         }
17110         if(this.pageSize){
17111             this.footer = this.list.createChild({cls:cls+'-ft'});
17112             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17113                     {pageSize: this.pageSize});
17114             
17115         }
17116         
17117         if (this.pageTb && this.allowBlank && !this.disableClear) {
17118             var _this = this;
17119             this.pageTb.add(new Roo.Toolbar.Fill(), {
17120                 cls: 'x-btn-icon x-btn-clear',
17121                 text: '&#160;',
17122                 handler: function()
17123                 {
17124                     _this.collapse();
17125                     _this.clearValue();
17126                     _this.onSelect(false, -1);
17127                 }
17128             });
17129         }
17130         if (this.footer) {
17131             this.assetHeight += this.footer.getHeight();
17132         }
17133         */
17134             
17135         if(!this.tpl){
17136             this.tpl = Roo.bootstrap.version == 4 ?
17137                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17138                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17139         }
17140
17141         this.view = new Roo.View(this.list, this.tpl, {
17142             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17143         });
17144         //this.view.wrapEl.setDisplayed(false);
17145         this.view.on('click', this.onViewClick, this);
17146         
17147         
17148         this.store.on('beforeload', this.onBeforeLoad, this);
17149         this.store.on('load', this.onLoad, this);
17150         this.store.on('loadexception', this.onLoadException, this);
17151         /*
17152         if(this.resizable){
17153             this.resizer = new Roo.Resizable(this.list,  {
17154                pinned:true, handles:'se'
17155             });
17156             this.resizer.on('resize', function(r, w, h){
17157                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17158                 this.listWidth = w;
17159                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17160                 this.restrictHeight();
17161             }, this);
17162             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17163         }
17164         */
17165         if(!this.editable){
17166             this.editable = true;
17167             this.setEditable(false);
17168         }
17169         
17170         /*
17171         
17172         if (typeof(this.events.add.listeners) != 'undefined') {
17173             
17174             this.addicon = this.wrap.createChild(
17175                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17176        
17177             this.addicon.on('click', function(e) {
17178                 this.fireEvent('add', this);
17179             }, this);
17180         }
17181         if (typeof(this.events.edit.listeners) != 'undefined') {
17182             
17183             this.editicon = this.wrap.createChild(
17184                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17185             if (this.addicon) {
17186                 this.editicon.setStyle('margin-left', '40px');
17187             }
17188             this.editicon.on('click', function(e) {
17189                 
17190                 // we fire even  if inothing is selected..
17191                 this.fireEvent('edit', this, this.lastData );
17192                 
17193             }, this);
17194         }
17195         */
17196         
17197         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17198             "up" : function(e){
17199                 this.inKeyMode = true;
17200                 this.selectPrev();
17201             },
17202
17203             "down" : function(e){
17204                 if(!this.isExpanded()){
17205                     this.onTriggerClick();
17206                 }else{
17207                     this.inKeyMode = true;
17208                     this.selectNext();
17209                 }
17210             },
17211
17212             "enter" : function(e){
17213 //                this.onViewClick();
17214                 //return true;
17215                 this.collapse();
17216                 
17217                 if(this.fireEvent("specialkey", this, e)){
17218                     this.onViewClick(false);
17219                 }
17220                 
17221                 return true;
17222             },
17223
17224             "esc" : function(e){
17225                 this.collapse();
17226             },
17227
17228             "tab" : function(e){
17229                 this.collapse();
17230                 
17231                 if(this.fireEvent("specialkey", this, e)){
17232                     this.onViewClick(false);
17233                 }
17234                 
17235                 return true;
17236             },
17237
17238             scope : this,
17239
17240             doRelay : function(foo, bar, hname){
17241                 if(hname == 'down' || this.scope.isExpanded()){
17242                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17243                 }
17244                 return true;
17245             },
17246
17247             forceKeyDown: true
17248         });
17249         
17250         
17251         this.queryDelay = Math.max(this.queryDelay || 10,
17252                 this.mode == 'local' ? 10 : 250);
17253         
17254         
17255         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17256         
17257         if(this.typeAhead){
17258             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17259         }
17260         if(this.editable !== false){
17261             this.inputEl().on("keyup", this.onKeyUp, this);
17262         }
17263         if(this.forceSelection){
17264             this.inputEl().on('blur', this.doForce, this);
17265         }
17266         
17267         if(this.multiple){
17268             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17269             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17270         }
17271     },
17272     
17273     initTickableEvents: function()
17274     {   
17275         this.createList();
17276         
17277         if(this.hiddenName){
17278             
17279             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17280             
17281             this.hiddenField.dom.value =
17282                 this.hiddenValue !== undefined ? this.hiddenValue :
17283                 this.value !== undefined ? this.value : '';
17284
17285             // prevent input submission
17286             this.el.dom.removeAttribute('name');
17287             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17288              
17289              
17290         }
17291         
17292 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17293         
17294         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17295         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17296         if(this.triggerList){
17297             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17298         }
17299          
17300         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17301         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17302         
17303         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17304         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17305         
17306         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17307         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17308         
17309         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17310         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17311         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17312         
17313         this.okBtn.hide();
17314         this.cancelBtn.hide();
17315         
17316         var _this = this;
17317         
17318         (function(){
17319             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17320             _this.list.setWidth(lw);
17321         }).defer(100);
17322         
17323         this.list.on('mouseover', this.onViewOver, this);
17324         this.list.on('mousemove', this.onViewMove, this);
17325         
17326         this.list.on('scroll', this.onViewScroll, this);
17327         
17328         if(!this.tpl){
17329             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17330                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17331         }
17332
17333         this.view = new Roo.View(this.list, this.tpl, {
17334             singleSelect:true,
17335             tickable:true,
17336             parent:this,
17337             store: this.store,
17338             selectedClass: this.selectedClass
17339         });
17340         
17341         //this.view.wrapEl.setDisplayed(false);
17342         this.view.on('click', this.onViewClick, this);
17343         
17344         
17345         
17346         this.store.on('beforeload', this.onBeforeLoad, this);
17347         this.store.on('load', this.onLoad, this);
17348         this.store.on('loadexception', this.onLoadException, this);
17349         
17350         if(this.editable){
17351             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17352                 "up" : function(e){
17353                     this.inKeyMode = true;
17354                     this.selectPrev();
17355                 },
17356
17357                 "down" : function(e){
17358                     this.inKeyMode = true;
17359                     this.selectNext();
17360                 },
17361
17362                 "enter" : function(e){
17363                     if(this.fireEvent("specialkey", this, e)){
17364                         this.onViewClick(false);
17365                     }
17366                     
17367                     return true;
17368                 },
17369
17370                 "esc" : function(e){
17371                     this.onTickableFooterButtonClick(e, false, false);
17372                 },
17373
17374                 "tab" : function(e){
17375                     this.fireEvent("specialkey", this, e);
17376                     
17377                     this.onTickableFooterButtonClick(e, false, false);
17378                     
17379                     return true;
17380                 },
17381
17382                 scope : this,
17383
17384                 doRelay : function(e, fn, key){
17385                     if(this.scope.isExpanded()){
17386                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17387                     }
17388                     return true;
17389                 },
17390
17391                 forceKeyDown: true
17392             });
17393         }
17394         
17395         this.queryDelay = Math.max(this.queryDelay || 10,
17396                 this.mode == 'local' ? 10 : 250);
17397         
17398         
17399         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17400         
17401         if(this.typeAhead){
17402             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17403         }
17404         
17405         if(this.editable !== false){
17406             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17407         }
17408         
17409         this.indicator = this.indicatorEl();
17410         
17411         if(this.indicator){
17412             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17413             this.indicator.hide();
17414         }
17415         
17416     },
17417
17418     onDestroy : function(){
17419         if(this.view){
17420             this.view.setStore(null);
17421             this.view.el.removeAllListeners();
17422             this.view.el.remove();
17423             this.view.purgeListeners();
17424         }
17425         if(this.list){
17426             this.list.dom.innerHTML  = '';
17427         }
17428         
17429         if(this.store){
17430             this.store.un('beforeload', this.onBeforeLoad, this);
17431             this.store.un('load', this.onLoad, this);
17432             this.store.un('loadexception', this.onLoadException, this);
17433         }
17434         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17435     },
17436
17437     // private
17438     fireKey : function(e){
17439         if(e.isNavKeyPress() && !this.list.isVisible()){
17440             this.fireEvent("specialkey", this, e);
17441         }
17442     },
17443
17444     // private
17445     onResize: function(w, h)
17446     {
17447         
17448         
17449 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17450 //        
17451 //        if(typeof w != 'number'){
17452 //            // we do not handle it!?!?
17453 //            return;
17454 //        }
17455 //        var tw = this.trigger.getWidth();
17456 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17457 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17458 //        var x = w - tw;
17459 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17460 //            
17461 //        //this.trigger.setStyle('left', x+'px');
17462 //        
17463 //        if(this.list && this.listWidth === undefined){
17464 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17465 //            this.list.setWidth(lw);
17466 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17467 //        }
17468         
17469     
17470         
17471     },
17472
17473     /**
17474      * Allow or prevent the user from directly editing the field text.  If false is passed,
17475      * the user will only be able to select from the items defined in the dropdown list.  This method
17476      * is the runtime equivalent of setting the 'editable' config option at config time.
17477      * @param {Boolean} value True to allow the user to directly edit the field text
17478      */
17479     setEditable : function(value){
17480         if(value == this.editable){
17481             return;
17482         }
17483         this.editable = value;
17484         if(!value){
17485             this.inputEl().dom.setAttribute('readOnly', true);
17486             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17487             this.inputEl().addClass('x-combo-noedit');
17488         }else{
17489             this.inputEl().dom.removeAttribute('readOnly');
17490             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17491             this.inputEl().removeClass('x-combo-noedit');
17492         }
17493     },
17494
17495     // private
17496     
17497     onBeforeLoad : function(combo,opts){
17498         if(!this.hasFocus){
17499             return;
17500         }
17501          if (!opts.add) {
17502             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17503          }
17504         this.restrictHeight();
17505         this.selectedIndex = -1;
17506     },
17507
17508     // private
17509     onLoad : function(){
17510         
17511         this.hasQuery = false;
17512         
17513         if(!this.hasFocus){
17514             return;
17515         }
17516         
17517         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17518             this.loading.hide();
17519         }
17520         
17521         if(this.store.getCount() > 0){
17522             
17523             this.expand();
17524             this.restrictHeight();
17525             if(this.lastQuery == this.allQuery){
17526                 if(this.editable && !this.tickable){
17527                     this.inputEl().dom.select();
17528                 }
17529                 
17530                 if(
17531                     !this.selectByValue(this.value, true) &&
17532                     this.autoFocus && 
17533                     (
17534                         !this.store.lastOptions ||
17535                         typeof(this.store.lastOptions.add) == 'undefined' || 
17536                         this.store.lastOptions.add != true
17537                     )
17538                 ){
17539                     this.select(0, true);
17540                 }
17541             }else{
17542                 if(this.autoFocus){
17543                     this.selectNext();
17544                 }
17545                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17546                     this.taTask.delay(this.typeAheadDelay);
17547                 }
17548             }
17549         }else{
17550             this.onEmptyResults();
17551         }
17552         
17553         //this.el.focus();
17554     },
17555     // private
17556     onLoadException : function()
17557     {
17558         this.hasQuery = false;
17559         
17560         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17561             this.loading.hide();
17562         }
17563         
17564         if(this.tickable && this.editable){
17565             return;
17566         }
17567         
17568         this.collapse();
17569         // only causes errors at present
17570         //Roo.log(this.store.reader.jsonData);
17571         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17572             // fixme
17573             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17574         //}
17575         
17576         
17577     },
17578     // private
17579     onTypeAhead : function(){
17580         if(this.store.getCount() > 0){
17581             var r = this.store.getAt(0);
17582             var newValue = r.data[this.displayField];
17583             var len = newValue.length;
17584             var selStart = this.getRawValue().length;
17585             
17586             if(selStart != len){
17587                 this.setRawValue(newValue);
17588                 this.selectText(selStart, newValue.length);
17589             }
17590         }
17591     },
17592
17593     // private
17594     onSelect : function(record, index){
17595         
17596         if(this.fireEvent('beforeselect', this, record, index) !== false){
17597         
17598             this.setFromData(index > -1 ? record.data : false);
17599             
17600             this.collapse();
17601             this.fireEvent('select', this, record, index);
17602         }
17603     },
17604
17605     /**
17606      * Returns the currently selected field value or empty string if no value is set.
17607      * @return {String} value The selected value
17608      */
17609     getValue : function()
17610     {
17611         if(Roo.isIOS && this.useNativeIOS){
17612             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17613         }
17614         
17615         if(this.multiple){
17616             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17617         }
17618         
17619         if(this.valueField){
17620             return typeof this.value != 'undefined' ? this.value : '';
17621         }else{
17622             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17623         }
17624     },
17625     
17626     getRawValue : function()
17627     {
17628         if(Roo.isIOS && this.useNativeIOS){
17629             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17630         }
17631         
17632         var v = this.inputEl().getValue();
17633         
17634         return v;
17635     },
17636
17637     /**
17638      * Clears any text/value currently set in the field
17639      */
17640     clearValue : function(){
17641         
17642         if(this.hiddenField){
17643             this.hiddenField.dom.value = '';
17644         }
17645         this.value = '';
17646         this.setRawValue('');
17647         this.lastSelectionText = '';
17648         this.lastData = false;
17649         
17650         var close = this.closeTriggerEl();
17651         
17652         if(close){
17653             close.hide();
17654         }
17655         
17656         this.validate();
17657         
17658     },
17659
17660     /**
17661      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17662      * will be displayed in the field.  If the value does not match the data value of an existing item,
17663      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17664      * Otherwise the field will be blank (although the value will still be set).
17665      * @param {String} value The value to match
17666      */
17667     setValue : function(v)
17668     {
17669         if(Roo.isIOS && this.useNativeIOS){
17670             this.setIOSValue(v);
17671             return;
17672         }
17673         
17674         if(this.multiple){
17675             this.syncValue();
17676             return;
17677         }
17678         
17679         var text = v;
17680         if(this.valueField){
17681             var r = this.findRecord(this.valueField, v);
17682             if(r){
17683                 text = r.data[this.displayField];
17684             }else if(this.valueNotFoundText !== undefined){
17685                 text = this.valueNotFoundText;
17686             }
17687         }
17688         this.lastSelectionText = text;
17689         if(this.hiddenField){
17690             this.hiddenField.dom.value = v;
17691         }
17692         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17693         this.value = v;
17694         
17695         var close = this.closeTriggerEl();
17696         
17697         if(close){
17698             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17699         }
17700         
17701         this.validate();
17702     },
17703     /**
17704      * @property {Object} the last set data for the element
17705      */
17706     
17707     lastData : false,
17708     /**
17709      * Sets the value of the field based on a object which is related to the record format for the store.
17710      * @param {Object} value the value to set as. or false on reset?
17711      */
17712     setFromData : function(o){
17713         
17714         if(this.multiple){
17715             this.addItem(o);
17716             return;
17717         }
17718             
17719         var dv = ''; // display value
17720         var vv = ''; // value value..
17721         this.lastData = o;
17722         if (this.displayField) {
17723             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17724         } else {
17725             // this is an error condition!!!
17726             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17727         }
17728         
17729         if(this.valueField){
17730             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17731         }
17732         
17733         var close = this.closeTriggerEl();
17734         
17735         if(close){
17736             if(dv.length || vv * 1 > 0){
17737                 close.show() ;
17738                 this.blockFocus=true;
17739             } else {
17740                 close.hide();
17741             }             
17742         }
17743         
17744         if(this.hiddenField){
17745             this.hiddenField.dom.value = vv;
17746             
17747             this.lastSelectionText = dv;
17748             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17749             this.value = vv;
17750             return;
17751         }
17752         // no hidden field.. - we store the value in 'value', but still display
17753         // display field!!!!
17754         this.lastSelectionText = dv;
17755         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17756         this.value = vv;
17757         
17758         
17759         
17760     },
17761     // private
17762     reset : function(){
17763         // overridden so that last data is reset..
17764         
17765         if(this.multiple){
17766             this.clearItem();
17767             return;
17768         }
17769         
17770         this.setValue(this.originalValue);
17771         //this.clearInvalid();
17772         this.lastData = false;
17773         if (this.view) {
17774             this.view.clearSelections();
17775         }
17776         
17777         this.validate();
17778     },
17779     // private
17780     findRecord : function(prop, value){
17781         var record;
17782         if(this.store.getCount() > 0){
17783             this.store.each(function(r){
17784                 if(r.data[prop] == value){
17785                     record = r;
17786                     return false;
17787                 }
17788                 return true;
17789             });
17790         }
17791         return record;
17792     },
17793     
17794     getName: function()
17795     {
17796         // returns hidden if it's set..
17797         if (!this.rendered) {return ''};
17798         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17799         
17800     },
17801     // private
17802     onViewMove : function(e, t){
17803         this.inKeyMode = false;
17804     },
17805
17806     // private
17807     onViewOver : function(e, t){
17808         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17809             return;
17810         }
17811         var item = this.view.findItemFromChild(t);
17812         
17813         if(item){
17814             var index = this.view.indexOf(item);
17815             this.select(index, false);
17816         }
17817     },
17818
17819     // private
17820     onViewClick : function(view, doFocus, el, e)
17821     {
17822         var index = this.view.getSelectedIndexes()[0];
17823         
17824         var r = this.store.getAt(index);
17825         
17826         if(this.tickable){
17827             
17828             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17829                 return;
17830             }
17831             
17832             var rm = false;
17833             var _this = this;
17834             
17835             Roo.each(this.tickItems, function(v,k){
17836                 
17837                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17838                     Roo.log(v);
17839                     _this.tickItems.splice(k, 1);
17840                     
17841                     if(typeof(e) == 'undefined' && view == false){
17842                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17843                     }
17844                     
17845                     rm = true;
17846                     return;
17847                 }
17848             });
17849             
17850             if(rm){
17851                 return;
17852             }
17853             
17854             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17855                 this.tickItems.push(r.data);
17856             }
17857             
17858             if(typeof(e) == 'undefined' && view == false){
17859                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17860             }
17861                     
17862             return;
17863         }
17864         
17865         if(r){
17866             this.onSelect(r, index);
17867         }
17868         if(doFocus !== false && !this.blockFocus){
17869             this.inputEl().focus();
17870         }
17871     },
17872
17873     // private
17874     restrictHeight : function(){
17875         //this.innerList.dom.style.height = '';
17876         //var inner = this.innerList.dom;
17877         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17878         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17879         //this.list.beginUpdate();
17880         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17881         this.list.alignTo(this.inputEl(), this.listAlign);
17882         this.list.alignTo(this.inputEl(), this.listAlign);
17883         //this.list.endUpdate();
17884     },
17885
17886     // private
17887     onEmptyResults : function(){
17888         
17889         if(this.tickable && this.editable){
17890             this.hasFocus = false;
17891             this.restrictHeight();
17892             return;
17893         }
17894         
17895         this.collapse();
17896     },
17897
17898     /**
17899      * Returns true if the dropdown list is expanded, else false.
17900      */
17901     isExpanded : function(){
17902         return this.list.isVisible();
17903     },
17904
17905     /**
17906      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17907      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17908      * @param {String} value The data value of the item to select
17909      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17910      * selected item if it is not currently in view (defaults to true)
17911      * @return {Boolean} True if the value matched an item in the list, else false
17912      */
17913     selectByValue : function(v, scrollIntoView){
17914         if(v !== undefined && v !== null){
17915             var r = this.findRecord(this.valueField || this.displayField, v);
17916             if(r){
17917                 this.select(this.store.indexOf(r), scrollIntoView);
17918                 return true;
17919             }
17920         }
17921         return false;
17922     },
17923
17924     /**
17925      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17926      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17927      * @param {Number} index The zero-based index of the list item to select
17928      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17929      * selected item if it is not currently in view (defaults to true)
17930      */
17931     select : function(index, scrollIntoView){
17932         this.selectedIndex = index;
17933         this.view.select(index);
17934         if(scrollIntoView !== false){
17935             var el = this.view.getNode(index);
17936             /*
17937              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17938              */
17939             if(el){
17940                 this.list.scrollChildIntoView(el, false);
17941             }
17942         }
17943     },
17944
17945     // private
17946     selectNext : function(){
17947         var ct = this.store.getCount();
17948         if(ct > 0){
17949             if(this.selectedIndex == -1){
17950                 this.select(0);
17951             }else if(this.selectedIndex < ct-1){
17952                 this.select(this.selectedIndex+1);
17953             }
17954         }
17955     },
17956
17957     // private
17958     selectPrev : function(){
17959         var ct = this.store.getCount();
17960         if(ct > 0){
17961             if(this.selectedIndex == -1){
17962                 this.select(0);
17963             }else if(this.selectedIndex != 0){
17964                 this.select(this.selectedIndex-1);
17965             }
17966         }
17967     },
17968
17969     // private
17970     onKeyUp : function(e){
17971         if(this.editable !== false && !e.isSpecialKey()){
17972             this.lastKey = e.getKey();
17973             this.dqTask.delay(this.queryDelay);
17974         }
17975     },
17976
17977     // private
17978     validateBlur : function(){
17979         return !this.list || !this.list.isVisible();   
17980     },
17981
17982     // private
17983     initQuery : function(){
17984         
17985         var v = this.getRawValue();
17986         
17987         if(this.tickable && this.editable){
17988             v = this.tickableInputEl().getValue();
17989         }
17990         
17991         this.doQuery(v);
17992     },
17993
17994     // private
17995     doForce : function(){
17996         if(this.inputEl().dom.value.length > 0){
17997             this.inputEl().dom.value =
17998                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17999              
18000         }
18001     },
18002
18003     /**
18004      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18005      * query allowing the query action to be canceled if needed.
18006      * @param {String} query The SQL query to execute
18007      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18008      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18009      * saved in the current store (defaults to false)
18010      */
18011     doQuery : function(q, forceAll){
18012         
18013         if(q === undefined || q === null){
18014             q = '';
18015         }
18016         var qe = {
18017             query: q,
18018             forceAll: forceAll,
18019             combo: this,
18020             cancel:false
18021         };
18022         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18023             return false;
18024         }
18025         q = qe.query;
18026         
18027         forceAll = qe.forceAll;
18028         if(forceAll === true || (q.length >= this.minChars)){
18029             
18030             this.hasQuery = true;
18031             
18032             if(this.lastQuery != q || this.alwaysQuery){
18033                 this.lastQuery = q;
18034                 if(this.mode == 'local'){
18035                     this.selectedIndex = -1;
18036                     if(forceAll){
18037                         this.store.clearFilter();
18038                     }else{
18039                         
18040                         if(this.specialFilter){
18041                             this.fireEvent('specialfilter', this);
18042                             this.onLoad();
18043                             return;
18044                         }
18045                         
18046                         this.store.filter(this.displayField, q);
18047                     }
18048                     
18049                     this.store.fireEvent("datachanged", this.store);
18050                     
18051                     this.onLoad();
18052                     
18053                     
18054                 }else{
18055                     
18056                     this.store.baseParams[this.queryParam] = q;
18057                     
18058                     var options = {params : this.getParams(q)};
18059                     
18060                     if(this.loadNext){
18061                         options.add = true;
18062                         options.params.start = this.page * this.pageSize;
18063                     }
18064                     
18065                     this.store.load(options);
18066                     
18067                     /*
18068                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18069                      *  we should expand the list on onLoad
18070                      *  so command out it
18071                      */
18072 //                    this.expand();
18073                 }
18074             }else{
18075                 this.selectedIndex = -1;
18076                 this.onLoad();   
18077             }
18078         }
18079         
18080         this.loadNext = false;
18081     },
18082     
18083     // private
18084     getParams : function(q){
18085         var p = {};
18086         //p[this.queryParam] = q;
18087         
18088         if(this.pageSize){
18089             p.start = 0;
18090             p.limit = this.pageSize;
18091         }
18092         return p;
18093     },
18094
18095     /**
18096      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18097      */
18098     collapse : function(){
18099         if(!this.isExpanded()){
18100             return;
18101         }
18102         
18103         this.list.hide();
18104         
18105         this.hasFocus = false;
18106         
18107         if(this.tickable){
18108             this.okBtn.hide();
18109             this.cancelBtn.hide();
18110             this.trigger.show();
18111             
18112             if(this.editable){
18113                 this.tickableInputEl().dom.value = '';
18114                 this.tickableInputEl().blur();
18115             }
18116             
18117         }
18118         
18119         Roo.get(document).un('mousedown', this.collapseIf, this);
18120         Roo.get(document).un('mousewheel', this.collapseIf, this);
18121         if (!this.editable) {
18122             Roo.get(document).un('keydown', this.listKeyPress, this);
18123         }
18124         this.fireEvent('collapse', this);
18125         
18126         this.validate();
18127     },
18128
18129     // private
18130     collapseIf : function(e){
18131         var in_combo  = e.within(this.el);
18132         var in_list =  e.within(this.list);
18133         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18134         
18135         if (in_combo || in_list || is_list) {
18136             //e.stopPropagation();
18137             return;
18138         }
18139         
18140         if(this.tickable){
18141             this.onTickableFooterButtonClick(e, false, false);
18142         }
18143
18144         this.collapse();
18145         
18146     },
18147
18148     /**
18149      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18150      */
18151     expand : function(){
18152        
18153         if(this.isExpanded() || !this.hasFocus){
18154             return;
18155         }
18156         
18157         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18158         this.list.setWidth(lw);
18159         
18160         Roo.log('expand');
18161         
18162         this.list.show();
18163         
18164         this.restrictHeight();
18165         
18166         if(this.tickable){
18167             
18168             this.tickItems = Roo.apply([], this.item);
18169             
18170             this.okBtn.show();
18171             this.cancelBtn.show();
18172             this.trigger.hide();
18173             
18174             if(this.editable){
18175                 this.tickableInputEl().focus();
18176             }
18177             
18178         }
18179         
18180         Roo.get(document).on('mousedown', this.collapseIf, this);
18181         Roo.get(document).on('mousewheel', this.collapseIf, this);
18182         if (!this.editable) {
18183             Roo.get(document).on('keydown', this.listKeyPress, this);
18184         }
18185         
18186         this.fireEvent('expand', this);
18187     },
18188
18189     // private
18190     // Implements the default empty TriggerField.onTriggerClick function
18191     onTriggerClick : function(e)
18192     {
18193         Roo.log('trigger click');
18194         
18195         if(this.disabled || !this.triggerList){
18196             return;
18197         }
18198         
18199         this.page = 0;
18200         this.loadNext = false;
18201         
18202         if(this.isExpanded()){
18203             this.collapse();
18204             if (!this.blockFocus) {
18205                 this.inputEl().focus();
18206             }
18207             
18208         }else {
18209             this.hasFocus = true;
18210             if(this.triggerAction == 'all') {
18211                 this.doQuery(this.allQuery, true);
18212             } else {
18213                 this.doQuery(this.getRawValue());
18214             }
18215             if (!this.blockFocus) {
18216                 this.inputEl().focus();
18217             }
18218         }
18219     },
18220     
18221     onTickableTriggerClick : function(e)
18222     {
18223         if(this.disabled){
18224             return;
18225         }
18226         
18227         this.page = 0;
18228         this.loadNext = false;
18229         this.hasFocus = true;
18230         
18231         if(this.triggerAction == 'all') {
18232             this.doQuery(this.allQuery, true);
18233         } else {
18234             this.doQuery(this.getRawValue());
18235         }
18236     },
18237     
18238     onSearchFieldClick : function(e)
18239     {
18240         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18241             this.onTickableFooterButtonClick(e, false, false);
18242             return;
18243         }
18244         
18245         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18246             return;
18247         }
18248         
18249         this.page = 0;
18250         this.loadNext = false;
18251         this.hasFocus = true;
18252         
18253         if(this.triggerAction == 'all') {
18254             this.doQuery(this.allQuery, true);
18255         } else {
18256             this.doQuery(this.getRawValue());
18257         }
18258     },
18259     
18260     listKeyPress : function(e)
18261     {
18262         //Roo.log('listkeypress');
18263         // scroll to first matching element based on key pres..
18264         if (e.isSpecialKey()) {
18265             return false;
18266         }
18267         var k = String.fromCharCode(e.getKey()).toUpperCase();
18268         //Roo.log(k);
18269         var match  = false;
18270         var csel = this.view.getSelectedNodes();
18271         var cselitem = false;
18272         if (csel.length) {
18273             var ix = this.view.indexOf(csel[0]);
18274             cselitem  = this.store.getAt(ix);
18275             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18276                 cselitem = false;
18277             }
18278             
18279         }
18280         
18281         this.store.each(function(v) { 
18282             if (cselitem) {
18283                 // start at existing selection.
18284                 if (cselitem.id == v.id) {
18285                     cselitem = false;
18286                 }
18287                 return true;
18288             }
18289                 
18290             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18291                 match = this.store.indexOf(v);
18292                 return false;
18293             }
18294             return true;
18295         }, this);
18296         
18297         if (match === false) {
18298             return true; // no more action?
18299         }
18300         // scroll to?
18301         this.view.select(match);
18302         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18303         sn.scrollIntoView(sn.dom.parentNode, false);
18304     },
18305     
18306     onViewScroll : function(e, t){
18307         
18308         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){
18309             return;
18310         }
18311         
18312         this.hasQuery = true;
18313         
18314         this.loading = this.list.select('.loading', true).first();
18315         
18316         if(this.loading === null){
18317             this.list.createChild({
18318                 tag: 'div',
18319                 cls: 'loading roo-select2-more-results roo-select2-active',
18320                 html: 'Loading more results...'
18321             });
18322             
18323             this.loading = this.list.select('.loading', true).first();
18324             
18325             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18326             
18327             this.loading.hide();
18328         }
18329         
18330         this.loading.show();
18331         
18332         var _combo = this;
18333         
18334         this.page++;
18335         this.loadNext = true;
18336         
18337         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18338         
18339         return;
18340     },
18341     
18342     addItem : function(o)
18343     {   
18344         var dv = ''; // display value
18345         
18346         if (this.displayField) {
18347             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18348         } else {
18349             // this is an error condition!!!
18350             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18351         }
18352         
18353         if(!dv.length){
18354             return;
18355         }
18356         
18357         var choice = this.choices.createChild({
18358             tag: 'li',
18359             cls: 'roo-select2-search-choice',
18360             cn: [
18361                 {
18362                     tag: 'div',
18363                     html: dv
18364                 },
18365                 {
18366                     tag: 'a',
18367                     href: '#',
18368                     cls: 'roo-select2-search-choice-close fa fa-times',
18369                     tabindex: '-1'
18370                 }
18371             ]
18372             
18373         }, this.searchField);
18374         
18375         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18376         
18377         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18378         
18379         this.item.push(o);
18380         
18381         this.lastData = o;
18382         
18383         this.syncValue();
18384         
18385         this.inputEl().dom.value = '';
18386         
18387         this.validate();
18388     },
18389     
18390     onRemoveItem : function(e, _self, o)
18391     {
18392         e.preventDefault();
18393         
18394         this.lastItem = Roo.apply([], this.item);
18395         
18396         var index = this.item.indexOf(o.data) * 1;
18397         
18398         if( index < 0){
18399             Roo.log('not this item?!');
18400             return;
18401         }
18402         
18403         this.item.splice(index, 1);
18404         o.item.remove();
18405         
18406         this.syncValue();
18407         
18408         this.fireEvent('remove', this, e);
18409         
18410         this.validate();
18411         
18412     },
18413     
18414     syncValue : function()
18415     {
18416         if(!this.item.length){
18417             this.clearValue();
18418             return;
18419         }
18420             
18421         var value = [];
18422         var _this = this;
18423         Roo.each(this.item, function(i){
18424             if(_this.valueField){
18425                 value.push(i[_this.valueField]);
18426                 return;
18427             }
18428
18429             value.push(i);
18430         });
18431
18432         this.value = value.join(',');
18433
18434         if(this.hiddenField){
18435             this.hiddenField.dom.value = this.value;
18436         }
18437         
18438         this.store.fireEvent("datachanged", this.store);
18439         
18440         this.validate();
18441     },
18442     
18443     clearItem : function()
18444     {
18445         if(!this.multiple){
18446             return;
18447         }
18448         
18449         this.item = [];
18450         
18451         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18452            c.remove();
18453         });
18454         
18455         this.syncValue();
18456         
18457         this.validate();
18458         
18459         if(this.tickable && !Roo.isTouch){
18460             this.view.refresh();
18461         }
18462     },
18463     
18464     inputEl: function ()
18465     {
18466         if(Roo.isIOS && this.useNativeIOS){
18467             return this.el.select('select.roo-ios-select', true).first();
18468         }
18469         
18470         if(Roo.isTouch && this.mobileTouchView){
18471             return this.el.select('input.form-control',true).first();
18472         }
18473         
18474         if(this.tickable){
18475             return this.searchField;
18476         }
18477         
18478         return this.el.select('input.form-control',true).first();
18479     },
18480     
18481     onTickableFooterButtonClick : function(e, btn, el)
18482     {
18483         e.preventDefault();
18484         
18485         this.lastItem = Roo.apply([], this.item);
18486         
18487         if(btn && btn.name == 'cancel'){
18488             this.tickItems = Roo.apply([], this.item);
18489             this.collapse();
18490             return;
18491         }
18492         
18493         this.clearItem();
18494         
18495         var _this = this;
18496         
18497         Roo.each(this.tickItems, function(o){
18498             _this.addItem(o);
18499         });
18500         
18501         this.collapse();
18502         
18503     },
18504     
18505     validate : function()
18506     {
18507         if(this.getVisibilityEl().hasClass('hidden')){
18508             return true;
18509         }
18510         
18511         var v = this.getRawValue();
18512         
18513         if(this.multiple){
18514             v = this.getValue();
18515         }
18516         
18517         if(this.disabled || this.allowBlank || v.length){
18518             this.markValid();
18519             return true;
18520         }
18521         
18522         this.markInvalid();
18523         return false;
18524     },
18525     
18526     tickableInputEl : function()
18527     {
18528         if(!this.tickable || !this.editable){
18529             return this.inputEl();
18530         }
18531         
18532         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18533     },
18534     
18535     
18536     getAutoCreateTouchView : function()
18537     {
18538         var id = Roo.id();
18539         
18540         var cfg = {
18541             cls: 'form-group' //input-group
18542         };
18543         
18544         var input =  {
18545             tag: 'input',
18546             id : id,
18547             type : this.inputType,
18548             cls : 'form-control x-combo-noedit',
18549             autocomplete: 'new-password',
18550             placeholder : this.placeholder || '',
18551             readonly : true
18552         };
18553         
18554         if (this.name) {
18555             input.name = this.name;
18556         }
18557         
18558         if (this.size) {
18559             input.cls += ' input-' + this.size;
18560         }
18561         
18562         if (this.disabled) {
18563             input.disabled = true;
18564         }
18565         
18566         var inputblock = {
18567             cls : 'roo-combobox-wrap',
18568             cn : [
18569                 input
18570             ]
18571         };
18572         
18573         if(this.before){
18574             inputblock.cls += ' input-group';
18575             
18576             inputblock.cn.unshift({
18577                 tag :'span',
18578                 cls : 'input-group-addon input-group-prepend input-group-text',
18579                 html : this.before
18580             });
18581         }
18582         
18583         if(this.removable && !this.multiple){
18584             inputblock.cls += ' roo-removable';
18585             
18586             inputblock.cn.push({
18587                 tag: 'button',
18588                 html : 'x',
18589                 cls : 'roo-combo-removable-btn close'
18590             });
18591         }
18592
18593         if(this.hasFeedback && !this.allowBlank){
18594             
18595             inputblock.cls += ' has-feedback';
18596             
18597             inputblock.cn.push({
18598                 tag: 'span',
18599                 cls: 'glyphicon form-control-feedback'
18600             });
18601             
18602         }
18603         
18604         if (this.after) {
18605             
18606             inputblock.cls += (this.before) ? '' : ' input-group';
18607             
18608             inputblock.cn.push({
18609                 tag :'span',
18610                 cls : 'input-group-addon input-group-append input-group-text',
18611                 html : this.after
18612             });
18613         }
18614
18615         
18616         var ibwrap = inputblock;
18617         
18618         if(this.multiple){
18619             ibwrap = {
18620                 tag: 'ul',
18621                 cls: 'roo-select2-choices',
18622                 cn:[
18623                     {
18624                         tag: 'li',
18625                         cls: 'roo-select2-search-field',
18626                         cn: [
18627
18628                             inputblock
18629                         ]
18630                     }
18631                 ]
18632             };
18633         
18634             
18635         }
18636         
18637         var combobox = {
18638             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18639             cn: [
18640                 {
18641                     tag: 'input',
18642                     type : 'hidden',
18643                     cls: 'form-hidden-field'
18644                 },
18645                 ibwrap
18646             ]
18647         };
18648         
18649         if(!this.multiple && this.showToggleBtn){
18650             
18651             var caret = {
18652                 cls: 'caret'
18653             };
18654             
18655             if (this.caret != false) {
18656                 caret = {
18657                      tag: 'i',
18658                      cls: 'fa fa-' + this.caret
18659                 };
18660                 
18661             }
18662             
18663             combobox.cn.push({
18664                 tag :'span',
18665                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18666                 cn : [
18667                     Roo.bootstrap.version == 3 ? caret : '',
18668                     {
18669                         tag: 'span',
18670                         cls: 'combobox-clear',
18671                         cn  : [
18672                             {
18673                                 tag : 'i',
18674                                 cls: 'icon-remove'
18675                             }
18676                         ]
18677                     }
18678                 ]
18679
18680             })
18681         }
18682         
18683         if(this.multiple){
18684             combobox.cls += ' roo-select2-container-multi';
18685         }
18686         
18687         var required =  this.allowBlank ?  {
18688                     tag : 'i',
18689                     style: 'display: none'
18690                 } : {
18691                    tag : 'i',
18692                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18693                    tooltip : 'This field is required'
18694                 };
18695         
18696         var align = this.labelAlign || this.parentLabelAlign();
18697         
18698         if (align ==='left' && this.fieldLabel.length) {
18699
18700             cfg.cn = [
18701                 required,
18702                 {
18703                     tag: 'label',
18704                     cls : 'control-label col-form-label',
18705                     html : this.fieldLabel
18706
18707                 },
18708                 {
18709                     cls : 'roo-combobox-wrap ', 
18710                     cn: [
18711                         combobox
18712                     ]
18713                 }
18714             ];
18715             
18716             var labelCfg = cfg.cn[1];
18717             var contentCfg = cfg.cn[2];
18718             
18719
18720             if(this.indicatorpos == 'right'){
18721                 cfg.cn = [
18722                     {
18723                         tag: 'label',
18724                         'for' :  id,
18725                         cls : 'control-label col-form-label',
18726                         cn : [
18727                             {
18728                                 tag : 'span',
18729                                 html : this.fieldLabel
18730                             },
18731                             required
18732                         ]
18733                     },
18734                     {
18735                         cls : "roo-combobox-wrap ",
18736                         cn: [
18737                             combobox
18738                         ]
18739                     }
18740
18741                 ];
18742                 
18743                 labelCfg = cfg.cn[0];
18744                 contentCfg = cfg.cn[1];
18745             }
18746             
18747            
18748             
18749             if(this.labelWidth > 12){
18750                 labelCfg.style = "width: " + this.labelWidth + 'px';
18751             }
18752            
18753             if(this.labelWidth < 13 && this.labelmd == 0){
18754                 this.labelmd = this.labelWidth;
18755             }
18756             
18757             if(this.labellg > 0){
18758                 labelCfg.cls += ' col-lg-' + this.labellg;
18759                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18760             }
18761             
18762             if(this.labelmd > 0){
18763                 labelCfg.cls += ' col-md-' + this.labelmd;
18764                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18765             }
18766             
18767             if(this.labelsm > 0){
18768                 labelCfg.cls += ' col-sm-' + this.labelsm;
18769                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18770             }
18771             
18772             if(this.labelxs > 0){
18773                 labelCfg.cls += ' col-xs-' + this.labelxs;
18774                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18775             }
18776                 
18777                 
18778         } else if ( this.fieldLabel.length) {
18779             cfg.cn = [
18780                required,
18781                 {
18782                     tag: 'label',
18783                     cls : 'control-label',
18784                     html : this.fieldLabel
18785
18786                 },
18787                 {
18788                     cls : '', 
18789                     cn: [
18790                         combobox
18791                     ]
18792                 }
18793             ];
18794             
18795             if(this.indicatorpos == 'right'){
18796                 cfg.cn = [
18797                     {
18798                         tag: 'label',
18799                         cls : 'control-label',
18800                         html : this.fieldLabel,
18801                         cn : [
18802                             required
18803                         ]
18804                     },
18805                     {
18806                         cls : '', 
18807                         cn: [
18808                             combobox
18809                         ]
18810                     }
18811                 ];
18812             }
18813         } else {
18814             cfg.cn = combobox;    
18815         }
18816         
18817         
18818         var settings = this;
18819         
18820         ['xs','sm','md','lg'].map(function(size){
18821             if (settings[size]) {
18822                 cfg.cls += ' col-' + size + '-' + settings[size];
18823             }
18824         });
18825         
18826         return cfg;
18827     },
18828     
18829     initTouchView : function()
18830     {
18831         this.renderTouchView();
18832         
18833         this.touchViewEl.on('scroll', function(){
18834             this.el.dom.scrollTop = 0;
18835         }, this);
18836         
18837         this.originalValue = this.getValue();
18838         
18839         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18840         
18841         this.inputEl().on("click", this.showTouchView, this);
18842         if (this.triggerEl) {
18843             this.triggerEl.on("click", this.showTouchView, this);
18844         }
18845         
18846         
18847         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18848         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18849         
18850         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18851         
18852         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18853         this.store.on('load', this.onTouchViewLoad, this);
18854         this.store.on('loadexception', this.onTouchViewLoadException, this);
18855         
18856         if(this.hiddenName){
18857             
18858             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18859             
18860             this.hiddenField.dom.value =
18861                 this.hiddenValue !== undefined ? this.hiddenValue :
18862                 this.value !== undefined ? this.value : '';
18863         
18864             this.el.dom.removeAttribute('name');
18865             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18866         }
18867         
18868         if(this.multiple){
18869             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18870             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18871         }
18872         
18873         if(this.removable && !this.multiple){
18874             var close = this.closeTriggerEl();
18875             if(close){
18876                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18877                 close.on('click', this.removeBtnClick, this, close);
18878             }
18879         }
18880         /*
18881          * fix the bug in Safari iOS8
18882          */
18883         this.inputEl().on("focus", function(e){
18884             document.activeElement.blur();
18885         }, this);
18886         
18887         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18888         
18889         return;
18890         
18891         
18892     },
18893     
18894     renderTouchView : function()
18895     {
18896         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18897         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898         
18899         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18900         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901         
18902         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18903         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904         this.touchViewBodyEl.setStyle('overflow', 'auto');
18905         
18906         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18907         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18908         
18909         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18910         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18911         
18912     },
18913     
18914     showTouchView : function()
18915     {
18916         if(this.disabled){
18917             return;
18918         }
18919         
18920         this.touchViewHeaderEl.hide();
18921
18922         if(this.modalTitle.length){
18923             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18924             this.touchViewHeaderEl.show();
18925         }
18926
18927         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18928         this.touchViewEl.show();
18929
18930         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18931         
18932         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18933         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18934
18935         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18936
18937         if(this.modalTitle.length){
18938             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18939         }
18940         
18941         this.touchViewBodyEl.setHeight(bodyHeight);
18942
18943         if(this.animate){
18944             var _this = this;
18945             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18946         }else{
18947             this.touchViewEl.addClass(['in','show']);
18948         }
18949         
18950         if(this._touchViewMask){
18951             Roo.get(document.body).addClass("x-body-masked");
18952             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18953             this._touchViewMask.setStyle('z-index', 10000);
18954             this._touchViewMask.addClass('show');
18955         }
18956         
18957         this.doTouchViewQuery();
18958         
18959     },
18960     
18961     hideTouchView : function()
18962     {
18963         this.touchViewEl.removeClass(['in','show']);
18964
18965         if(this.animate){
18966             var _this = this;
18967             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18968         }else{
18969             this.touchViewEl.setStyle('display', 'none');
18970         }
18971         
18972         if(this._touchViewMask){
18973             this._touchViewMask.removeClass('show');
18974             Roo.get(document.body).removeClass("x-body-masked");
18975         }
18976     },
18977     
18978     setTouchViewValue : function()
18979     {
18980         if(this.multiple){
18981             this.clearItem();
18982         
18983             var _this = this;
18984
18985             Roo.each(this.tickItems, function(o){
18986                 this.addItem(o);
18987             }, this);
18988         }
18989         
18990         this.hideTouchView();
18991     },
18992     
18993     doTouchViewQuery : function()
18994     {
18995         var qe = {
18996             query: '',
18997             forceAll: true,
18998             combo: this,
18999             cancel:false
19000         };
19001         
19002         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19003             return false;
19004         }
19005         
19006         if(!this.alwaysQuery || this.mode == 'local'){
19007             this.onTouchViewLoad();
19008             return;
19009         }
19010         
19011         this.store.load();
19012     },
19013     
19014     onTouchViewBeforeLoad : function(combo,opts)
19015     {
19016         return;
19017     },
19018
19019     // private
19020     onTouchViewLoad : function()
19021     {
19022         if(this.store.getCount() < 1){
19023             this.onTouchViewEmptyResults();
19024             return;
19025         }
19026         
19027         this.clearTouchView();
19028         
19029         var rawValue = this.getRawValue();
19030         
19031         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19032         
19033         this.tickItems = [];
19034         
19035         this.store.data.each(function(d, rowIndex){
19036             var row = this.touchViewListGroup.createChild(template);
19037             
19038             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19039                 row.addClass(d.data.cls);
19040             }
19041             
19042             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19043                 var cfg = {
19044                     data : d.data,
19045                     html : d.data[this.displayField]
19046                 };
19047                 
19048                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19049                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19050                 }
19051             }
19052             row.removeClass('selected');
19053             if(!this.multiple && this.valueField &&
19054                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19055             {
19056                 // radio buttons..
19057                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19058                 row.addClass('selected');
19059             }
19060             
19061             if(this.multiple && this.valueField &&
19062                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19063             {
19064                 
19065                 // checkboxes...
19066                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19067                 this.tickItems.push(d.data);
19068             }
19069             
19070             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19071             
19072         }, this);
19073         
19074         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19075         
19076         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19077
19078         if(this.modalTitle.length){
19079             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19080         }
19081
19082         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19083         
19084         if(this.mobile_restrict_height && listHeight < bodyHeight){
19085             this.touchViewBodyEl.setHeight(listHeight);
19086         }
19087         
19088         var _this = this;
19089         
19090         if(firstChecked && listHeight > bodyHeight){
19091             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19092         }
19093         
19094     },
19095     
19096     onTouchViewLoadException : function()
19097     {
19098         this.hideTouchView();
19099     },
19100     
19101     onTouchViewEmptyResults : function()
19102     {
19103         this.clearTouchView();
19104         
19105         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19106         
19107         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19108         
19109     },
19110     
19111     clearTouchView : function()
19112     {
19113         this.touchViewListGroup.dom.innerHTML = '';
19114     },
19115     
19116     onTouchViewClick : function(e, el, o)
19117     {
19118         e.preventDefault();
19119         
19120         var row = o.row;
19121         var rowIndex = o.rowIndex;
19122         
19123         var r = this.store.getAt(rowIndex);
19124         
19125         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19126             
19127             if(!this.multiple){
19128                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19129                     c.dom.removeAttribute('checked');
19130                 }, this);
19131
19132                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19133
19134                 this.setFromData(r.data);
19135
19136                 var close = this.closeTriggerEl();
19137
19138                 if(close){
19139                     close.show();
19140                 }
19141
19142                 this.hideTouchView();
19143
19144                 this.fireEvent('select', this, r, rowIndex);
19145
19146                 return;
19147             }
19148
19149             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19150                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19151                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19152                 return;
19153             }
19154
19155             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19156             this.addItem(r.data);
19157             this.tickItems.push(r.data);
19158         }
19159     },
19160     
19161     getAutoCreateNativeIOS : function()
19162     {
19163         var cfg = {
19164             cls: 'form-group' //input-group,
19165         };
19166         
19167         var combobox =  {
19168             tag: 'select',
19169             cls : 'roo-ios-select'
19170         };
19171         
19172         if (this.name) {
19173             combobox.name = this.name;
19174         }
19175         
19176         if (this.disabled) {
19177             combobox.disabled = true;
19178         }
19179         
19180         var settings = this;
19181         
19182         ['xs','sm','md','lg'].map(function(size){
19183             if (settings[size]) {
19184                 cfg.cls += ' col-' + size + '-' + settings[size];
19185             }
19186         });
19187         
19188         cfg.cn = combobox;
19189         
19190         return cfg;
19191         
19192     },
19193     
19194     initIOSView : function()
19195     {
19196         this.store.on('load', this.onIOSViewLoad, this);
19197         
19198         return;
19199     },
19200     
19201     onIOSViewLoad : function()
19202     {
19203         if(this.store.getCount() < 1){
19204             return;
19205         }
19206         
19207         this.clearIOSView();
19208         
19209         if(this.allowBlank) {
19210             
19211             var default_text = '-- SELECT --';
19212             
19213             if(this.placeholder.length){
19214                 default_text = this.placeholder;
19215             }
19216             
19217             if(this.emptyTitle.length){
19218                 default_text += ' - ' + this.emptyTitle + ' -';
19219             }
19220             
19221             var opt = this.inputEl().createChild({
19222                 tag: 'option',
19223                 value : 0,
19224                 html : default_text
19225             });
19226             
19227             var o = {};
19228             o[this.valueField] = 0;
19229             o[this.displayField] = default_text;
19230             
19231             this.ios_options.push({
19232                 data : o,
19233                 el : opt
19234             });
19235             
19236         }
19237         
19238         this.store.data.each(function(d, rowIndex){
19239             
19240             var html = '';
19241             
19242             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19243                 html = d.data[this.displayField];
19244             }
19245             
19246             var value = '';
19247             
19248             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19249                 value = d.data[this.valueField];
19250             }
19251             
19252             var option = {
19253                 tag: 'option',
19254                 value : value,
19255                 html : html
19256             };
19257             
19258             if(this.value == d.data[this.valueField]){
19259                 option['selected'] = true;
19260             }
19261             
19262             var opt = this.inputEl().createChild(option);
19263             
19264             this.ios_options.push({
19265                 data : d.data,
19266                 el : opt
19267             });
19268             
19269         }, this);
19270         
19271         this.inputEl().on('change', function(){
19272            this.fireEvent('select', this);
19273         }, this);
19274         
19275     },
19276     
19277     clearIOSView: function()
19278     {
19279         this.inputEl().dom.innerHTML = '';
19280         
19281         this.ios_options = [];
19282     },
19283     
19284     setIOSValue: function(v)
19285     {
19286         this.value = v;
19287         
19288         if(!this.ios_options){
19289             return;
19290         }
19291         
19292         Roo.each(this.ios_options, function(opts){
19293            
19294            opts.el.dom.removeAttribute('selected');
19295            
19296            if(opts.data[this.valueField] != v){
19297                return;
19298            }
19299            
19300            opts.el.dom.setAttribute('selected', true);
19301            
19302         }, this);
19303     }
19304
19305     /** 
19306     * @cfg {Boolean} grow 
19307     * @hide 
19308     */
19309     /** 
19310     * @cfg {Number} growMin 
19311     * @hide 
19312     */
19313     /** 
19314     * @cfg {Number} growMax 
19315     * @hide 
19316     */
19317     /**
19318      * @hide
19319      * @method autoSize
19320      */
19321 });
19322
19323 Roo.apply(Roo.bootstrap.ComboBox,  {
19324     
19325     header : {
19326         tag: 'div',
19327         cls: 'modal-header',
19328         cn: [
19329             {
19330                 tag: 'h4',
19331                 cls: 'modal-title'
19332             }
19333         ]
19334     },
19335     
19336     body : {
19337         tag: 'div',
19338         cls: 'modal-body',
19339         cn: [
19340             {
19341                 tag: 'ul',
19342                 cls: 'list-group'
19343             }
19344         ]
19345     },
19346     
19347     listItemRadio : {
19348         tag: 'li',
19349         cls: 'list-group-item',
19350         cn: [
19351             {
19352                 tag: 'span',
19353                 cls: 'roo-combobox-list-group-item-value'
19354             },
19355             {
19356                 tag: 'div',
19357                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19358                 cn: [
19359                     {
19360                         tag: 'input',
19361                         type: 'radio'
19362                     },
19363                     {
19364                         tag: 'label'
19365                     }
19366                 ]
19367             }
19368         ]
19369     },
19370     
19371     listItemCheckbox : {
19372         tag: 'li',
19373         cls: 'list-group-item',
19374         cn: [
19375             {
19376                 tag: 'span',
19377                 cls: 'roo-combobox-list-group-item-value'
19378             },
19379             {
19380                 tag: 'div',
19381                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19382                 cn: [
19383                     {
19384                         tag: 'input',
19385                         type: 'checkbox'
19386                     },
19387                     {
19388                         tag: 'label'
19389                     }
19390                 ]
19391             }
19392         ]
19393     },
19394     
19395     emptyResult : {
19396         tag: 'div',
19397         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19398     },
19399     
19400     footer : {
19401         tag: 'div',
19402         cls: 'modal-footer',
19403         cn: [
19404             {
19405                 tag: 'div',
19406                 cls: 'row',
19407                 cn: [
19408                     {
19409                         tag: 'div',
19410                         cls: 'col-xs-6 text-left',
19411                         cn: {
19412                             tag: 'button',
19413                             cls: 'btn btn-danger roo-touch-view-cancel',
19414                             html: 'Cancel'
19415                         }
19416                     },
19417                     {
19418                         tag: 'div',
19419                         cls: 'col-xs-6 text-right',
19420                         cn: {
19421                             tag: 'button',
19422                             cls: 'btn btn-success roo-touch-view-ok',
19423                             html: 'OK'
19424                         }
19425                     }
19426                 ]
19427             }
19428         ]
19429         
19430     }
19431 });
19432
19433 Roo.apply(Roo.bootstrap.ComboBox,  {
19434     
19435     touchViewTemplate : {
19436         tag: 'div',
19437         cls: 'modal fade roo-combobox-touch-view',
19438         cn: [
19439             {
19440                 tag: 'div',
19441                 cls: 'modal-dialog',
19442                 style : 'position:fixed', // we have to fix position....
19443                 cn: [
19444                     {
19445                         tag: 'div',
19446                         cls: 'modal-content',
19447                         cn: [
19448                             Roo.bootstrap.ComboBox.header,
19449                             Roo.bootstrap.ComboBox.body,
19450                             Roo.bootstrap.ComboBox.footer
19451                         ]
19452                     }
19453                 ]
19454             }
19455         ]
19456     }
19457 });/*
19458  * Based on:
19459  * Ext JS Library 1.1.1
19460  * Copyright(c) 2006-2007, Ext JS, LLC.
19461  *
19462  * Originally Released Under LGPL - original licence link has changed is not relivant.
19463  *
19464  * Fork - LGPL
19465  * <script type="text/javascript">
19466  */
19467
19468 /**
19469  * @class Roo.View
19470  * @extends Roo.util.Observable
19471  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19472  * This class also supports single and multi selection modes. <br>
19473  * Create a data model bound view:
19474  <pre><code>
19475  var store = new Roo.data.Store(...);
19476
19477  var view = new Roo.View({
19478     el : "my-element",
19479     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19480  
19481     singleSelect: true,
19482     selectedClass: "ydataview-selected",
19483     store: store
19484  });
19485
19486  // listen for node click?
19487  view.on("click", function(vw, index, node, e){
19488  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19489  });
19490
19491  // load XML data
19492  dataModel.load("foobar.xml");
19493  </code></pre>
19494  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19495  * <br><br>
19496  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19497  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19498  * 
19499  * Note: old style constructor is still suported (container, template, config)
19500  * 
19501  * @constructor
19502  * Create a new View
19503  * @param {Object} config The config object
19504  * 
19505  */
19506 Roo.View = function(config, depreciated_tpl, depreciated_config){
19507     
19508     this.parent = false;
19509     
19510     if (typeof(depreciated_tpl) == 'undefined') {
19511         // new way.. - universal constructor.
19512         Roo.apply(this, config);
19513         this.el  = Roo.get(this.el);
19514     } else {
19515         // old format..
19516         this.el  = Roo.get(config);
19517         this.tpl = depreciated_tpl;
19518         Roo.apply(this, depreciated_config);
19519     }
19520     this.wrapEl  = this.el.wrap().wrap();
19521     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19522     
19523     
19524     if(typeof(this.tpl) == "string"){
19525         this.tpl = new Roo.Template(this.tpl);
19526     } else {
19527         // support xtype ctors..
19528         this.tpl = new Roo.factory(this.tpl, Roo);
19529     }
19530     
19531     
19532     this.tpl.compile();
19533     
19534     /** @private */
19535     this.addEvents({
19536         /**
19537          * @event beforeclick
19538          * Fires before a click is processed. Returns false to cancel the default action.
19539          * @param {Roo.View} this
19540          * @param {Number} index The index of the target node
19541          * @param {HTMLElement} node The target node
19542          * @param {Roo.EventObject} e The raw event object
19543          */
19544             "beforeclick" : true,
19545         /**
19546          * @event click
19547          * Fires when a template node is clicked.
19548          * @param {Roo.View} this
19549          * @param {Number} index The index of the target node
19550          * @param {HTMLElement} node The target node
19551          * @param {Roo.EventObject} e The raw event object
19552          */
19553             "click" : true,
19554         /**
19555          * @event dblclick
19556          * Fires when a template node is double clicked.
19557          * @param {Roo.View} this
19558          * @param {Number} index The index of the target node
19559          * @param {HTMLElement} node The target node
19560          * @param {Roo.EventObject} e The raw event object
19561          */
19562             "dblclick" : true,
19563         /**
19564          * @event contextmenu
19565          * Fires when a template node is right clicked.
19566          * @param {Roo.View} this
19567          * @param {Number} index The index of the target node
19568          * @param {HTMLElement} node The target node
19569          * @param {Roo.EventObject} e The raw event object
19570          */
19571             "contextmenu" : true,
19572         /**
19573          * @event selectionchange
19574          * Fires when the selected nodes change.
19575          * @param {Roo.View} this
19576          * @param {Array} selections Array of the selected nodes
19577          */
19578             "selectionchange" : true,
19579     
19580         /**
19581          * @event beforeselect
19582          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19583          * @param {Roo.View} this
19584          * @param {HTMLElement} node The node to be selected
19585          * @param {Array} selections Array of currently selected nodes
19586          */
19587             "beforeselect" : true,
19588         /**
19589          * @event preparedata
19590          * Fires on every row to render, to allow you to change the data.
19591          * @param {Roo.View} this
19592          * @param {Object} data to be rendered (change this)
19593          */
19594           "preparedata" : true
19595           
19596           
19597         });
19598
19599
19600
19601     this.el.on({
19602         "click": this.onClick,
19603         "dblclick": this.onDblClick,
19604         "contextmenu": this.onContextMenu,
19605         scope:this
19606     });
19607
19608     this.selections = [];
19609     this.nodes = [];
19610     this.cmp = new Roo.CompositeElementLite([]);
19611     if(this.store){
19612         this.store = Roo.factory(this.store, Roo.data);
19613         this.setStore(this.store, true);
19614     }
19615     
19616     if ( this.footer && this.footer.xtype) {
19617            
19618          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19619         
19620         this.footer.dataSource = this.store;
19621         this.footer.container = fctr;
19622         this.footer = Roo.factory(this.footer, Roo);
19623         fctr.insertFirst(this.el);
19624         
19625         // this is a bit insane - as the paging toolbar seems to detach the el..
19626 //        dom.parentNode.parentNode.parentNode
19627          // they get detached?
19628     }
19629     
19630     
19631     Roo.View.superclass.constructor.call(this);
19632     
19633     
19634 };
19635
19636 Roo.extend(Roo.View, Roo.util.Observable, {
19637     
19638      /**
19639      * @cfg {Roo.data.Store} store Data store to load data from.
19640      */
19641     store : false,
19642     
19643     /**
19644      * @cfg {String|Roo.Element} el The container element.
19645      */
19646     el : '',
19647     
19648     /**
19649      * @cfg {String|Roo.Template} tpl The template used by this View 
19650      */
19651     tpl : false,
19652     /**
19653      * @cfg {String} dataName the named area of the template to use as the data area
19654      *                          Works with domtemplates roo-name="name"
19655      */
19656     dataName: false,
19657     /**
19658      * @cfg {String} selectedClass The css class to add to selected nodes
19659      */
19660     selectedClass : "x-view-selected",
19661      /**
19662      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19663      */
19664     emptyText : "",
19665     
19666     /**
19667      * @cfg {String} text to display on mask (default Loading)
19668      */
19669     mask : false,
19670     /**
19671      * @cfg {Boolean} multiSelect Allow multiple selection
19672      */
19673     multiSelect : false,
19674     /**
19675      * @cfg {Boolean} singleSelect Allow single selection
19676      */
19677     singleSelect:  false,
19678     
19679     /**
19680      * @cfg {Boolean} toggleSelect - selecting 
19681      */
19682     toggleSelect : false,
19683     
19684     /**
19685      * @cfg {Boolean} tickable - selecting 
19686      */
19687     tickable : false,
19688     
19689     /**
19690      * Returns the element this view is bound to.
19691      * @return {Roo.Element}
19692      */
19693     getEl : function(){
19694         return this.wrapEl;
19695     },
19696     
19697     
19698
19699     /**
19700      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19701      */
19702     refresh : function(){
19703         //Roo.log('refresh');
19704         var t = this.tpl;
19705         
19706         // if we are using something like 'domtemplate', then
19707         // the what gets used is:
19708         // t.applySubtemplate(NAME, data, wrapping data..)
19709         // the outer template then get' applied with
19710         //     the store 'extra data'
19711         // and the body get's added to the
19712         //      roo-name="data" node?
19713         //      <span class='roo-tpl-{name}'></span> ?????
19714         
19715         
19716         
19717         this.clearSelections();
19718         this.el.update("");
19719         var html = [];
19720         var records = this.store.getRange();
19721         if(records.length < 1) {
19722             
19723             // is this valid??  = should it render a template??
19724             
19725             this.el.update(this.emptyText);
19726             return;
19727         }
19728         var el = this.el;
19729         if (this.dataName) {
19730             this.el.update(t.apply(this.store.meta)); //????
19731             el = this.el.child('.roo-tpl-' + this.dataName);
19732         }
19733         
19734         for(var i = 0, len = records.length; i < len; i++){
19735             var data = this.prepareData(records[i].data, i, records[i]);
19736             this.fireEvent("preparedata", this, data, i, records[i]);
19737             
19738             var d = Roo.apply({}, data);
19739             
19740             if(this.tickable){
19741                 Roo.apply(d, {'roo-id' : Roo.id()});
19742                 
19743                 var _this = this;
19744             
19745                 Roo.each(this.parent.item, function(item){
19746                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19747                         return;
19748                     }
19749                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19750                 });
19751             }
19752             
19753             html[html.length] = Roo.util.Format.trim(
19754                 this.dataName ?
19755                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19756                     t.apply(d)
19757             );
19758         }
19759         
19760         
19761         
19762         el.update(html.join(""));
19763         this.nodes = el.dom.childNodes;
19764         this.updateIndexes(0);
19765     },
19766     
19767
19768     /**
19769      * Function to override to reformat the data that is sent to
19770      * the template for each node.
19771      * DEPRICATED - use the preparedata event handler.
19772      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19773      * a JSON object for an UpdateManager bound view).
19774      */
19775     prepareData : function(data, index, record)
19776     {
19777         this.fireEvent("preparedata", this, data, index, record);
19778         return data;
19779     },
19780
19781     onUpdate : function(ds, record){
19782         // Roo.log('on update');   
19783         this.clearSelections();
19784         var index = this.store.indexOf(record);
19785         var n = this.nodes[index];
19786         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19787         n.parentNode.removeChild(n);
19788         this.updateIndexes(index, index);
19789     },
19790
19791     
19792     
19793 // --------- FIXME     
19794     onAdd : function(ds, records, index)
19795     {
19796         //Roo.log(['on Add', ds, records, index] );        
19797         this.clearSelections();
19798         if(this.nodes.length == 0){
19799             this.refresh();
19800             return;
19801         }
19802         var n = this.nodes[index];
19803         for(var i = 0, len = records.length; i < len; i++){
19804             var d = this.prepareData(records[i].data, i, records[i]);
19805             if(n){
19806                 this.tpl.insertBefore(n, d);
19807             }else{
19808                 
19809                 this.tpl.append(this.el, d);
19810             }
19811         }
19812         this.updateIndexes(index);
19813     },
19814
19815     onRemove : function(ds, record, index){
19816        // Roo.log('onRemove');
19817         this.clearSelections();
19818         var el = this.dataName  ?
19819             this.el.child('.roo-tpl-' + this.dataName) :
19820             this.el; 
19821         
19822         el.dom.removeChild(this.nodes[index]);
19823         this.updateIndexes(index);
19824     },
19825
19826     /**
19827      * Refresh an individual node.
19828      * @param {Number} index
19829      */
19830     refreshNode : function(index){
19831         this.onUpdate(this.store, this.store.getAt(index));
19832     },
19833
19834     updateIndexes : function(startIndex, endIndex){
19835         var ns = this.nodes;
19836         startIndex = startIndex || 0;
19837         endIndex = endIndex || ns.length - 1;
19838         for(var i = startIndex; i <= endIndex; i++){
19839             ns[i].nodeIndex = i;
19840         }
19841     },
19842
19843     /**
19844      * Changes the data store this view uses and refresh the view.
19845      * @param {Store} store
19846      */
19847     setStore : function(store, initial){
19848         if(!initial && this.store){
19849             this.store.un("datachanged", this.refresh);
19850             this.store.un("add", this.onAdd);
19851             this.store.un("remove", this.onRemove);
19852             this.store.un("update", this.onUpdate);
19853             this.store.un("clear", this.refresh);
19854             this.store.un("beforeload", this.onBeforeLoad);
19855             this.store.un("load", this.onLoad);
19856             this.store.un("loadexception", this.onLoad);
19857         }
19858         if(store){
19859           
19860             store.on("datachanged", this.refresh, this);
19861             store.on("add", this.onAdd, this);
19862             store.on("remove", this.onRemove, this);
19863             store.on("update", this.onUpdate, this);
19864             store.on("clear", this.refresh, this);
19865             store.on("beforeload", this.onBeforeLoad, this);
19866             store.on("load", this.onLoad, this);
19867             store.on("loadexception", this.onLoad, this);
19868         }
19869         
19870         if(store){
19871             this.refresh();
19872         }
19873     },
19874     /**
19875      * onbeforeLoad - masks the loading area.
19876      *
19877      */
19878     onBeforeLoad : function(store,opts)
19879     {
19880          //Roo.log('onBeforeLoad');   
19881         if (!opts.add) {
19882             this.el.update("");
19883         }
19884         this.el.mask(this.mask ? this.mask : "Loading" ); 
19885     },
19886     onLoad : function ()
19887     {
19888         this.el.unmask();
19889     },
19890     
19891
19892     /**
19893      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19894      * @param {HTMLElement} node
19895      * @return {HTMLElement} The template node
19896      */
19897     findItemFromChild : function(node){
19898         var el = this.dataName  ?
19899             this.el.child('.roo-tpl-' + this.dataName,true) :
19900             this.el.dom; 
19901         
19902         if(!node || node.parentNode == el){
19903                     return node;
19904             }
19905             var p = node.parentNode;
19906             while(p && p != el){
19907             if(p.parentNode == el){
19908                 return p;
19909             }
19910             p = p.parentNode;
19911         }
19912             return null;
19913     },
19914
19915     /** @ignore */
19916     onClick : function(e){
19917         var item = this.findItemFromChild(e.getTarget());
19918         if(item){
19919             var index = this.indexOf(item);
19920             if(this.onItemClick(item, index, e) !== false){
19921                 this.fireEvent("click", this, index, item, e);
19922             }
19923         }else{
19924             this.clearSelections();
19925         }
19926     },
19927
19928     /** @ignore */
19929     onContextMenu : function(e){
19930         var item = this.findItemFromChild(e.getTarget());
19931         if(item){
19932             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19933         }
19934     },
19935
19936     /** @ignore */
19937     onDblClick : function(e){
19938         var item = this.findItemFromChild(e.getTarget());
19939         if(item){
19940             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19941         }
19942     },
19943
19944     onItemClick : function(item, index, e)
19945     {
19946         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19947             return false;
19948         }
19949         if (this.toggleSelect) {
19950             var m = this.isSelected(item) ? 'unselect' : 'select';
19951             //Roo.log(m);
19952             var _t = this;
19953             _t[m](item, true, false);
19954             return true;
19955         }
19956         if(this.multiSelect || this.singleSelect){
19957             if(this.multiSelect && e.shiftKey && this.lastSelection){
19958                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19959             }else{
19960                 this.select(item, this.multiSelect && e.ctrlKey);
19961                 this.lastSelection = item;
19962             }
19963             
19964             if(!this.tickable){
19965                 e.preventDefault();
19966             }
19967             
19968         }
19969         return true;
19970     },
19971
19972     /**
19973      * Get the number of selected nodes.
19974      * @return {Number}
19975      */
19976     getSelectionCount : function(){
19977         return this.selections.length;
19978     },
19979
19980     /**
19981      * Get the currently selected nodes.
19982      * @return {Array} An array of HTMLElements
19983      */
19984     getSelectedNodes : function(){
19985         return this.selections;
19986     },
19987
19988     /**
19989      * Get the indexes of the selected nodes.
19990      * @return {Array}
19991      */
19992     getSelectedIndexes : function(){
19993         var indexes = [], s = this.selections;
19994         for(var i = 0, len = s.length; i < len; i++){
19995             indexes.push(s[i].nodeIndex);
19996         }
19997         return indexes;
19998     },
19999
20000     /**
20001      * Clear all selections
20002      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20003      */
20004     clearSelections : function(suppressEvent){
20005         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20006             this.cmp.elements = this.selections;
20007             this.cmp.removeClass(this.selectedClass);
20008             this.selections = [];
20009             if(!suppressEvent){
20010                 this.fireEvent("selectionchange", this, this.selections);
20011             }
20012         }
20013     },
20014
20015     /**
20016      * Returns true if the passed node is selected
20017      * @param {HTMLElement/Number} node The node or node index
20018      * @return {Boolean}
20019      */
20020     isSelected : function(node){
20021         var s = this.selections;
20022         if(s.length < 1){
20023             return false;
20024         }
20025         node = this.getNode(node);
20026         return s.indexOf(node) !== -1;
20027     },
20028
20029     /**
20030      * Selects nodes.
20031      * @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
20032      * @param {Boolean} keepExisting (optional) true to keep existing selections
20033      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20034      */
20035     select : function(nodeInfo, keepExisting, suppressEvent){
20036         if(nodeInfo instanceof Array){
20037             if(!keepExisting){
20038                 this.clearSelections(true);
20039             }
20040             for(var i = 0, len = nodeInfo.length; i < len; i++){
20041                 this.select(nodeInfo[i], true, true);
20042             }
20043             return;
20044         } 
20045         var node = this.getNode(nodeInfo);
20046         if(!node || this.isSelected(node)){
20047             return; // already selected.
20048         }
20049         if(!keepExisting){
20050             this.clearSelections(true);
20051         }
20052         
20053         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20054             Roo.fly(node).addClass(this.selectedClass);
20055             this.selections.push(node);
20056             if(!suppressEvent){
20057                 this.fireEvent("selectionchange", this, this.selections);
20058             }
20059         }
20060         
20061         
20062     },
20063       /**
20064      * Unselects nodes.
20065      * @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
20066      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20067      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20068      */
20069     unselect : function(nodeInfo, keepExisting, suppressEvent)
20070     {
20071         if(nodeInfo instanceof Array){
20072             Roo.each(this.selections, function(s) {
20073                 this.unselect(s, nodeInfo);
20074             }, this);
20075             return;
20076         }
20077         var node = this.getNode(nodeInfo);
20078         if(!node || !this.isSelected(node)){
20079             //Roo.log("not selected");
20080             return; // not selected.
20081         }
20082         // fireevent???
20083         var ns = [];
20084         Roo.each(this.selections, function(s) {
20085             if (s == node ) {
20086                 Roo.fly(node).removeClass(this.selectedClass);
20087
20088                 return;
20089             }
20090             ns.push(s);
20091         },this);
20092         
20093         this.selections= ns;
20094         this.fireEvent("selectionchange", this, this.selections);
20095     },
20096
20097     /**
20098      * Gets a template node.
20099      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20100      * @return {HTMLElement} The node or null if it wasn't found
20101      */
20102     getNode : function(nodeInfo){
20103         if(typeof nodeInfo == "string"){
20104             return document.getElementById(nodeInfo);
20105         }else if(typeof nodeInfo == "number"){
20106             return this.nodes[nodeInfo];
20107         }
20108         return nodeInfo;
20109     },
20110
20111     /**
20112      * Gets a range template nodes.
20113      * @param {Number} startIndex
20114      * @param {Number} endIndex
20115      * @return {Array} An array of nodes
20116      */
20117     getNodes : function(start, end){
20118         var ns = this.nodes;
20119         start = start || 0;
20120         end = typeof end == "undefined" ? ns.length - 1 : end;
20121         var nodes = [];
20122         if(start <= end){
20123             for(var i = start; i <= end; i++){
20124                 nodes.push(ns[i]);
20125             }
20126         } else{
20127             for(var i = start; i >= end; i--){
20128                 nodes.push(ns[i]);
20129             }
20130         }
20131         return nodes;
20132     },
20133
20134     /**
20135      * Finds the index of the passed node
20136      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20137      * @return {Number} The index of the node or -1
20138      */
20139     indexOf : function(node){
20140         node = this.getNode(node);
20141         if(typeof node.nodeIndex == "number"){
20142             return node.nodeIndex;
20143         }
20144         var ns = this.nodes;
20145         for(var i = 0, len = ns.length; i < len; i++){
20146             if(ns[i] == node){
20147                 return i;
20148             }
20149         }
20150         return -1;
20151     }
20152 });
20153 /*
20154  * - LGPL
20155  *
20156  * based on jquery fullcalendar
20157  * 
20158  */
20159
20160 Roo.bootstrap = Roo.bootstrap || {};
20161 /**
20162  * @class Roo.bootstrap.Calendar
20163  * @extends Roo.bootstrap.Component
20164  * Bootstrap Calendar class
20165  * @cfg {Boolean} loadMask (true|false) default false
20166  * @cfg {Object} header generate the user specific header of the calendar, default false
20167
20168  * @constructor
20169  * Create a new Container
20170  * @param {Object} config The config object
20171  */
20172
20173
20174
20175 Roo.bootstrap.Calendar = function(config){
20176     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20177      this.addEvents({
20178         /**
20179              * @event select
20180              * Fires when a date is selected
20181              * @param {DatePicker} this
20182              * @param {Date} date The selected date
20183              */
20184         'select': true,
20185         /**
20186              * @event monthchange
20187              * Fires when the displayed month changes 
20188              * @param {DatePicker} this
20189              * @param {Date} date The selected month
20190              */
20191         'monthchange': true,
20192         /**
20193              * @event evententer
20194              * Fires when mouse over an event
20195              * @param {Calendar} this
20196              * @param {event} Event
20197              */
20198         'evententer': true,
20199         /**
20200              * @event eventleave
20201              * Fires when the mouse leaves an
20202              * @param {Calendar} this
20203              * @param {event}
20204              */
20205         'eventleave': true,
20206         /**
20207              * @event eventclick
20208              * Fires when the mouse click an
20209              * @param {Calendar} this
20210              * @param {event}
20211              */
20212         'eventclick': true
20213         
20214     });
20215
20216 };
20217
20218 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20219     
20220           /**
20221      * @cfg {Roo.data.Store} store
20222      * The data source for the calendar
20223      */
20224         store : false,
20225      /**
20226      * @cfg {Number} startDay
20227      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20228      */
20229     startDay : 0,
20230     
20231     loadMask : false,
20232     
20233     header : false,
20234       
20235     getAutoCreate : function(){
20236         
20237         
20238         var fc_button = function(name, corner, style, content ) {
20239             return Roo.apply({},{
20240                 tag : 'span',
20241                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20242                          (corner.length ?
20243                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20244                             ''
20245                         ),
20246                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20247                 unselectable: 'on'
20248             });
20249         };
20250         
20251         var header = {};
20252         
20253         if(!this.header){
20254             header = {
20255                 tag : 'table',
20256                 cls : 'fc-header',
20257                 style : 'width:100%',
20258                 cn : [
20259                     {
20260                         tag: 'tr',
20261                         cn : [
20262                             {
20263                                 tag : 'td',
20264                                 cls : 'fc-header-left',
20265                                 cn : [
20266                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20267                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20268                                     { tag: 'span', cls: 'fc-header-space' },
20269                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20270
20271
20272                                 ]
20273                             },
20274
20275                             {
20276                                 tag : 'td',
20277                                 cls : 'fc-header-center',
20278                                 cn : [
20279                                     {
20280                                         tag: 'span',
20281                                         cls: 'fc-header-title',
20282                                         cn : {
20283                                             tag: 'H2',
20284                                             html : 'month / year'
20285                                         }
20286                                     }
20287
20288                                 ]
20289                             },
20290                             {
20291                                 tag : 'td',
20292                                 cls : 'fc-header-right',
20293                                 cn : [
20294                               /*      fc_button('month', 'left', '', 'month' ),
20295                                     fc_button('week', '', '', 'week' ),
20296                                     fc_button('day', 'right', '', 'day' )
20297                                 */    
20298
20299                                 ]
20300                             }
20301
20302                         ]
20303                     }
20304                 ]
20305             };
20306         }
20307         
20308         header = this.header;
20309         
20310        
20311         var cal_heads = function() {
20312             var ret = [];
20313             // fixme - handle this.
20314             
20315             for (var i =0; i < Date.dayNames.length; i++) {
20316                 var d = Date.dayNames[i];
20317                 ret.push({
20318                     tag: 'th',
20319                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20320                     html : d.substring(0,3)
20321                 });
20322                 
20323             }
20324             ret[0].cls += ' fc-first';
20325             ret[6].cls += ' fc-last';
20326             return ret;
20327         };
20328         var cal_cell = function(n) {
20329             return  {
20330                 tag: 'td',
20331                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20332                 cn : [
20333                     {
20334                         cn : [
20335                             {
20336                                 cls: 'fc-day-number',
20337                                 html: 'D'
20338                             },
20339                             {
20340                                 cls: 'fc-day-content',
20341                              
20342                                 cn : [
20343                                      {
20344                                         style: 'position: relative;' // height: 17px;
20345                                     }
20346                                 ]
20347                             }
20348                             
20349                             
20350                         ]
20351                     }
20352                 ]
20353                 
20354             }
20355         };
20356         var cal_rows = function() {
20357             
20358             var ret = [];
20359             for (var r = 0; r < 6; r++) {
20360                 var row= {
20361                     tag : 'tr',
20362                     cls : 'fc-week',
20363                     cn : []
20364                 };
20365                 
20366                 for (var i =0; i < Date.dayNames.length; i++) {
20367                     var d = Date.dayNames[i];
20368                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20369
20370                 }
20371                 row.cn[0].cls+=' fc-first';
20372                 row.cn[0].cn[0].style = 'min-height:90px';
20373                 row.cn[6].cls+=' fc-last';
20374                 ret.push(row);
20375                 
20376             }
20377             ret[0].cls += ' fc-first';
20378             ret[4].cls += ' fc-prev-last';
20379             ret[5].cls += ' fc-last';
20380             return ret;
20381             
20382         };
20383         
20384         var cal_table = {
20385             tag: 'table',
20386             cls: 'fc-border-separate',
20387             style : 'width:100%',
20388             cellspacing  : 0,
20389             cn : [
20390                 { 
20391                     tag: 'thead',
20392                     cn : [
20393                         { 
20394                             tag: 'tr',
20395                             cls : 'fc-first fc-last',
20396                             cn : cal_heads()
20397                         }
20398                     ]
20399                 },
20400                 { 
20401                     tag: 'tbody',
20402                     cn : cal_rows()
20403                 }
20404                   
20405             ]
20406         };
20407          
20408          var cfg = {
20409             cls : 'fc fc-ltr',
20410             cn : [
20411                 header,
20412                 {
20413                     cls : 'fc-content',
20414                     style : "position: relative;",
20415                     cn : [
20416                         {
20417                             cls : 'fc-view fc-view-month fc-grid',
20418                             style : 'position: relative',
20419                             unselectable : 'on',
20420                             cn : [
20421                                 {
20422                                     cls : 'fc-event-container',
20423                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20424                                 },
20425                                 cal_table
20426                             ]
20427                         }
20428                     ]
20429     
20430                 }
20431            ] 
20432             
20433         };
20434         
20435          
20436         
20437         return cfg;
20438     },
20439     
20440     
20441     initEvents : function()
20442     {
20443         if(!this.store){
20444             throw "can not find store for calendar";
20445         }
20446         
20447         var mark = {
20448             tag: "div",
20449             cls:"x-dlg-mask",
20450             style: "text-align:center",
20451             cn: [
20452                 {
20453                     tag: "div",
20454                     style: "background-color:white;width:50%;margin:250 auto",
20455                     cn: [
20456                         {
20457                             tag: "img",
20458                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20459                         },
20460                         {
20461                             tag: "span",
20462                             html: "Loading"
20463                         }
20464                         
20465                     ]
20466                 }
20467             ]
20468         };
20469         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20470         
20471         var size = this.el.select('.fc-content', true).first().getSize();
20472         this.maskEl.setSize(size.width, size.height);
20473         this.maskEl.enableDisplayMode("block");
20474         if(!this.loadMask){
20475             this.maskEl.hide();
20476         }
20477         
20478         this.store = Roo.factory(this.store, Roo.data);
20479         this.store.on('load', this.onLoad, this);
20480         this.store.on('beforeload', this.onBeforeLoad, this);
20481         
20482         this.resize();
20483         
20484         this.cells = this.el.select('.fc-day',true);
20485         //Roo.log(this.cells);
20486         this.textNodes = this.el.query('.fc-day-number');
20487         this.cells.addClassOnOver('fc-state-hover');
20488         
20489         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20490         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20491         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20492         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20493         
20494         this.on('monthchange', this.onMonthChange, this);
20495         
20496         this.update(new Date().clearTime());
20497     },
20498     
20499     resize : function() {
20500         var sz  = this.el.getSize();
20501         
20502         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20503         this.el.select('.fc-day-content div',true).setHeight(34);
20504     },
20505     
20506     
20507     // private
20508     showPrevMonth : function(e){
20509         this.update(this.activeDate.add("mo", -1));
20510     },
20511     showToday : function(e){
20512         this.update(new Date().clearTime());
20513     },
20514     // private
20515     showNextMonth : function(e){
20516         this.update(this.activeDate.add("mo", 1));
20517     },
20518
20519     // private
20520     showPrevYear : function(){
20521         this.update(this.activeDate.add("y", -1));
20522     },
20523
20524     // private
20525     showNextYear : function(){
20526         this.update(this.activeDate.add("y", 1));
20527     },
20528
20529     
20530    // private
20531     update : function(date)
20532     {
20533         var vd = this.activeDate;
20534         this.activeDate = date;
20535 //        if(vd && this.el){
20536 //            var t = date.getTime();
20537 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20538 //                Roo.log('using add remove');
20539 //                
20540 //                this.fireEvent('monthchange', this, date);
20541 //                
20542 //                this.cells.removeClass("fc-state-highlight");
20543 //                this.cells.each(function(c){
20544 //                   if(c.dateValue == t){
20545 //                       c.addClass("fc-state-highlight");
20546 //                       setTimeout(function(){
20547 //                            try{c.dom.firstChild.focus();}catch(e){}
20548 //                       }, 50);
20549 //                       return false;
20550 //                   }
20551 //                   return true;
20552 //                });
20553 //                return;
20554 //            }
20555 //        }
20556         
20557         var days = date.getDaysInMonth();
20558         
20559         var firstOfMonth = date.getFirstDateOfMonth();
20560         var startingPos = firstOfMonth.getDay()-this.startDay;
20561         
20562         if(startingPos < this.startDay){
20563             startingPos += 7;
20564         }
20565         
20566         var pm = date.add(Date.MONTH, -1);
20567         var prevStart = pm.getDaysInMonth()-startingPos;
20568 //        
20569         this.cells = this.el.select('.fc-day',true);
20570         this.textNodes = this.el.query('.fc-day-number');
20571         this.cells.addClassOnOver('fc-state-hover');
20572         
20573         var cells = this.cells.elements;
20574         var textEls = this.textNodes;
20575         
20576         Roo.each(cells, function(cell){
20577             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20578         });
20579         
20580         days += startingPos;
20581
20582         // convert everything to numbers so it's fast
20583         var day = 86400000;
20584         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20585         //Roo.log(d);
20586         //Roo.log(pm);
20587         //Roo.log(prevStart);
20588         
20589         var today = new Date().clearTime().getTime();
20590         var sel = date.clearTime().getTime();
20591         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20592         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20593         var ddMatch = this.disabledDatesRE;
20594         var ddText = this.disabledDatesText;
20595         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20596         var ddaysText = this.disabledDaysText;
20597         var format = this.format;
20598         
20599         var setCellClass = function(cal, cell){
20600             cell.row = 0;
20601             cell.events = [];
20602             cell.more = [];
20603             //Roo.log('set Cell Class');
20604             cell.title = "";
20605             var t = d.getTime();
20606             
20607             //Roo.log(d);
20608             
20609             cell.dateValue = t;
20610             if(t == today){
20611                 cell.className += " fc-today";
20612                 cell.className += " fc-state-highlight";
20613                 cell.title = cal.todayText;
20614             }
20615             if(t == sel){
20616                 // disable highlight in other month..
20617                 //cell.className += " fc-state-highlight";
20618                 
20619             }
20620             // disabling
20621             if(t < min) {
20622                 cell.className = " fc-state-disabled";
20623                 cell.title = cal.minText;
20624                 return;
20625             }
20626             if(t > max) {
20627                 cell.className = " fc-state-disabled";
20628                 cell.title = cal.maxText;
20629                 return;
20630             }
20631             if(ddays){
20632                 if(ddays.indexOf(d.getDay()) != -1){
20633                     cell.title = ddaysText;
20634                     cell.className = " fc-state-disabled";
20635                 }
20636             }
20637             if(ddMatch && format){
20638                 var fvalue = d.dateFormat(format);
20639                 if(ddMatch.test(fvalue)){
20640                     cell.title = ddText.replace("%0", fvalue);
20641                     cell.className = " fc-state-disabled";
20642                 }
20643             }
20644             
20645             if (!cell.initialClassName) {
20646                 cell.initialClassName = cell.dom.className;
20647             }
20648             
20649             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20650         };
20651
20652         var i = 0;
20653         
20654         for(; i < startingPos; i++) {
20655             textEls[i].innerHTML = (++prevStart);
20656             d.setDate(d.getDate()+1);
20657             
20658             cells[i].className = "fc-past fc-other-month";
20659             setCellClass(this, cells[i]);
20660         }
20661         
20662         var intDay = 0;
20663         
20664         for(; i < days; i++){
20665             intDay = i - startingPos + 1;
20666             textEls[i].innerHTML = (intDay);
20667             d.setDate(d.getDate()+1);
20668             
20669             cells[i].className = ''; // "x-date-active";
20670             setCellClass(this, cells[i]);
20671         }
20672         var extraDays = 0;
20673         
20674         for(; i < 42; i++) {
20675             textEls[i].innerHTML = (++extraDays);
20676             d.setDate(d.getDate()+1);
20677             
20678             cells[i].className = "fc-future fc-other-month";
20679             setCellClass(this, cells[i]);
20680         }
20681         
20682         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20683         
20684         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20685         
20686         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20687         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20688         
20689         if(totalRows != 6){
20690             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20691             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20692         }
20693         
20694         this.fireEvent('monthchange', this, date);
20695         
20696         
20697         /*
20698         if(!this.internalRender){
20699             var main = this.el.dom.firstChild;
20700             var w = main.offsetWidth;
20701             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20702             Roo.fly(main).setWidth(w);
20703             this.internalRender = true;
20704             // opera does not respect the auto grow header center column
20705             // then, after it gets a width opera refuses to recalculate
20706             // without a second pass
20707             if(Roo.isOpera && !this.secondPass){
20708                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20709                 this.secondPass = true;
20710                 this.update.defer(10, this, [date]);
20711             }
20712         }
20713         */
20714         
20715     },
20716     
20717     findCell : function(dt) {
20718         dt = dt.clearTime().getTime();
20719         var ret = false;
20720         this.cells.each(function(c){
20721             //Roo.log("check " +c.dateValue + '?=' + dt);
20722             if(c.dateValue == dt){
20723                 ret = c;
20724                 return false;
20725             }
20726             return true;
20727         });
20728         
20729         return ret;
20730     },
20731     
20732     findCells : function(ev) {
20733         var s = ev.start.clone().clearTime().getTime();
20734        // Roo.log(s);
20735         var e= ev.end.clone().clearTime().getTime();
20736        // Roo.log(e);
20737         var ret = [];
20738         this.cells.each(function(c){
20739              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20740             
20741             if(c.dateValue > e){
20742                 return ;
20743             }
20744             if(c.dateValue < s){
20745                 return ;
20746             }
20747             ret.push(c);
20748         });
20749         
20750         return ret;    
20751     },
20752     
20753 //    findBestRow: function(cells)
20754 //    {
20755 //        var ret = 0;
20756 //        
20757 //        for (var i =0 ; i < cells.length;i++) {
20758 //            ret  = Math.max(cells[i].rows || 0,ret);
20759 //        }
20760 //        return ret;
20761 //        
20762 //    },
20763     
20764     
20765     addItem : function(ev)
20766     {
20767         // look for vertical location slot in
20768         var cells = this.findCells(ev);
20769         
20770 //        ev.row = this.findBestRow(cells);
20771         
20772         // work out the location.
20773         
20774         var crow = false;
20775         var rows = [];
20776         for(var i =0; i < cells.length; i++) {
20777             
20778             cells[i].row = cells[0].row;
20779             
20780             if(i == 0){
20781                 cells[i].row = cells[i].row + 1;
20782             }
20783             
20784             if (!crow) {
20785                 crow = {
20786                     start : cells[i],
20787                     end :  cells[i]
20788                 };
20789                 continue;
20790             }
20791             if (crow.start.getY() == cells[i].getY()) {
20792                 // on same row.
20793                 crow.end = cells[i];
20794                 continue;
20795             }
20796             // different row.
20797             rows.push(crow);
20798             crow = {
20799                 start: cells[i],
20800                 end : cells[i]
20801             };
20802             
20803         }
20804         
20805         rows.push(crow);
20806         ev.els = [];
20807         ev.rows = rows;
20808         ev.cells = cells;
20809         
20810         cells[0].events.push(ev);
20811         
20812         this.calevents.push(ev);
20813     },
20814     
20815     clearEvents: function() {
20816         
20817         if(!this.calevents){
20818             return;
20819         }
20820         
20821         Roo.each(this.cells.elements, function(c){
20822             c.row = 0;
20823             c.events = [];
20824             c.more = [];
20825         });
20826         
20827         Roo.each(this.calevents, function(e) {
20828             Roo.each(e.els, function(el) {
20829                 el.un('mouseenter' ,this.onEventEnter, this);
20830                 el.un('mouseleave' ,this.onEventLeave, this);
20831                 el.remove();
20832             },this);
20833         },this);
20834         
20835         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20836             e.remove();
20837         });
20838         
20839     },
20840     
20841     renderEvents: function()
20842     {   
20843         var _this = this;
20844         
20845         this.cells.each(function(c) {
20846             
20847             if(c.row < 5){
20848                 return;
20849             }
20850             
20851             var ev = c.events;
20852             
20853             var r = 4;
20854             if(c.row != c.events.length){
20855                 r = 4 - (4 - (c.row - c.events.length));
20856             }
20857             
20858             c.events = ev.slice(0, r);
20859             c.more = ev.slice(r);
20860             
20861             if(c.more.length && c.more.length == 1){
20862                 c.events.push(c.more.pop());
20863             }
20864             
20865             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20866             
20867         });
20868             
20869         this.cells.each(function(c) {
20870             
20871             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20872             
20873             
20874             for (var e = 0; e < c.events.length; e++){
20875                 var ev = c.events[e];
20876                 var rows = ev.rows;
20877                 
20878                 for(var i = 0; i < rows.length; i++) {
20879                 
20880                     // how many rows should it span..
20881
20882                     var  cfg = {
20883                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20884                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20885
20886                         unselectable : "on",
20887                         cn : [
20888                             {
20889                                 cls: 'fc-event-inner',
20890                                 cn : [
20891     //                                {
20892     //                                  tag:'span',
20893     //                                  cls: 'fc-event-time',
20894     //                                  html : cells.length > 1 ? '' : ev.time
20895     //                                },
20896                                     {
20897                                       tag:'span',
20898                                       cls: 'fc-event-title',
20899                                       html : String.format('{0}', ev.title)
20900                                     }
20901
20902
20903                                 ]
20904                             },
20905                             {
20906                                 cls: 'ui-resizable-handle ui-resizable-e',
20907                                 html : '&nbsp;&nbsp;&nbsp'
20908                             }
20909
20910                         ]
20911                     };
20912
20913                     if (i == 0) {
20914                         cfg.cls += ' fc-event-start';
20915                     }
20916                     if ((i+1) == rows.length) {
20917                         cfg.cls += ' fc-event-end';
20918                     }
20919
20920                     var ctr = _this.el.select('.fc-event-container',true).first();
20921                     var cg = ctr.createChild(cfg);
20922
20923                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20924                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20925
20926                     var r = (c.more.length) ? 1 : 0;
20927                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20928                     cg.setWidth(ebox.right - sbox.x -2);
20929
20930                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20931                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20932                     cg.on('click', _this.onEventClick, _this, ev);
20933
20934                     ev.els.push(cg);
20935                     
20936                 }
20937                 
20938             }
20939             
20940             
20941             if(c.more.length){
20942                 var  cfg = {
20943                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20944                     style : 'position: absolute',
20945                     unselectable : "on",
20946                     cn : [
20947                         {
20948                             cls: 'fc-event-inner',
20949                             cn : [
20950                                 {
20951                                   tag:'span',
20952                                   cls: 'fc-event-title',
20953                                   html : 'More'
20954                                 }
20955
20956
20957                             ]
20958                         },
20959                         {
20960                             cls: 'ui-resizable-handle ui-resizable-e',
20961                             html : '&nbsp;&nbsp;&nbsp'
20962                         }
20963
20964                     ]
20965                 };
20966
20967                 var ctr = _this.el.select('.fc-event-container',true).first();
20968                 var cg = ctr.createChild(cfg);
20969
20970                 var sbox = c.select('.fc-day-content',true).first().getBox();
20971                 var ebox = c.select('.fc-day-content',true).first().getBox();
20972                 //Roo.log(cg);
20973                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20974                 cg.setWidth(ebox.right - sbox.x -2);
20975
20976                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20977                 
20978             }
20979             
20980         });
20981         
20982         
20983         
20984     },
20985     
20986     onEventEnter: function (e, el,event,d) {
20987         this.fireEvent('evententer', this, el, event);
20988     },
20989     
20990     onEventLeave: function (e, el,event,d) {
20991         this.fireEvent('eventleave', this, el, event);
20992     },
20993     
20994     onEventClick: function (e, el,event,d) {
20995         this.fireEvent('eventclick', this, el, event);
20996     },
20997     
20998     onMonthChange: function () {
20999         this.store.load();
21000     },
21001     
21002     onMoreEventClick: function(e, el, more)
21003     {
21004         var _this = this;
21005         
21006         this.calpopover.placement = 'right';
21007         this.calpopover.setTitle('More');
21008         
21009         this.calpopover.setContent('');
21010         
21011         var ctr = this.calpopover.el.select('.popover-content', true).first();
21012         
21013         Roo.each(more, function(m){
21014             var cfg = {
21015                 cls : 'fc-event-hori fc-event-draggable',
21016                 html : m.title
21017             };
21018             var cg = ctr.createChild(cfg);
21019             
21020             cg.on('click', _this.onEventClick, _this, m);
21021         });
21022         
21023         this.calpopover.show(el);
21024         
21025         
21026     },
21027     
21028     onLoad: function () 
21029     {   
21030         this.calevents = [];
21031         var cal = this;
21032         
21033         if(this.store.getCount() > 0){
21034             this.store.data.each(function(d){
21035                cal.addItem({
21036                     id : d.data.id,
21037                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21038                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21039                     time : d.data.start_time,
21040                     title : d.data.title,
21041                     description : d.data.description,
21042                     venue : d.data.venue
21043                 });
21044             });
21045         }
21046         
21047         this.renderEvents();
21048         
21049         if(this.calevents.length && this.loadMask){
21050             this.maskEl.hide();
21051         }
21052     },
21053     
21054     onBeforeLoad: function()
21055     {
21056         this.clearEvents();
21057         if(this.loadMask){
21058             this.maskEl.show();
21059         }
21060     }
21061 });
21062
21063  
21064  /*
21065  * - LGPL
21066  *
21067  * element
21068  * 
21069  */
21070
21071 /**
21072  * @class Roo.bootstrap.Popover
21073  * @extends Roo.bootstrap.Component
21074  * @builder-top
21075  * Bootstrap Popover class
21076  * @cfg {String} html contents of the popover   (or false to use children..)
21077  * @cfg {String} title of popover (or false to hide)
21078  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21079  * @cfg {String} trigger click || hover (or false to trigger manually)
21080  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21081  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21082  *      - if false and it has a 'parent' then it will be automatically added to that element
21083  *      - if string - Roo.get  will be called 
21084  * @cfg {Number} delay - delay before showing
21085  
21086  * @constructor
21087  * Create a new Popover
21088  * @param {Object} config The config object
21089  */
21090
21091 Roo.bootstrap.Popover = function(config){
21092     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21093     
21094     this.addEvents({
21095         // raw events
21096          /**
21097          * @event show
21098          * After the popover show
21099          * 
21100          * @param {Roo.bootstrap.Popover} this
21101          */
21102         "show" : true,
21103         /**
21104          * @event hide
21105          * After the popover hide
21106          * 
21107          * @param {Roo.bootstrap.Popover} this
21108          */
21109         "hide" : true
21110     });
21111 };
21112
21113 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21114     
21115     title: false,
21116     html: false,
21117     
21118     placement : 'right',
21119     trigger : 'hover', // hover
21120     modal : false,
21121     delay : 0,
21122     
21123     over: false,
21124     
21125     can_build_overlaid : false,
21126     
21127     maskEl : false, // the mask element
21128     headerEl : false,
21129     contentEl : false,
21130     alignEl : false, // when show is called with an element - this get's stored.
21131     
21132     getChildContainer : function()
21133     {
21134         return this.contentEl;
21135         
21136     },
21137     getPopoverHeader : function()
21138     {
21139         this.title = true; // flag not to hide it..
21140         this.headerEl.addClass('p-0');
21141         return this.headerEl
21142     },
21143     
21144     
21145     getAutoCreate : function(){
21146          
21147         var cfg = {
21148            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21149            style: 'display:block',
21150            cn : [
21151                 {
21152                     cls : 'arrow'
21153                 },
21154                 {
21155                     cls : 'popover-inner ',
21156                     cn : [
21157                         {
21158                             tag: 'h3',
21159                             cls: 'popover-title popover-header',
21160                             html : this.title === false ? '' : this.title
21161                         },
21162                         {
21163                             cls : 'popover-content popover-body '  + (this.cls || ''),
21164                             html : this.html || ''
21165                         }
21166                     ]
21167                     
21168                 }
21169            ]
21170         };
21171         
21172         return cfg;
21173     },
21174     /**
21175      * @param {string} the title
21176      */
21177     setTitle: function(str)
21178     {
21179         this.title = str;
21180         if (this.el) {
21181             this.headerEl.dom.innerHTML = str;
21182         }
21183         
21184     },
21185     /**
21186      * @param {string} the body content
21187      */
21188     setContent: function(str)
21189     {
21190         this.html = str;
21191         if (this.contentEl) {
21192             this.contentEl.dom.innerHTML = str;
21193         }
21194         
21195     },
21196     // as it get's added to the bottom of the page.
21197     onRender : function(ct, position)
21198     {
21199         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21200         
21201         
21202         
21203         if(!this.el){
21204             var cfg = Roo.apply({},  this.getAutoCreate());
21205             cfg.id = Roo.id();
21206             
21207             if (this.cls) {
21208                 cfg.cls += ' ' + this.cls;
21209             }
21210             if (this.style) {
21211                 cfg.style = this.style;
21212             }
21213             //Roo.log("adding to ");
21214             this.el = Roo.get(document.body).createChild(cfg, position);
21215 //            Roo.log(this.el);
21216         }
21217         
21218         this.contentEl = this.el.select('.popover-content',true).first();
21219         this.headerEl =  this.el.select('.popover-title',true).first();
21220         
21221         var nitems = [];
21222         if(typeof(this.items) != 'undefined'){
21223             var items = this.items;
21224             delete this.items;
21225
21226             for(var i =0;i < items.length;i++) {
21227                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21228             }
21229         }
21230
21231         this.items = nitems;
21232         
21233         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21234         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21235         
21236         
21237         
21238         this.initEvents();
21239     },
21240     
21241     resizeMask : function()
21242     {
21243         this.maskEl.setSize(
21244             Roo.lib.Dom.getViewWidth(true),
21245             Roo.lib.Dom.getViewHeight(true)
21246         );
21247     },
21248     
21249     initEvents : function()
21250     {
21251         
21252         if (!this.modal) { 
21253             Roo.bootstrap.Popover.register(this);
21254         }
21255          
21256         this.arrowEl = this.el.select('.arrow',true).first();
21257         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21258         this.el.enableDisplayMode('block');
21259         this.el.hide();
21260  
21261         
21262         if (this.over === false && !this.parent()) {
21263             return; 
21264         }
21265         if (this.triggers === false) {
21266             return;
21267         }
21268          
21269         // support parent
21270         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21271         var triggers = this.trigger ? this.trigger.split(' ') : [];
21272         Roo.each(triggers, function(trigger) {
21273         
21274             if (trigger == 'click') {
21275                 on_el.on('click', this.toggle, this);
21276             } else if (trigger != 'manual') {
21277                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21278                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21279       
21280                 on_el.on(eventIn  ,this.enter, this);
21281                 on_el.on(eventOut, this.leave, this);
21282             }
21283         }, this);
21284     },
21285     
21286     
21287     // private
21288     timeout : null,
21289     hoverState : null,
21290     
21291     toggle : function () {
21292         this.hoverState == 'in' ? this.leave() : this.enter();
21293     },
21294     
21295     enter : function () {
21296         
21297         clearTimeout(this.timeout);
21298     
21299         this.hoverState = 'in';
21300     
21301         if (!this.delay || !this.delay.show) {
21302             this.show();
21303             return;
21304         }
21305         var _t = this;
21306         this.timeout = setTimeout(function () {
21307             if (_t.hoverState == 'in') {
21308                 _t.show();
21309             }
21310         }, this.delay.show)
21311     },
21312     
21313     leave : function() {
21314         clearTimeout(this.timeout);
21315     
21316         this.hoverState = 'out';
21317     
21318         if (!this.delay || !this.delay.hide) {
21319             this.hide();
21320             return;
21321         }
21322         var _t = this;
21323         this.timeout = setTimeout(function () {
21324             if (_t.hoverState == 'out') {
21325                 _t.hide();
21326             }
21327         }, this.delay.hide)
21328     },
21329     /**
21330      * Show the popover
21331      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21332      * @param {string} (left|right|top|bottom) position
21333      */
21334     show : function (on_el, placement)
21335     {
21336         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21337         on_el = on_el || false; // default to false
21338          
21339         if (!on_el) {
21340             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21341                 on_el = this.parent().el;
21342             } else if (this.over) {
21343                 on_el = Roo.get(this.over);
21344             }
21345             
21346         }
21347         
21348         this.alignEl = Roo.get( on_el );
21349
21350         if (!this.el) {
21351             this.render(document.body);
21352         }
21353         
21354         
21355          
21356         
21357         if (this.title === false) {
21358             this.headerEl.hide();
21359         }
21360         
21361        
21362         this.el.show();
21363         this.el.dom.style.display = 'block';
21364          
21365  
21366         if (this.alignEl) {
21367             this.updatePosition(this.placement, true);
21368              
21369         } else {
21370             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21371             var es = this.el.getSize();
21372             var x = Roo.lib.Dom.getViewWidth()/2;
21373             var y = Roo.lib.Dom.getViewHeight()/2;
21374             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21375             
21376         }
21377
21378         
21379         //var arrow = this.el.select('.arrow',true).first();
21380         //arrow.set(align[2], 
21381         
21382         this.el.addClass('in');
21383         
21384          
21385         
21386         this.hoverState = 'in';
21387         
21388         if (this.modal) {
21389             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21390             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21391             this.maskEl.dom.style.display = 'block';
21392             this.maskEl.addClass('show');
21393         }
21394         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21395  
21396         this.fireEvent('show', this);
21397         
21398     },
21399     /**
21400      * fire this manually after loading a grid in the table for example
21401      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21402      * @param {Boolean} try and move it if we cant get right position.
21403      */
21404     updatePosition : function(placement, try_move)
21405     {
21406         // allow for calling with no parameters
21407         placement = placement   ? placement :  this.placement;
21408         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21409         
21410         this.el.removeClass([
21411             'fade','top','bottom', 'left', 'right','in',
21412             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21413         ]);
21414         this.el.addClass(placement + ' bs-popover-' + placement);
21415         
21416         if (!this.alignEl ) {
21417             return false;
21418         }
21419         
21420         switch (placement) {
21421             case 'right':
21422                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21423                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21424                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21425                     //normal display... or moved up/down.
21426                     this.el.setXY(offset);
21427                     var xy = this.alignEl.getAnchorXY('tr', false);
21428                     xy[0]+=2;xy[1]+=5;
21429                     this.arrowEl.setXY(xy);
21430                     return true;
21431                 }
21432                 // continue through...
21433                 return this.updatePosition('left', false);
21434                 
21435             
21436             case 'left':
21437                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21438                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21439                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21440                     //normal display... or moved up/down.
21441                     this.el.setXY(offset);
21442                     var xy = this.alignEl.getAnchorXY('tl', false);
21443                     xy[0]-=10;xy[1]+=5; // << fix me
21444                     this.arrowEl.setXY(xy);
21445                     return true;
21446                 }
21447                 // call self...
21448                 return this.updatePosition('right', false);
21449             
21450             case 'top':
21451                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21452                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21453                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21454                     //normal display... or moved up/down.
21455                     this.el.setXY(offset);
21456                     var xy = this.alignEl.getAnchorXY('t', false);
21457                     xy[1]-=10; // << fix me
21458                     this.arrowEl.setXY(xy);
21459                     return true;
21460                 }
21461                 // fall through
21462                return this.updatePosition('bottom', false);
21463             
21464             case 'bottom':
21465                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21466                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21467                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21468                     //normal display... or moved up/down.
21469                     this.el.setXY(offset);
21470                     var xy = this.alignEl.getAnchorXY('b', false);
21471                      xy[1]+=2; // << fix me
21472                     this.arrowEl.setXY(xy);
21473                     return true;
21474                 }
21475                 // fall through
21476                 return this.updatePosition('top', false);
21477                 
21478             
21479         }
21480         
21481         
21482         return false;
21483     },
21484     
21485     hide : function()
21486     {
21487         this.el.setXY([0,0]);
21488         this.el.removeClass('in');
21489         this.el.hide();
21490         this.hoverState = null;
21491         this.maskEl.hide(); // always..
21492         this.fireEvent('hide', this);
21493     }
21494     
21495 });
21496
21497
21498 Roo.apply(Roo.bootstrap.Popover, {
21499
21500     alignment : {
21501         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21502         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21503         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21504         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21505     },
21506     
21507     zIndex : 20001,
21508
21509     clickHander : false,
21510     
21511     
21512
21513     onMouseDown : function(e)
21514     {
21515         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21516             /// what is nothing is showing..
21517             this.hideAll();
21518         }
21519          
21520     },
21521     
21522     
21523     popups : [],
21524     
21525     register : function(popup)
21526     {
21527         if (!Roo.bootstrap.Popover.clickHandler) {
21528             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21529         }
21530         // hide other popups.
21531         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21532         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21533         this.hideAll(); //<< why?
21534         //this.popups.push(popup);
21535     },
21536     hideAll : function()
21537     {
21538         this.popups.forEach(function(p) {
21539             p.hide();
21540         });
21541     },
21542     onShow : function() {
21543         Roo.bootstrap.Popover.popups.push(this);
21544     },
21545     onHide : function() {
21546         Roo.bootstrap.Popover.popups.remove(this);
21547     } 
21548
21549 });/*
21550  * - LGPL
21551  *
21552  * Card header - holder for the card header elements.
21553  * 
21554  */
21555
21556 /**
21557  * @class Roo.bootstrap.PopoverNav
21558  * @extends Roo.bootstrap.NavGroup
21559  * Bootstrap Popover header navigation class
21560  * @constructor
21561  * Create a new Popover Header Navigation 
21562  * @param {Object} config The config object
21563  */
21564
21565 Roo.bootstrap.PopoverNav = function(config){
21566     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21567 };
21568
21569 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21570     
21571     
21572     container_method : 'getPopoverHeader' 
21573     
21574      
21575     
21576     
21577    
21578 });
21579
21580  
21581
21582  /*
21583  * - LGPL
21584  *
21585  * Progress
21586  * 
21587  */
21588
21589 /**
21590  * @class Roo.bootstrap.Progress
21591  * @extends Roo.bootstrap.Component
21592  * Bootstrap Progress class
21593  * @cfg {Boolean} striped striped of the progress bar
21594  * @cfg {Boolean} active animated of the progress bar
21595  * 
21596  * 
21597  * @constructor
21598  * Create a new Progress
21599  * @param {Object} config The config object
21600  */
21601
21602 Roo.bootstrap.Progress = function(config){
21603     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21604 };
21605
21606 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21607     
21608     striped : false,
21609     active: false,
21610     
21611     getAutoCreate : function(){
21612         var cfg = {
21613             tag: 'div',
21614             cls: 'progress'
21615         };
21616         
21617         
21618         if(this.striped){
21619             cfg.cls += ' progress-striped';
21620         }
21621       
21622         if(this.active){
21623             cfg.cls += ' active';
21624         }
21625         
21626         
21627         return cfg;
21628     }
21629    
21630 });
21631
21632  
21633
21634  /*
21635  * - LGPL
21636  *
21637  * ProgressBar
21638  * 
21639  */
21640
21641 /**
21642  * @class Roo.bootstrap.ProgressBar
21643  * @extends Roo.bootstrap.Component
21644  * Bootstrap ProgressBar class
21645  * @cfg {Number} aria_valuenow aria-value now
21646  * @cfg {Number} aria_valuemin aria-value min
21647  * @cfg {Number} aria_valuemax aria-value max
21648  * @cfg {String} label label for the progress bar
21649  * @cfg {String} panel (success | info | warning | danger )
21650  * @cfg {String} role role of the progress bar
21651  * @cfg {String} sr_only text
21652  * 
21653  * 
21654  * @constructor
21655  * Create a new ProgressBar
21656  * @param {Object} config The config object
21657  */
21658
21659 Roo.bootstrap.ProgressBar = function(config){
21660     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21661 };
21662
21663 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21664     
21665     aria_valuenow : 0,
21666     aria_valuemin : 0,
21667     aria_valuemax : 100,
21668     label : false,
21669     panel : false,
21670     role : false,
21671     sr_only: false,
21672     
21673     getAutoCreate : function()
21674     {
21675         
21676         var cfg = {
21677             tag: 'div',
21678             cls: 'progress-bar',
21679             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21680         };
21681         
21682         if(this.sr_only){
21683             cfg.cn = {
21684                 tag: 'span',
21685                 cls: 'sr-only',
21686                 html: this.sr_only
21687             }
21688         }
21689         
21690         if(this.role){
21691             cfg.role = this.role;
21692         }
21693         
21694         if(this.aria_valuenow){
21695             cfg['aria-valuenow'] = this.aria_valuenow;
21696         }
21697         
21698         if(this.aria_valuemin){
21699             cfg['aria-valuemin'] = this.aria_valuemin;
21700         }
21701         
21702         if(this.aria_valuemax){
21703             cfg['aria-valuemax'] = this.aria_valuemax;
21704         }
21705         
21706         if(this.label && !this.sr_only){
21707             cfg.html = this.label;
21708         }
21709         
21710         if(this.panel){
21711             cfg.cls += ' progress-bar-' + this.panel;
21712         }
21713         
21714         return cfg;
21715     },
21716     
21717     update : function(aria_valuenow)
21718     {
21719         this.aria_valuenow = aria_valuenow;
21720         
21721         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21722     }
21723    
21724 });
21725
21726  
21727
21728  /*
21729  * - LGPL
21730  *
21731  * column
21732  * 
21733  */
21734
21735 /**
21736  * @class Roo.bootstrap.TabGroup
21737  * @extends Roo.bootstrap.Column
21738  * Bootstrap Column class
21739  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21740  * @cfg {Boolean} carousel true to make the group behave like a carousel
21741  * @cfg {Boolean} bullets show bullets for the panels
21742  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21743  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21744  * @cfg {Boolean} showarrow (true|false) show arrow default true
21745  * 
21746  * @constructor
21747  * Create a new TabGroup
21748  * @param {Object} config The config object
21749  */
21750
21751 Roo.bootstrap.TabGroup = function(config){
21752     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21753     if (!this.navId) {
21754         this.navId = Roo.id();
21755     }
21756     this.tabs = [];
21757     Roo.bootstrap.TabGroup.register(this);
21758     
21759 };
21760
21761 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21762     
21763     carousel : false,
21764     transition : false,
21765     bullets : 0,
21766     timer : 0,
21767     autoslide : false,
21768     slideFn : false,
21769     slideOnTouch : false,
21770     showarrow : true,
21771     
21772     getAutoCreate : function()
21773     {
21774         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21775         
21776         cfg.cls += ' tab-content';
21777         
21778         if (this.carousel) {
21779             cfg.cls += ' carousel slide';
21780             
21781             cfg.cn = [{
21782                cls : 'carousel-inner',
21783                cn : []
21784             }];
21785         
21786             if(this.bullets  && !Roo.isTouch){
21787                 
21788                 var bullets = {
21789                     cls : 'carousel-bullets',
21790                     cn : []
21791                 };
21792                
21793                 if(this.bullets_cls){
21794                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21795                 }
21796                 
21797                 bullets.cn.push({
21798                     cls : 'clear'
21799                 });
21800                 
21801                 cfg.cn[0].cn.push(bullets);
21802             }
21803             
21804             if(this.showarrow){
21805                 cfg.cn[0].cn.push({
21806                     tag : 'div',
21807                     class : 'carousel-arrow',
21808                     cn : [
21809                         {
21810                             tag : 'div',
21811                             class : 'carousel-prev',
21812                             cn : [
21813                                 {
21814                                     tag : 'i',
21815                                     class : 'fa fa-chevron-left'
21816                                 }
21817                             ]
21818                         },
21819                         {
21820                             tag : 'div',
21821                             class : 'carousel-next',
21822                             cn : [
21823                                 {
21824                                     tag : 'i',
21825                                     class : 'fa fa-chevron-right'
21826                                 }
21827                             ]
21828                         }
21829                     ]
21830                 });
21831             }
21832             
21833         }
21834         
21835         return cfg;
21836     },
21837     
21838     initEvents:  function()
21839     {
21840 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21841 //            this.el.on("touchstart", this.onTouchStart, this);
21842 //        }
21843         
21844         if(this.autoslide){
21845             var _this = this;
21846             
21847             this.slideFn = window.setInterval(function() {
21848                 _this.showPanelNext();
21849             }, this.timer);
21850         }
21851         
21852         if(this.showarrow){
21853             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21854             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21855         }
21856         
21857         
21858     },
21859     
21860 //    onTouchStart : function(e, el, o)
21861 //    {
21862 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21863 //            return;
21864 //        }
21865 //        
21866 //        this.showPanelNext();
21867 //    },
21868     
21869     
21870     getChildContainer : function()
21871     {
21872         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21873     },
21874     
21875     /**
21876     * register a Navigation item
21877     * @param {Roo.bootstrap.NavItem} the navitem to add
21878     */
21879     register : function(item)
21880     {
21881         this.tabs.push( item);
21882         item.navId = this.navId; // not really needed..
21883         this.addBullet();
21884     
21885     },
21886     
21887     getActivePanel : function()
21888     {
21889         var r = false;
21890         Roo.each(this.tabs, function(t) {
21891             if (t.active) {
21892                 r = t;
21893                 return false;
21894             }
21895             return null;
21896         });
21897         return r;
21898         
21899     },
21900     getPanelByName : function(n)
21901     {
21902         var r = false;
21903         Roo.each(this.tabs, function(t) {
21904             if (t.tabId == n) {
21905                 r = t;
21906                 return false;
21907             }
21908             return null;
21909         });
21910         return r;
21911     },
21912     indexOfPanel : function(p)
21913     {
21914         var r = false;
21915         Roo.each(this.tabs, function(t,i) {
21916             if (t.tabId == p.tabId) {
21917                 r = i;
21918                 return false;
21919             }
21920             return null;
21921         });
21922         return r;
21923     },
21924     /**
21925      * show a specific panel
21926      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21927      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21928      */
21929     showPanel : function (pan)
21930     {
21931         if(this.transition || typeof(pan) == 'undefined'){
21932             Roo.log("waiting for the transitionend");
21933             return false;
21934         }
21935         
21936         if (typeof(pan) == 'number') {
21937             pan = this.tabs[pan];
21938         }
21939         
21940         if (typeof(pan) == 'string') {
21941             pan = this.getPanelByName(pan);
21942         }
21943         
21944         var cur = this.getActivePanel();
21945         
21946         if(!pan || !cur){
21947             Roo.log('pan or acitve pan is undefined');
21948             return false;
21949         }
21950         
21951         if (pan.tabId == this.getActivePanel().tabId) {
21952             return true;
21953         }
21954         
21955         if (false === cur.fireEvent('beforedeactivate')) {
21956             return false;
21957         }
21958         
21959         if(this.bullets > 0 && !Roo.isTouch){
21960             this.setActiveBullet(this.indexOfPanel(pan));
21961         }
21962         
21963         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21964             
21965             //class="carousel-item carousel-item-next carousel-item-left"
21966             
21967             this.transition = true;
21968             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21969             var lr = dir == 'next' ? 'left' : 'right';
21970             pan.el.addClass(dir); // or prev
21971             pan.el.addClass('carousel-item-' + dir); // or prev
21972             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21973             cur.el.addClass(lr); // or right
21974             pan.el.addClass(lr);
21975             cur.el.addClass('carousel-item-' +lr); // or right
21976             pan.el.addClass('carousel-item-' +lr);
21977             
21978             
21979             var _this = this;
21980             cur.el.on('transitionend', function() {
21981                 Roo.log("trans end?");
21982                 
21983                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21984                 pan.setActive(true);
21985                 
21986                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21987                 cur.setActive(false);
21988                 
21989                 _this.transition = false;
21990                 
21991             }, this, { single:  true } );
21992             
21993             return true;
21994         }
21995         
21996         cur.setActive(false);
21997         pan.setActive(true);
21998         
21999         return true;
22000         
22001     },
22002     showPanelNext : function()
22003     {
22004         var i = this.indexOfPanel(this.getActivePanel());
22005         
22006         if (i >= this.tabs.length - 1 && !this.autoslide) {
22007             return;
22008         }
22009         
22010         if (i >= this.tabs.length - 1 && this.autoslide) {
22011             i = -1;
22012         }
22013         
22014         this.showPanel(this.tabs[i+1]);
22015     },
22016     
22017     showPanelPrev : function()
22018     {
22019         var i = this.indexOfPanel(this.getActivePanel());
22020         
22021         if (i  < 1 && !this.autoslide) {
22022             return;
22023         }
22024         
22025         if (i < 1 && this.autoslide) {
22026             i = this.tabs.length;
22027         }
22028         
22029         this.showPanel(this.tabs[i-1]);
22030     },
22031     
22032     
22033     addBullet: function()
22034     {
22035         if(!this.bullets || Roo.isTouch){
22036             return;
22037         }
22038         var ctr = this.el.select('.carousel-bullets',true).first();
22039         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22040         var bullet = ctr.createChild({
22041             cls : 'bullet bullet-' + i
22042         },ctr.dom.lastChild);
22043         
22044         
22045         var _this = this;
22046         
22047         bullet.on('click', (function(e, el, o, ii, t){
22048
22049             e.preventDefault();
22050
22051             this.showPanel(ii);
22052
22053             if(this.autoslide && this.slideFn){
22054                 clearInterval(this.slideFn);
22055                 this.slideFn = window.setInterval(function() {
22056                     _this.showPanelNext();
22057                 }, this.timer);
22058             }
22059
22060         }).createDelegate(this, [i, bullet], true));
22061                 
22062         
22063     },
22064      
22065     setActiveBullet : function(i)
22066     {
22067         if(Roo.isTouch){
22068             return;
22069         }
22070         
22071         Roo.each(this.el.select('.bullet', true).elements, function(el){
22072             el.removeClass('selected');
22073         });
22074
22075         var bullet = this.el.select('.bullet-' + i, true).first();
22076         
22077         if(!bullet){
22078             return;
22079         }
22080         
22081         bullet.addClass('selected');
22082     }
22083     
22084     
22085   
22086 });
22087
22088  
22089
22090  
22091  
22092 Roo.apply(Roo.bootstrap.TabGroup, {
22093     
22094     groups: {},
22095      /**
22096     * register a Navigation Group
22097     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22098     */
22099     register : function(navgrp)
22100     {
22101         this.groups[navgrp.navId] = navgrp;
22102         
22103     },
22104     /**
22105     * fetch a Navigation Group based on the navigation ID
22106     * if one does not exist , it will get created.
22107     * @param {string} the navgroup to add
22108     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22109     */
22110     get: function(navId) {
22111         if (typeof(this.groups[navId]) == 'undefined') {
22112             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22113         }
22114         return this.groups[navId] ;
22115     }
22116     
22117     
22118     
22119 });
22120
22121  /*
22122  * - LGPL
22123  *
22124  * TabPanel
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.TabPanel
22130  * @extends Roo.bootstrap.Component
22131  * @children Roo.bootstrap.Component
22132  * Bootstrap TabPanel class
22133  * @cfg {Boolean} active panel active
22134  * @cfg {String} html panel content
22135  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22136  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22137  * @cfg {String} href click to link..
22138  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22139  * 
22140  * 
22141  * @constructor
22142  * Create a new TabPanel
22143  * @param {Object} config The config object
22144  */
22145
22146 Roo.bootstrap.TabPanel = function(config){
22147     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22148     this.addEvents({
22149         /**
22150              * @event changed
22151              * Fires when the active status changes
22152              * @param {Roo.bootstrap.TabPanel} this
22153              * @param {Boolean} state the new state
22154             
22155          */
22156         'changed': true,
22157         /**
22158              * @event beforedeactivate
22159              * Fires before a tab is de-activated - can be used to do validation on a form.
22160              * @param {Roo.bootstrap.TabPanel} this
22161              * @return {Boolean} false if there is an error
22162             
22163          */
22164         'beforedeactivate': true
22165      });
22166     
22167     this.tabId = this.tabId || Roo.id();
22168   
22169 };
22170
22171 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22172     
22173     active: false,
22174     html: false,
22175     tabId: false,
22176     navId : false,
22177     href : '',
22178     touchSlide : false,
22179     getAutoCreate : function(){
22180         
22181         
22182         var cfg = {
22183             tag: 'div',
22184             // item is needed for carousel - not sure if it has any effect otherwise
22185             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22186             html: this.html || ''
22187         };
22188         
22189         if(this.active){
22190             cfg.cls += ' active';
22191         }
22192         
22193         if(this.tabId){
22194             cfg.tabId = this.tabId;
22195         }
22196         
22197         
22198         
22199         return cfg;
22200     },
22201     
22202     initEvents:  function()
22203     {
22204         var p = this.parent();
22205         
22206         this.navId = this.navId || p.navId;
22207         
22208         if (typeof(this.navId) != 'undefined') {
22209             // not really needed.. but just in case.. parent should be a NavGroup.
22210             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22211             
22212             tg.register(this);
22213             
22214             var i = tg.tabs.length - 1;
22215             
22216             if(this.active && tg.bullets > 0 && i < tg.bullets){
22217                 tg.setActiveBullet(i);
22218             }
22219         }
22220         
22221         this.el.on('click', this.onClick, this);
22222         
22223         if(Roo.isTouch && this.touchSlide){
22224             this.el.on("touchstart", this.onTouchStart, this);
22225             this.el.on("touchmove", this.onTouchMove, this);
22226             this.el.on("touchend", this.onTouchEnd, this);
22227         }
22228         
22229     },
22230     
22231     onRender : function(ct, position)
22232     {
22233         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22234     },
22235     
22236     setActive : function(state)
22237     {
22238         Roo.log("panel - set active " + this.tabId + "=" + state);
22239         
22240         this.active = state;
22241         if (!state) {
22242             this.el.removeClass('active');
22243             
22244         } else  if (!this.el.hasClass('active')) {
22245             this.el.addClass('active');
22246         }
22247         
22248         this.fireEvent('changed', this, state);
22249     },
22250     
22251     onClick : function(e)
22252     {
22253         e.preventDefault();
22254         
22255         if(!this.href.length){
22256             return;
22257         }
22258         
22259         window.location.href = this.href;
22260     },
22261     
22262     startX : 0,
22263     startY : 0,
22264     endX : 0,
22265     endY : 0,
22266     swiping : false,
22267     
22268     onTouchStart : function(e)
22269     {
22270         this.swiping = false;
22271         
22272         this.startX = e.browserEvent.touches[0].clientX;
22273         this.startY = e.browserEvent.touches[0].clientY;
22274     },
22275     
22276     onTouchMove : function(e)
22277     {
22278         this.swiping = true;
22279         
22280         this.endX = e.browserEvent.touches[0].clientX;
22281         this.endY = e.browserEvent.touches[0].clientY;
22282     },
22283     
22284     onTouchEnd : function(e)
22285     {
22286         if(!this.swiping){
22287             this.onClick(e);
22288             return;
22289         }
22290         
22291         var tabGroup = this.parent();
22292         
22293         if(this.endX > this.startX){ // swiping right
22294             tabGroup.showPanelPrev();
22295             return;
22296         }
22297         
22298         if(this.startX > this.endX){ // swiping left
22299             tabGroup.showPanelNext();
22300             return;
22301         }
22302     }
22303     
22304     
22305 });
22306  
22307
22308  
22309
22310  /*
22311  * - LGPL
22312  *
22313  * DateField
22314  * 
22315  */
22316
22317 /**
22318  * @class Roo.bootstrap.DateField
22319  * @extends Roo.bootstrap.Input
22320  * Bootstrap DateField class
22321  * @cfg {Number} weekStart default 0
22322  * @cfg {String} viewMode default empty, (months|years)
22323  * @cfg {String} minViewMode default empty, (months|years)
22324  * @cfg {Number} startDate default -Infinity
22325  * @cfg {Number} endDate default Infinity
22326  * @cfg {Boolean} todayHighlight default false
22327  * @cfg {Boolean} todayBtn default false
22328  * @cfg {Boolean} calendarWeeks default false
22329  * @cfg {Object} daysOfWeekDisabled default empty
22330  * @cfg {Boolean} singleMode default false (true | false)
22331  * 
22332  * @cfg {Boolean} keyboardNavigation default true
22333  * @cfg {String} language default en
22334  * 
22335  * @constructor
22336  * Create a new DateField
22337  * @param {Object} config The config object
22338  */
22339
22340 Roo.bootstrap.DateField = function(config){
22341     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22342      this.addEvents({
22343             /**
22344              * @event show
22345              * Fires when this field show.
22346              * @param {Roo.bootstrap.DateField} this
22347              * @param {Mixed} date The date value
22348              */
22349             show : true,
22350             /**
22351              * @event show
22352              * Fires when this field hide.
22353              * @param {Roo.bootstrap.DateField} this
22354              * @param {Mixed} date The date value
22355              */
22356             hide : true,
22357             /**
22358              * @event select
22359              * Fires when select a date.
22360              * @param {Roo.bootstrap.DateField} this
22361              * @param {Mixed} date The date value
22362              */
22363             select : true,
22364             /**
22365              * @event beforeselect
22366              * Fires when before select a date.
22367              * @param {Roo.bootstrap.DateField} this
22368              * @param {Mixed} date The date value
22369              */
22370             beforeselect : true
22371         });
22372 };
22373
22374 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22375     
22376     /**
22377      * @cfg {String} format
22378      * The default date format string which can be overriden for localization support.  The format must be
22379      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22380      */
22381     format : "m/d/y",
22382     /**
22383      * @cfg {String} altFormats
22384      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22385      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22386      */
22387     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22388     
22389     weekStart : 0,
22390     
22391     viewMode : '',
22392     
22393     minViewMode : '',
22394     
22395     todayHighlight : false,
22396     
22397     todayBtn: false,
22398     
22399     language: 'en',
22400     
22401     keyboardNavigation: true,
22402     
22403     calendarWeeks: false,
22404     
22405     startDate: -Infinity,
22406     
22407     endDate: Infinity,
22408     
22409     daysOfWeekDisabled: [],
22410     
22411     _events: [],
22412     
22413     singleMode : false,
22414     
22415     UTCDate: function()
22416     {
22417         return new Date(Date.UTC.apply(Date, arguments));
22418     },
22419     
22420     UTCToday: function()
22421     {
22422         var today = new Date();
22423         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22424     },
22425     
22426     getDate: function() {
22427             var d = this.getUTCDate();
22428             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22429     },
22430     
22431     getUTCDate: function() {
22432             return this.date;
22433     },
22434     
22435     setDate: function(d) {
22436             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22437     },
22438     
22439     setUTCDate: function(d) {
22440             this.date = d;
22441             this.setValue(this.formatDate(this.date));
22442     },
22443         
22444     onRender: function(ct, position)
22445     {
22446         
22447         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22448         
22449         this.language = this.language || 'en';
22450         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22451         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22452         
22453         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22454         this.format = this.format || 'm/d/y';
22455         this.isInline = false;
22456         this.isInput = true;
22457         this.component = this.el.select('.add-on', true).first() || false;
22458         this.component = (this.component && this.component.length === 0) ? false : this.component;
22459         this.hasInput = this.component && this.inputEl().length;
22460         
22461         if (typeof(this.minViewMode === 'string')) {
22462             switch (this.minViewMode) {
22463                 case 'months':
22464                     this.minViewMode = 1;
22465                     break;
22466                 case 'years':
22467                     this.minViewMode = 2;
22468                     break;
22469                 default:
22470                     this.minViewMode = 0;
22471                     break;
22472             }
22473         }
22474         
22475         if (typeof(this.viewMode === 'string')) {
22476             switch (this.viewMode) {
22477                 case 'months':
22478                     this.viewMode = 1;
22479                     break;
22480                 case 'years':
22481                     this.viewMode = 2;
22482                     break;
22483                 default:
22484                     this.viewMode = 0;
22485                     break;
22486             }
22487         }
22488                 
22489         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22490         
22491 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22492         
22493         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22494         
22495         this.picker().on('mousedown', this.onMousedown, this);
22496         this.picker().on('click', this.onClick, this);
22497         
22498         this.picker().addClass('datepicker-dropdown');
22499         
22500         this.startViewMode = this.viewMode;
22501         
22502         if(this.singleMode){
22503             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22504                 v.setVisibilityMode(Roo.Element.DISPLAY);
22505                 v.hide();
22506             });
22507             
22508             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22509                 v.setStyle('width', '189px');
22510             });
22511         }
22512         
22513         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22514             if(!this.calendarWeeks){
22515                 v.remove();
22516                 return;
22517             }
22518             
22519             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22520             v.attr('colspan', function(i, val){
22521                 return parseInt(val) + 1;
22522             });
22523         });
22524                         
22525         
22526         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22527         
22528         this.setStartDate(this.startDate);
22529         this.setEndDate(this.endDate);
22530         
22531         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22532         
22533         this.fillDow();
22534         this.fillMonths();
22535         this.update();
22536         this.showMode();
22537         
22538         if(this.isInline) {
22539             this.showPopup();
22540         }
22541     },
22542     
22543     picker : function()
22544     {
22545         return this.pickerEl;
22546 //        return this.el.select('.datepicker', true).first();
22547     },
22548     
22549     fillDow: function()
22550     {
22551         var dowCnt = this.weekStart;
22552         
22553         var dow = {
22554             tag: 'tr',
22555             cn: [
22556                 
22557             ]
22558         };
22559         
22560         if(this.calendarWeeks){
22561             dow.cn.push({
22562                 tag: 'th',
22563                 cls: 'cw',
22564                 html: '&nbsp;'
22565             })
22566         }
22567         
22568         while (dowCnt < this.weekStart + 7) {
22569             dow.cn.push({
22570                 tag: 'th',
22571                 cls: 'dow',
22572                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22573             });
22574         }
22575         
22576         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22577     },
22578     
22579     fillMonths: function()
22580     {    
22581         var i = 0;
22582         var months = this.picker().select('>.datepicker-months td', true).first();
22583         
22584         months.dom.innerHTML = '';
22585         
22586         while (i < 12) {
22587             var month = {
22588                 tag: 'span',
22589                 cls: 'month',
22590                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22591             };
22592             
22593             months.createChild(month);
22594         }
22595         
22596     },
22597     
22598     update: function()
22599     {
22600         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;
22601         
22602         if (this.date < this.startDate) {
22603             this.viewDate = new Date(this.startDate);
22604         } else if (this.date > this.endDate) {
22605             this.viewDate = new Date(this.endDate);
22606         } else {
22607             this.viewDate = new Date(this.date);
22608         }
22609         
22610         this.fill();
22611     },
22612     
22613     fill: function() 
22614     {
22615         var d = new Date(this.viewDate),
22616                 year = d.getUTCFullYear(),
22617                 month = d.getUTCMonth(),
22618                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22619                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22620                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22621                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22622                 currentDate = this.date && this.date.valueOf(),
22623                 today = this.UTCToday();
22624         
22625         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22626         
22627 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22628         
22629 //        this.picker.select('>tfoot th.today').
22630 //                                              .text(dates[this.language].today)
22631 //                                              .toggle(this.todayBtn !== false);
22632     
22633         this.updateNavArrows();
22634         this.fillMonths();
22635                                                 
22636         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22637         
22638         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22639          
22640         prevMonth.setUTCDate(day);
22641         
22642         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22643         
22644         var nextMonth = new Date(prevMonth);
22645         
22646         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22647         
22648         nextMonth = nextMonth.valueOf();
22649         
22650         var fillMonths = false;
22651         
22652         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22653         
22654         while(prevMonth.valueOf() <= nextMonth) {
22655             var clsName = '';
22656             
22657             if (prevMonth.getUTCDay() === this.weekStart) {
22658                 if(fillMonths){
22659                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22660                 }
22661                     
22662                 fillMonths = {
22663                     tag: 'tr',
22664                     cn: []
22665                 };
22666                 
22667                 if(this.calendarWeeks){
22668                     // ISO 8601: First week contains first thursday.
22669                     // ISO also states week starts on Monday, but we can be more abstract here.
22670                     var
22671                     // Start of current week: based on weekstart/current date
22672                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22673                     // Thursday of this week
22674                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22675                     // First Thursday of year, year from thursday
22676                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22677                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22678                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22679                     
22680                     fillMonths.cn.push({
22681                         tag: 'td',
22682                         cls: 'cw',
22683                         html: calWeek
22684                     });
22685                 }
22686             }
22687             
22688             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22689                 clsName += ' old';
22690             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22691                 clsName += ' new';
22692             }
22693             if (this.todayHighlight &&
22694                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22695                 prevMonth.getUTCMonth() == today.getMonth() &&
22696                 prevMonth.getUTCDate() == today.getDate()) {
22697                 clsName += ' today';
22698             }
22699             
22700             if (currentDate && prevMonth.valueOf() === currentDate) {
22701                 clsName += ' active';
22702             }
22703             
22704             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22705                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22706                     clsName += ' disabled';
22707             }
22708             
22709             fillMonths.cn.push({
22710                 tag: 'td',
22711                 cls: 'day ' + clsName,
22712                 html: prevMonth.getDate()
22713             });
22714             
22715             prevMonth.setDate(prevMonth.getDate()+1);
22716         }
22717           
22718         var currentYear = this.date && this.date.getUTCFullYear();
22719         var currentMonth = this.date && this.date.getUTCMonth();
22720         
22721         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22722         
22723         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22724             v.removeClass('active');
22725             
22726             if(currentYear === year && k === currentMonth){
22727                 v.addClass('active');
22728             }
22729             
22730             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22731                 v.addClass('disabled');
22732             }
22733             
22734         });
22735         
22736         
22737         year = parseInt(year/10, 10) * 10;
22738         
22739         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22740         
22741         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22742         
22743         year -= 1;
22744         for (var i = -1; i < 11; i++) {
22745             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22746                 tag: 'span',
22747                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22748                 html: year
22749             });
22750             
22751             year += 1;
22752         }
22753     },
22754     
22755     showMode: function(dir) 
22756     {
22757         if (dir) {
22758             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22759         }
22760         
22761         Roo.each(this.picker().select('>div',true).elements, function(v){
22762             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22763             v.hide();
22764         });
22765         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22766     },
22767     
22768     place: function()
22769     {
22770         if(this.isInline) {
22771             return;
22772         }
22773         
22774         this.picker().removeClass(['bottom', 'top']);
22775         
22776         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22777             /*
22778              * place to the top of element!
22779              *
22780              */
22781             
22782             this.picker().addClass('top');
22783             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22784             
22785             return;
22786         }
22787         
22788         this.picker().addClass('bottom');
22789         
22790         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22791     },
22792     
22793     parseDate : function(value)
22794     {
22795         if(!value || value instanceof Date){
22796             return value;
22797         }
22798         var v = Date.parseDate(value, this.format);
22799         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22800             v = Date.parseDate(value, 'Y-m-d');
22801         }
22802         if(!v && this.altFormats){
22803             if(!this.altFormatsArray){
22804                 this.altFormatsArray = this.altFormats.split("|");
22805             }
22806             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22807                 v = Date.parseDate(value, this.altFormatsArray[i]);
22808             }
22809         }
22810         return v;
22811     },
22812     
22813     formatDate : function(date, fmt)
22814     {   
22815         return (!date || !(date instanceof Date)) ?
22816         date : date.dateFormat(fmt || this.format);
22817     },
22818     
22819     onFocus : function()
22820     {
22821         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22822         this.showPopup();
22823     },
22824     
22825     onBlur : function()
22826     {
22827         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22828         
22829         var d = this.inputEl().getValue();
22830         
22831         this.setValue(d);
22832                 
22833         this.hidePopup();
22834     },
22835     
22836     showPopup : function()
22837     {
22838         this.picker().show();
22839         this.update();
22840         this.place();
22841         
22842         this.fireEvent('showpopup', this, this.date);
22843     },
22844     
22845     hidePopup : function()
22846     {
22847         if(this.isInline) {
22848             return;
22849         }
22850         this.picker().hide();
22851         this.viewMode = this.startViewMode;
22852         this.showMode();
22853         
22854         this.fireEvent('hidepopup', this, this.date);
22855         
22856     },
22857     
22858     onMousedown: function(e)
22859     {
22860         e.stopPropagation();
22861         e.preventDefault();
22862     },
22863     
22864     keyup: function(e)
22865     {
22866         Roo.bootstrap.DateField.superclass.keyup.call(this);
22867         this.update();
22868     },
22869
22870     setValue: function(v)
22871     {
22872         if(this.fireEvent('beforeselect', this, v) !== false){
22873             var d = new Date(this.parseDate(v) ).clearTime();
22874         
22875             if(isNaN(d.getTime())){
22876                 this.date = this.viewDate = '';
22877                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22878                 return;
22879             }
22880
22881             v = this.formatDate(d);
22882
22883             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22884
22885             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22886
22887             this.update();
22888
22889             this.fireEvent('select', this, this.date);
22890         }
22891     },
22892     
22893     getValue: function()
22894     {
22895         return this.formatDate(this.date);
22896     },
22897     
22898     fireKey: function(e)
22899     {
22900         if (!this.picker().isVisible()){
22901             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22902                 this.showPopup();
22903             }
22904             return;
22905         }
22906         
22907         var dateChanged = false,
22908         dir, day, month,
22909         newDate, newViewDate;
22910         
22911         switch(e.keyCode){
22912             case 27: // escape
22913                 this.hidePopup();
22914                 e.preventDefault();
22915                 break;
22916             case 37: // left
22917             case 39: // right
22918                 if (!this.keyboardNavigation) {
22919                     break;
22920                 }
22921                 dir = e.keyCode == 37 ? -1 : 1;
22922                 
22923                 if (e.ctrlKey){
22924                     newDate = this.moveYear(this.date, dir);
22925                     newViewDate = this.moveYear(this.viewDate, dir);
22926                 } else if (e.shiftKey){
22927                     newDate = this.moveMonth(this.date, dir);
22928                     newViewDate = this.moveMonth(this.viewDate, dir);
22929                 } else {
22930                     newDate = new Date(this.date);
22931                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22932                     newViewDate = new Date(this.viewDate);
22933                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22934                 }
22935                 if (this.dateWithinRange(newDate)){
22936                     this.date = newDate;
22937                     this.viewDate = newViewDate;
22938                     this.setValue(this.formatDate(this.date));
22939 //                    this.update();
22940                     e.preventDefault();
22941                     dateChanged = true;
22942                 }
22943                 break;
22944             case 38: // up
22945             case 40: // down
22946                 if (!this.keyboardNavigation) {
22947                     break;
22948                 }
22949                 dir = e.keyCode == 38 ? -1 : 1;
22950                 if (e.ctrlKey){
22951                     newDate = this.moveYear(this.date, dir);
22952                     newViewDate = this.moveYear(this.viewDate, dir);
22953                 } else if (e.shiftKey){
22954                     newDate = this.moveMonth(this.date, dir);
22955                     newViewDate = this.moveMonth(this.viewDate, dir);
22956                 } else {
22957                     newDate = new Date(this.date);
22958                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22959                     newViewDate = new Date(this.viewDate);
22960                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22961                 }
22962                 if (this.dateWithinRange(newDate)){
22963                     this.date = newDate;
22964                     this.viewDate = newViewDate;
22965                     this.setValue(this.formatDate(this.date));
22966 //                    this.update();
22967                     e.preventDefault();
22968                     dateChanged = true;
22969                 }
22970                 break;
22971             case 13: // enter
22972                 this.setValue(this.formatDate(this.date));
22973                 this.hidePopup();
22974                 e.preventDefault();
22975                 break;
22976             case 9: // tab
22977                 this.setValue(this.formatDate(this.date));
22978                 this.hidePopup();
22979                 break;
22980             case 16: // shift
22981             case 17: // ctrl
22982             case 18: // alt
22983                 break;
22984             default :
22985                 this.hidePopup();
22986                 
22987         }
22988     },
22989     
22990     
22991     onClick: function(e) 
22992     {
22993         e.stopPropagation();
22994         e.preventDefault();
22995         
22996         var target = e.getTarget();
22997         
22998         if(target.nodeName.toLowerCase() === 'i'){
22999             target = Roo.get(target).dom.parentNode;
23000         }
23001         
23002         var nodeName = target.nodeName;
23003         var className = target.className;
23004         var html = target.innerHTML;
23005         //Roo.log(nodeName);
23006         
23007         switch(nodeName.toLowerCase()) {
23008             case 'th':
23009                 switch(className) {
23010                     case 'switch':
23011                         this.showMode(1);
23012                         break;
23013                     case 'prev':
23014                     case 'next':
23015                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23016                         switch(this.viewMode){
23017                                 case 0:
23018                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23019                                         break;
23020                                 case 1:
23021                                 case 2:
23022                                         this.viewDate = this.moveYear(this.viewDate, dir);
23023                                         break;
23024                         }
23025                         this.fill();
23026                         break;
23027                     case 'today':
23028                         var date = new Date();
23029                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23030 //                        this.fill()
23031                         this.setValue(this.formatDate(this.date));
23032                         
23033                         this.hidePopup();
23034                         break;
23035                 }
23036                 break;
23037             case 'span':
23038                 if (className.indexOf('disabled') < 0) {
23039                 if (!this.viewDate) {
23040                     this.viewDate = new Date();
23041                 }
23042                 this.viewDate.setUTCDate(1);
23043                     if (className.indexOf('month') > -1) {
23044                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23045                     } else {
23046                         var year = parseInt(html, 10) || 0;
23047                         this.viewDate.setUTCFullYear(year);
23048                         
23049                     }
23050                     
23051                     if(this.singleMode){
23052                         this.setValue(this.formatDate(this.viewDate));
23053                         this.hidePopup();
23054                         return;
23055                     }
23056                     
23057                     this.showMode(-1);
23058                     this.fill();
23059                 }
23060                 break;
23061                 
23062             case 'td':
23063                 //Roo.log(className);
23064                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23065                     var day = parseInt(html, 10) || 1;
23066                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23067                         month = (this.viewDate || new Date()).getUTCMonth();
23068
23069                     if (className.indexOf('old') > -1) {
23070                         if(month === 0 ){
23071                             month = 11;
23072                             year -= 1;
23073                         }else{
23074                             month -= 1;
23075                         }
23076                     } else if (className.indexOf('new') > -1) {
23077                         if (month == 11) {
23078                             month = 0;
23079                             year += 1;
23080                         } else {
23081                             month += 1;
23082                         }
23083                     }
23084                     //Roo.log([year,month,day]);
23085                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23086                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23087 //                    this.fill();
23088                     //Roo.log(this.formatDate(this.date));
23089                     this.setValue(this.formatDate(this.date));
23090                     this.hidePopup();
23091                 }
23092                 break;
23093         }
23094     },
23095     
23096     setStartDate: function(startDate)
23097     {
23098         this.startDate = startDate || -Infinity;
23099         if (this.startDate !== -Infinity) {
23100             this.startDate = this.parseDate(this.startDate);
23101         }
23102         this.update();
23103         this.updateNavArrows();
23104     },
23105
23106     setEndDate: function(endDate)
23107     {
23108         this.endDate = endDate || Infinity;
23109         if (this.endDate !== Infinity) {
23110             this.endDate = this.parseDate(this.endDate);
23111         }
23112         this.update();
23113         this.updateNavArrows();
23114     },
23115     
23116     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23117     {
23118         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23119         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23120             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23121         }
23122         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23123             return parseInt(d, 10);
23124         });
23125         this.update();
23126         this.updateNavArrows();
23127     },
23128     
23129     updateNavArrows: function() 
23130     {
23131         if(this.singleMode){
23132             return;
23133         }
23134         
23135         var d = new Date(this.viewDate),
23136         year = d.getUTCFullYear(),
23137         month = d.getUTCMonth();
23138         
23139         Roo.each(this.picker().select('.prev', true).elements, function(v){
23140             v.show();
23141             switch (this.viewMode) {
23142                 case 0:
23143
23144                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23145                         v.hide();
23146                     }
23147                     break;
23148                 case 1:
23149                 case 2:
23150                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23151                         v.hide();
23152                     }
23153                     break;
23154             }
23155         });
23156         
23157         Roo.each(this.picker().select('.next', true).elements, function(v){
23158             v.show();
23159             switch (this.viewMode) {
23160                 case 0:
23161
23162                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23163                         v.hide();
23164                     }
23165                     break;
23166                 case 1:
23167                 case 2:
23168                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23169                         v.hide();
23170                     }
23171                     break;
23172             }
23173         })
23174     },
23175     
23176     moveMonth: function(date, dir)
23177     {
23178         if (!dir) {
23179             return date;
23180         }
23181         var new_date = new Date(date.valueOf()),
23182         day = new_date.getUTCDate(),
23183         month = new_date.getUTCMonth(),
23184         mag = Math.abs(dir),
23185         new_month, test;
23186         dir = dir > 0 ? 1 : -1;
23187         if (mag == 1){
23188             test = dir == -1
23189             // If going back one month, make sure month is not current month
23190             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23191             ? function(){
23192                 return new_date.getUTCMonth() == month;
23193             }
23194             // If going forward one month, make sure month is as expected
23195             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23196             : function(){
23197                 return new_date.getUTCMonth() != new_month;
23198             };
23199             new_month = month + dir;
23200             new_date.setUTCMonth(new_month);
23201             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23202             if (new_month < 0 || new_month > 11) {
23203                 new_month = (new_month + 12) % 12;
23204             }
23205         } else {
23206             // For magnitudes >1, move one month at a time...
23207             for (var i=0; i<mag; i++) {
23208                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23209                 new_date = this.moveMonth(new_date, dir);
23210             }
23211             // ...then reset the day, keeping it in the new month
23212             new_month = new_date.getUTCMonth();
23213             new_date.setUTCDate(day);
23214             test = function(){
23215                 return new_month != new_date.getUTCMonth();
23216             };
23217         }
23218         // Common date-resetting loop -- if date is beyond end of month, make it
23219         // end of month
23220         while (test()){
23221             new_date.setUTCDate(--day);
23222             new_date.setUTCMonth(new_month);
23223         }
23224         return new_date;
23225     },
23226
23227     moveYear: function(date, dir)
23228     {
23229         return this.moveMonth(date, dir*12);
23230     },
23231
23232     dateWithinRange: function(date)
23233     {
23234         return date >= this.startDate && date <= this.endDate;
23235     },
23236
23237     
23238     remove: function() 
23239     {
23240         this.picker().remove();
23241     },
23242     
23243     validateValue : function(value)
23244     {
23245         if(this.getVisibilityEl().hasClass('hidden')){
23246             return true;
23247         }
23248         
23249         if(value.length < 1)  {
23250             if(this.allowBlank){
23251                 return true;
23252             }
23253             return false;
23254         }
23255         
23256         if(value.length < this.minLength){
23257             return false;
23258         }
23259         if(value.length > this.maxLength){
23260             return false;
23261         }
23262         if(this.vtype){
23263             var vt = Roo.form.VTypes;
23264             if(!vt[this.vtype](value, this)){
23265                 return false;
23266             }
23267         }
23268         if(typeof this.validator == "function"){
23269             var msg = this.validator(value);
23270             if(msg !== true){
23271                 return false;
23272             }
23273         }
23274         
23275         if(this.regex && !this.regex.test(value)){
23276             return false;
23277         }
23278         
23279         if(typeof(this.parseDate(value)) == 'undefined'){
23280             return false;
23281         }
23282         
23283         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23284             return false;
23285         }      
23286         
23287         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23288             return false;
23289         } 
23290         
23291         
23292         return true;
23293     },
23294     
23295     reset : function()
23296     {
23297         this.date = this.viewDate = '';
23298         
23299         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23300     }
23301    
23302 });
23303
23304 Roo.apply(Roo.bootstrap.DateField,  {
23305     
23306     head : {
23307         tag: 'thead',
23308         cn: [
23309         {
23310             tag: 'tr',
23311             cn: [
23312             {
23313                 tag: 'th',
23314                 cls: 'prev',
23315                 html: '<i class="fa fa-arrow-left"/>'
23316             },
23317             {
23318                 tag: 'th',
23319                 cls: 'switch',
23320                 colspan: '5'
23321             },
23322             {
23323                 tag: 'th',
23324                 cls: 'next',
23325                 html: '<i class="fa fa-arrow-right"/>'
23326             }
23327
23328             ]
23329         }
23330         ]
23331     },
23332     
23333     content : {
23334         tag: 'tbody',
23335         cn: [
23336         {
23337             tag: 'tr',
23338             cn: [
23339             {
23340                 tag: 'td',
23341                 colspan: '7'
23342             }
23343             ]
23344         }
23345         ]
23346     },
23347     
23348     footer : {
23349         tag: 'tfoot',
23350         cn: [
23351         {
23352             tag: 'tr',
23353             cn: [
23354             {
23355                 tag: 'th',
23356                 colspan: '7',
23357                 cls: 'today'
23358             }
23359                     
23360             ]
23361         }
23362         ]
23363     },
23364     
23365     dates:{
23366         en: {
23367             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23368             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23369             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23370             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23371             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23372             today: "Today"
23373         }
23374     },
23375     
23376     modes: [
23377     {
23378         clsName: 'days',
23379         navFnc: 'Month',
23380         navStep: 1
23381     },
23382     {
23383         clsName: 'months',
23384         navFnc: 'FullYear',
23385         navStep: 1
23386     },
23387     {
23388         clsName: 'years',
23389         navFnc: 'FullYear',
23390         navStep: 10
23391     }]
23392 });
23393
23394 Roo.apply(Roo.bootstrap.DateField,  {
23395   
23396     template : {
23397         tag: 'div',
23398         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23399         cn: [
23400         {
23401             tag: 'div',
23402             cls: 'datepicker-days',
23403             cn: [
23404             {
23405                 tag: 'table',
23406                 cls: 'table-condensed',
23407                 cn:[
23408                 Roo.bootstrap.DateField.head,
23409                 {
23410                     tag: 'tbody'
23411                 },
23412                 Roo.bootstrap.DateField.footer
23413                 ]
23414             }
23415             ]
23416         },
23417         {
23418             tag: 'div',
23419             cls: 'datepicker-months',
23420             cn: [
23421             {
23422                 tag: 'table',
23423                 cls: 'table-condensed',
23424                 cn:[
23425                 Roo.bootstrap.DateField.head,
23426                 Roo.bootstrap.DateField.content,
23427                 Roo.bootstrap.DateField.footer
23428                 ]
23429             }
23430             ]
23431         },
23432         {
23433             tag: 'div',
23434             cls: 'datepicker-years',
23435             cn: [
23436             {
23437                 tag: 'table',
23438                 cls: 'table-condensed',
23439                 cn:[
23440                 Roo.bootstrap.DateField.head,
23441                 Roo.bootstrap.DateField.content,
23442                 Roo.bootstrap.DateField.footer
23443                 ]
23444             }
23445             ]
23446         }
23447         ]
23448     }
23449 });
23450
23451  
23452
23453  /*
23454  * - LGPL
23455  *
23456  * TimeField
23457  * 
23458  */
23459
23460 /**
23461  * @class Roo.bootstrap.TimeField
23462  * @extends Roo.bootstrap.Input
23463  * Bootstrap DateField class
23464  * 
23465  * 
23466  * @constructor
23467  * Create a new TimeField
23468  * @param {Object} config The config object
23469  */
23470
23471 Roo.bootstrap.TimeField = function(config){
23472     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23473     this.addEvents({
23474             /**
23475              * @event show
23476              * Fires when this field show.
23477              * @param {Roo.bootstrap.DateField} thisthis
23478              * @param {Mixed} date The date value
23479              */
23480             show : true,
23481             /**
23482              * @event show
23483              * Fires when this field hide.
23484              * @param {Roo.bootstrap.DateField} this
23485              * @param {Mixed} date The date value
23486              */
23487             hide : true,
23488             /**
23489              * @event select
23490              * Fires when select a date.
23491              * @param {Roo.bootstrap.DateField} this
23492              * @param {Mixed} date The date value
23493              */
23494             select : true
23495         });
23496 };
23497
23498 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23499     
23500     /**
23501      * @cfg {String} format
23502      * The default time format string which can be overriden for localization support.  The format must be
23503      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23504      */
23505     format : "H:i",
23506
23507     getAutoCreate : function()
23508     {
23509         this.after = '<i class="fa far fa-clock"></i>';
23510         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23511         
23512          
23513     },
23514     onRender: function(ct, position)
23515     {
23516         
23517         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23518                 
23519         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23520         
23521         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23522         
23523         this.pop = this.picker().select('>.datepicker-time',true).first();
23524         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23525         
23526         this.picker().on('mousedown', this.onMousedown, this);
23527         this.picker().on('click', this.onClick, this);
23528         
23529         this.picker().addClass('datepicker-dropdown');
23530     
23531         this.fillTime();
23532         this.update();
23533             
23534         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23535         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23536         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23537         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23538         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23539         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23540
23541     },
23542     
23543     fireKey: function(e){
23544         if (!this.picker().isVisible()){
23545             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23546                 this.show();
23547             }
23548             return;
23549         }
23550
23551         e.preventDefault();
23552         
23553         switch(e.keyCode){
23554             case 27: // escape
23555                 this.hide();
23556                 break;
23557             case 37: // left
23558             case 39: // right
23559                 this.onTogglePeriod();
23560                 break;
23561             case 38: // up
23562                 this.onIncrementMinutes();
23563                 break;
23564             case 40: // down
23565                 this.onDecrementMinutes();
23566                 break;
23567             case 13: // enter
23568             case 9: // tab
23569                 this.setTime();
23570                 break;
23571         }
23572     },
23573     
23574     onClick: function(e) {
23575         e.stopPropagation();
23576         e.preventDefault();
23577     },
23578     
23579     picker : function()
23580     {
23581         return this.pickerEl;
23582     },
23583     
23584     fillTime: function()
23585     {    
23586         var time = this.pop.select('tbody', true).first();
23587         
23588         time.dom.innerHTML = '';
23589         
23590         time.createChild({
23591             tag: 'tr',
23592             cn: [
23593                 {
23594                     tag: 'td',
23595                     cn: [
23596                         {
23597                             tag: 'a',
23598                             href: '#',
23599                             cls: 'btn',
23600                             cn: [
23601                                 {
23602                                     tag: 'i',
23603                                     cls: 'hours-up fa fas fa-chevron-up'
23604                                 }
23605                             ]
23606                         } 
23607                     ]
23608                 },
23609                 {
23610                     tag: 'td',
23611                     cls: 'separator'
23612                 },
23613                 {
23614                     tag: 'td',
23615                     cn: [
23616                         {
23617                             tag: 'a',
23618                             href: '#',
23619                             cls: 'btn',
23620                             cn: [
23621                                 {
23622                                     tag: 'i',
23623                                     cls: 'minutes-up fa fas fa-chevron-up'
23624                                 }
23625                             ]
23626                         }
23627                     ]
23628                 },
23629                 {
23630                     tag: 'td',
23631                     cls: 'separator'
23632                 }
23633             ]
23634         });
23635         
23636         time.createChild({
23637             tag: 'tr',
23638             cn: [
23639                 {
23640                     tag: 'td',
23641                     cn: [
23642                         {
23643                             tag: 'span',
23644                             cls: 'timepicker-hour',
23645                             html: '00'
23646                         }  
23647                     ]
23648                 },
23649                 {
23650                     tag: 'td',
23651                     cls: 'separator',
23652                     html: ':'
23653                 },
23654                 {
23655                     tag: 'td',
23656                     cn: [
23657                         {
23658                             tag: 'span',
23659                             cls: 'timepicker-minute',
23660                             html: '00'
23661                         }  
23662                     ]
23663                 },
23664                 {
23665                     tag: 'td',
23666                     cls: 'separator'
23667                 },
23668                 {
23669                     tag: 'td',
23670                     cn: [
23671                         {
23672                             tag: 'button',
23673                             type: 'button',
23674                             cls: 'btn btn-primary period',
23675                             html: 'AM'
23676                             
23677                         }
23678                     ]
23679                 }
23680             ]
23681         });
23682         
23683         time.createChild({
23684             tag: 'tr',
23685             cn: [
23686                 {
23687                     tag: 'td',
23688                     cn: [
23689                         {
23690                             tag: 'a',
23691                             href: '#',
23692                             cls: 'btn',
23693                             cn: [
23694                                 {
23695                                     tag: 'span',
23696                                     cls: 'hours-down fa fas fa-chevron-down'
23697                                 }
23698                             ]
23699                         }
23700                     ]
23701                 },
23702                 {
23703                     tag: 'td',
23704                     cls: 'separator'
23705                 },
23706                 {
23707                     tag: 'td',
23708                     cn: [
23709                         {
23710                             tag: 'a',
23711                             href: '#',
23712                             cls: 'btn',
23713                             cn: [
23714                                 {
23715                                     tag: 'span',
23716                                     cls: 'minutes-down fa fas fa-chevron-down'
23717                                 }
23718                             ]
23719                         }
23720                     ]
23721                 },
23722                 {
23723                     tag: 'td',
23724                     cls: 'separator'
23725                 }
23726             ]
23727         });
23728         
23729     },
23730     
23731     update: function()
23732     {
23733         
23734         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23735         
23736         this.fill();
23737     },
23738     
23739     fill: function() 
23740     {
23741         var hours = this.time.getHours();
23742         var minutes = this.time.getMinutes();
23743         var period = 'AM';
23744         
23745         if(hours > 11){
23746             period = 'PM';
23747         }
23748         
23749         if(hours == 0){
23750             hours = 12;
23751         }
23752         
23753         
23754         if(hours > 12){
23755             hours = hours - 12;
23756         }
23757         
23758         if(hours < 10){
23759             hours = '0' + hours;
23760         }
23761         
23762         if(minutes < 10){
23763             minutes = '0' + minutes;
23764         }
23765         
23766         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23767         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23768         this.pop.select('button', true).first().dom.innerHTML = period;
23769         
23770     },
23771     
23772     place: function()
23773     {   
23774         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23775         
23776         var cls = ['bottom'];
23777         
23778         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23779             cls.pop();
23780             cls.push('top');
23781         }
23782         
23783         cls.push('right');
23784         
23785         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23786             cls.pop();
23787             cls.push('left');
23788         }
23789         //this.picker().setXY(20000,20000);
23790         this.picker().addClass(cls.join('-'));
23791         
23792         var _this = this;
23793         
23794         Roo.each(cls, function(c){
23795             if(c == 'bottom'){
23796                 (function() {
23797                  //  
23798                 }).defer(200);
23799                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23800                 //_this.picker().setTop(_this.inputEl().getHeight());
23801                 return;
23802             }
23803             if(c == 'top'){
23804                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23805                 
23806                 //_this.picker().setTop(0 - _this.picker().getHeight());
23807                 return;
23808             }
23809             /*
23810             if(c == 'left'){
23811                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23812                 return;
23813             }
23814             if(c == 'right'){
23815                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23816                 return;
23817             }
23818             */
23819         });
23820         
23821     },
23822   
23823     onFocus : function()
23824     {
23825         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23826         this.show();
23827     },
23828     
23829     onBlur : function()
23830     {
23831         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23832         this.hide();
23833     },
23834     
23835     show : function()
23836     {
23837         this.picker().show();
23838         this.pop.show();
23839         this.update();
23840         this.place();
23841         
23842         this.fireEvent('show', this, this.date);
23843     },
23844     
23845     hide : function()
23846     {
23847         this.picker().hide();
23848         this.pop.hide();
23849         
23850         this.fireEvent('hide', this, this.date);
23851     },
23852     
23853     setTime : function()
23854     {
23855         this.hide();
23856         this.setValue(this.time.format(this.format));
23857         
23858         this.fireEvent('select', this, this.date);
23859         
23860         
23861     },
23862     
23863     onMousedown: function(e){
23864         e.stopPropagation();
23865         e.preventDefault();
23866     },
23867     
23868     onIncrementHours: function()
23869     {
23870         Roo.log('onIncrementHours');
23871         this.time = this.time.add(Date.HOUR, 1);
23872         this.update();
23873         
23874     },
23875     
23876     onDecrementHours: function()
23877     {
23878         Roo.log('onDecrementHours');
23879         this.time = this.time.add(Date.HOUR, -1);
23880         this.update();
23881     },
23882     
23883     onIncrementMinutes: function()
23884     {
23885         Roo.log('onIncrementMinutes');
23886         this.time = this.time.add(Date.MINUTE, 1);
23887         this.update();
23888     },
23889     
23890     onDecrementMinutes: function()
23891     {
23892         Roo.log('onDecrementMinutes');
23893         this.time = this.time.add(Date.MINUTE, -1);
23894         this.update();
23895     },
23896     
23897     onTogglePeriod: function()
23898     {
23899         Roo.log('onTogglePeriod');
23900         this.time = this.time.add(Date.HOUR, 12);
23901         this.update();
23902     }
23903     
23904    
23905 });
23906  
23907
23908 Roo.apply(Roo.bootstrap.TimeField,  {
23909   
23910     template : {
23911         tag: 'div',
23912         cls: 'datepicker dropdown-menu',
23913         cn: [
23914             {
23915                 tag: 'div',
23916                 cls: 'datepicker-time',
23917                 cn: [
23918                 {
23919                     tag: 'table',
23920                     cls: 'table-condensed',
23921                     cn:[
23922                         {
23923                             tag: 'tbody',
23924                             cn: [
23925                                 {
23926                                     tag: 'tr',
23927                                     cn: [
23928                                     {
23929                                         tag: 'td',
23930                                         colspan: '7'
23931                                     }
23932                                     ]
23933                                 }
23934                             ]
23935                         },
23936                         {
23937                             tag: 'tfoot',
23938                             cn: [
23939                                 {
23940                                     tag: 'tr',
23941                                     cn: [
23942                                     {
23943                                         tag: 'th',
23944                                         colspan: '7',
23945                                         cls: '',
23946                                         cn: [
23947                                             {
23948                                                 tag: 'button',
23949                                                 cls: 'btn btn-info ok',
23950                                                 html: 'OK'
23951                                             }
23952                                         ]
23953                                     }
23954                     
23955                                     ]
23956                                 }
23957                             ]
23958                         }
23959                     ]
23960                 }
23961                 ]
23962             }
23963         ]
23964     }
23965 });
23966
23967  
23968
23969  /*
23970  * - LGPL
23971  *
23972  * MonthField
23973  * 
23974  */
23975
23976 /**
23977  * @class Roo.bootstrap.MonthField
23978  * @extends Roo.bootstrap.Input
23979  * Bootstrap MonthField class
23980  * 
23981  * @cfg {String} language default en
23982  * 
23983  * @constructor
23984  * Create a new MonthField
23985  * @param {Object} config The config object
23986  */
23987
23988 Roo.bootstrap.MonthField = function(config){
23989     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23990     
23991     this.addEvents({
23992         /**
23993          * @event show
23994          * Fires when this field show.
23995          * @param {Roo.bootstrap.MonthField} this
23996          * @param {Mixed} date The date value
23997          */
23998         show : true,
23999         /**
24000          * @event show
24001          * Fires when this field hide.
24002          * @param {Roo.bootstrap.MonthField} this
24003          * @param {Mixed} date The date value
24004          */
24005         hide : true,
24006         /**
24007          * @event select
24008          * Fires when select a date.
24009          * @param {Roo.bootstrap.MonthField} this
24010          * @param {String} oldvalue The old value
24011          * @param {String} newvalue The new value
24012          */
24013         select : true
24014     });
24015 };
24016
24017 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24018     
24019     onRender: function(ct, position)
24020     {
24021         
24022         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24023         
24024         this.language = this.language || 'en';
24025         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24026         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24027         
24028         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24029         this.isInline = false;
24030         this.isInput = true;
24031         this.component = this.el.select('.add-on', true).first() || false;
24032         this.component = (this.component && this.component.length === 0) ? false : this.component;
24033         this.hasInput = this.component && this.inputEL().length;
24034         
24035         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24036         
24037         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24038         
24039         this.picker().on('mousedown', this.onMousedown, this);
24040         this.picker().on('click', this.onClick, this);
24041         
24042         this.picker().addClass('datepicker-dropdown');
24043         
24044         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24045             v.setStyle('width', '189px');
24046         });
24047         
24048         this.fillMonths();
24049         
24050         this.update();
24051         
24052         if(this.isInline) {
24053             this.show();
24054         }
24055         
24056     },
24057     
24058     setValue: function(v, suppressEvent)
24059     {   
24060         var o = this.getValue();
24061         
24062         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24063         
24064         this.update();
24065
24066         if(suppressEvent !== true){
24067             this.fireEvent('select', this, o, v);
24068         }
24069         
24070     },
24071     
24072     getValue: function()
24073     {
24074         return this.value;
24075     },
24076     
24077     onClick: function(e) 
24078     {
24079         e.stopPropagation();
24080         e.preventDefault();
24081         
24082         var target = e.getTarget();
24083         
24084         if(target.nodeName.toLowerCase() === 'i'){
24085             target = Roo.get(target).dom.parentNode;
24086         }
24087         
24088         var nodeName = target.nodeName;
24089         var className = target.className;
24090         var html = target.innerHTML;
24091         
24092         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24093             return;
24094         }
24095         
24096         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24097         
24098         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24099         
24100         this.hide();
24101                         
24102     },
24103     
24104     picker : function()
24105     {
24106         return this.pickerEl;
24107     },
24108     
24109     fillMonths: function()
24110     {    
24111         var i = 0;
24112         var months = this.picker().select('>.datepicker-months td', true).first();
24113         
24114         months.dom.innerHTML = '';
24115         
24116         while (i < 12) {
24117             var month = {
24118                 tag: 'span',
24119                 cls: 'month',
24120                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24121             };
24122             
24123             months.createChild(month);
24124         }
24125         
24126     },
24127     
24128     update: function()
24129     {
24130         var _this = this;
24131         
24132         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24133             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24134         }
24135         
24136         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24137             e.removeClass('active');
24138             
24139             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24140                 e.addClass('active');
24141             }
24142         })
24143     },
24144     
24145     place: function()
24146     {
24147         if(this.isInline) {
24148             return;
24149         }
24150         
24151         this.picker().removeClass(['bottom', 'top']);
24152         
24153         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24154             /*
24155              * place to the top of element!
24156              *
24157              */
24158             
24159             this.picker().addClass('top');
24160             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24161             
24162             return;
24163         }
24164         
24165         this.picker().addClass('bottom');
24166         
24167         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24168     },
24169     
24170     onFocus : function()
24171     {
24172         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24173         this.show();
24174     },
24175     
24176     onBlur : function()
24177     {
24178         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24179         
24180         var d = this.inputEl().getValue();
24181         
24182         this.setValue(d);
24183                 
24184         this.hide();
24185     },
24186     
24187     show : function()
24188     {
24189         this.picker().show();
24190         this.picker().select('>.datepicker-months', true).first().show();
24191         this.update();
24192         this.place();
24193         
24194         this.fireEvent('show', this, this.date);
24195     },
24196     
24197     hide : function()
24198     {
24199         if(this.isInline) {
24200             return;
24201         }
24202         this.picker().hide();
24203         this.fireEvent('hide', this, this.date);
24204         
24205     },
24206     
24207     onMousedown: function(e)
24208     {
24209         e.stopPropagation();
24210         e.preventDefault();
24211     },
24212     
24213     keyup: function(e)
24214     {
24215         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24216         this.update();
24217     },
24218
24219     fireKey: function(e)
24220     {
24221         if (!this.picker().isVisible()){
24222             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24223                 this.show();
24224             }
24225             return;
24226         }
24227         
24228         var dir;
24229         
24230         switch(e.keyCode){
24231             case 27: // escape
24232                 this.hide();
24233                 e.preventDefault();
24234                 break;
24235             case 37: // left
24236             case 39: // right
24237                 dir = e.keyCode == 37 ? -1 : 1;
24238                 
24239                 this.vIndex = this.vIndex + dir;
24240                 
24241                 if(this.vIndex < 0){
24242                     this.vIndex = 0;
24243                 }
24244                 
24245                 if(this.vIndex > 11){
24246                     this.vIndex = 11;
24247                 }
24248                 
24249                 if(isNaN(this.vIndex)){
24250                     this.vIndex = 0;
24251                 }
24252                 
24253                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24254                 
24255                 break;
24256             case 38: // up
24257             case 40: // down
24258                 
24259                 dir = e.keyCode == 38 ? -1 : 1;
24260                 
24261                 this.vIndex = this.vIndex + dir * 4;
24262                 
24263                 if(this.vIndex < 0){
24264                     this.vIndex = 0;
24265                 }
24266                 
24267                 if(this.vIndex > 11){
24268                     this.vIndex = 11;
24269                 }
24270                 
24271                 if(isNaN(this.vIndex)){
24272                     this.vIndex = 0;
24273                 }
24274                 
24275                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24276                 break;
24277                 
24278             case 13: // enter
24279                 
24280                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24281                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24282                 }
24283                 
24284                 this.hide();
24285                 e.preventDefault();
24286                 break;
24287             case 9: // tab
24288                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24289                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24290                 }
24291                 this.hide();
24292                 break;
24293             case 16: // shift
24294             case 17: // ctrl
24295             case 18: // alt
24296                 break;
24297             default :
24298                 this.hide();
24299                 
24300         }
24301     },
24302     
24303     remove: function() 
24304     {
24305         this.picker().remove();
24306     }
24307    
24308 });
24309
24310 Roo.apply(Roo.bootstrap.MonthField,  {
24311     
24312     content : {
24313         tag: 'tbody',
24314         cn: [
24315         {
24316             tag: 'tr',
24317             cn: [
24318             {
24319                 tag: 'td',
24320                 colspan: '7'
24321             }
24322             ]
24323         }
24324         ]
24325     },
24326     
24327     dates:{
24328         en: {
24329             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24330             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24331         }
24332     }
24333 });
24334
24335 Roo.apply(Roo.bootstrap.MonthField,  {
24336   
24337     template : {
24338         tag: 'div',
24339         cls: 'datepicker dropdown-menu roo-dynamic',
24340         cn: [
24341             {
24342                 tag: 'div',
24343                 cls: 'datepicker-months',
24344                 cn: [
24345                 {
24346                     tag: 'table',
24347                     cls: 'table-condensed',
24348                     cn:[
24349                         Roo.bootstrap.DateField.content
24350                     ]
24351                 }
24352                 ]
24353             }
24354         ]
24355     }
24356 });
24357
24358  
24359
24360  
24361  /*
24362  * - LGPL
24363  *
24364  * CheckBox
24365  * 
24366  */
24367
24368 /**
24369  * @class Roo.bootstrap.CheckBox
24370  * @extends Roo.bootstrap.Input
24371  * Bootstrap CheckBox class
24372  * 
24373  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24374  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24375  * @cfg {String} boxLabel The text that appears beside the checkbox
24376  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24377  * @cfg {Boolean} checked initnal the element
24378  * @cfg {Boolean} inline inline the element (default false)
24379  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24380  * @cfg {String} tooltip label tooltip
24381  * 
24382  * @constructor
24383  * Create a new CheckBox
24384  * @param {Object} config The config object
24385  */
24386
24387 Roo.bootstrap.CheckBox = function(config){
24388     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24389    
24390     this.addEvents({
24391         /**
24392         * @event check
24393         * Fires when the element is checked or unchecked.
24394         * @param {Roo.bootstrap.CheckBox} this This input
24395         * @param {Boolean} checked The new checked value
24396         */
24397        check : true,
24398        /**
24399         * @event click
24400         * Fires when the element is click.
24401         * @param {Roo.bootstrap.CheckBox} this This input
24402         */
24403        click : true
24404     });
24405     
24406 };
24407
24408 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24409   
24410     inputType: 'checkbox',
24411     inputValue: 1,
24412     valueOff: 0,
24413     boxLabel: false,
24414     checked: false,
24415     weight : false,
24416     inline: false,
24417     tooltip : '',
24418     
24419     // checkbox success does not make any sense really.. 
24420     invalidClass : "",
24421     validClass : "",
24422     
24423     
24424     getAutoCreate : function()
24425     {
24426         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24427         
24428         var id = Roo.id();
24429         
24430         var cfg = {};
24431         
24432         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24433         
24434         if(this.inline){
24435             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24436         }
24437         
24438         var input =  {
24439             tag: 'input',
24440             id : id,
24441             type : this.inputType,
24442             value : this.inputValue,
24443             cls : 'roo-' + this.inputType, //'form-box',
24444             placeholder : this.placeholder || ''
24445             
24446         };
24447         
24448         if(this.inputType != 'radio'){
24449             var hidden =  {
24450                 tag: 'input',
24451                 type : 'hidden',
24452                 cls : 'roo-hidden-value',
24453                 value : this.checked ? this.inputValue : this.valueOff
24454             };
24455         }
24456         
24457             
24458         if (this.weight) { // Validity check?
24459             cfg.cls += " " + this.inputType + "-" + this.weight;
24460         }
24461         
24462         if (this.disabled) {
24463             input.disabled=true;
24464         }
24465         
24466         if(this.checked){
24467             input.checked = this.checked;
24468         }
24469         
24470         if (this.name) {
24471             
24472             input.name = this.name;
24473             
24474             if(this.inputType != 'radio'){
24475                 hidden.name = this.name;
24476                 input.name = '_hidden_' + this.name;
24477             }
24478         }
24479         
24480         if (this.size) {
24481             input.cls += ' input-' + this.size;
24482         }
24483         
24484         var settings=this;
24485         
24486         ['xs','sm','md','lg'].map(function(size){
24487             if (settings[size]) {
24488                 cfg.cls += ' col-' + size + '-' + settings[size];
24489             }
24490         });
24491         
24492         var inputblock = input;
24493          
24494         if (this.before || this.after) {
24495             
24496             inputblock = {
24497                 cls : 'input-group',
24498                 cn :  [] 
24499             };
24500             
24501             if (this.before) {
24502                 inputblock.cn.push({
24503                     tag :'span',
24504                     cls : 'input-group-addon',
24505                     html : this.before
24506                 });
24507             }
24508             
24509             inputblock.cn.push(input);
24510             
24511             if(this.inputType != 'radio'){
24512                 inputblock.cn.push(hidden);
24513             }
24514             
24515             if (this.after) {
24516                 inputblock.cn.push({
24517                     tag :'span',
24518                     cls : 'input-group-addon',
24519                     html : this.after
24520                 });
24521             }
24522             
24523         }
24524         var boxLabelCfg = false;
24525         
24526         if(this.boxLabel){
24527            
24528             boxLabelCfg = {
24529                 tag: 'label',
24530                 //'for': id, // box label is handled by onclick - so no for...
24531                 cls: 'box-label',
24532                 html: this.boxLabel
24533             };
24534             if(this.tooltip){
24535                 boxLabelCfg.tooltip = this.tooltip;
24536             }
24537              
24538         }
24539         
24540         
24541         if (align ==='left' && this.fieldLabel.length) {
24542 //                Roo.log("left and has label");
24543             cfg.cn = [
24544                 {
24545                     tag: 'label',
24546                     'for' :  id,
24547                     cls : 'control-label',
24548                     html : this.fieldLabel
24549                 },
24550                 {
24551                     cls : "", 
24552                     cn: [
24553                         inputblock
24554                     ]
24555                 }
24556             ];
24557             
24558             if (boxLabelCfg) {
24559                 cfg.cn[1].cn.push(boxLabelCfg);
24560             }
24561             
24562             if(this.labelWidth > 12){
24563                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24564             }
24565             
24566             if(this.labelWidth < 13 && this.labelmd == 0){
24567                 this.labelmd = this.labelWidth;
24568             }
24569             
24570             if(this.labellg > 0){
24571                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24572                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24573             }
24574             
24575             if(this.labelmd > 0){
24576                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24577                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24578             }
24579             
24580             if(this.labelsm > 0){
24581                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24582                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24583             }
24584             
24585             if(this.labelxs > 0){
24586                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24587                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24588             }
24589             
24590         } else if ( this.fieldLabel.length) {
24591 //                Roo.log(" label");
24592                 cfg.cn = [
24593                    
24594                     {
24595                         tag: this.boxLabel ? 'span' : 'label',
24596                         'for': id,
24597                         cls: 'control-label box-input-label',
24598                         //cls : 'input-group-addon',
24599                         html : this.fieldLabel
24600                     },
24601                     
24602                     inputblock
24603                     
24604                 ];
24605                 if (boxLabelCfg) {
24606                     cfg.cn.push(boxLabelCfg);
24607                 }
24608
24609         } else {
24610             
24611 //                Roo.log(" no label && no align");
24612                 cfg.cn = [  inputblock ] ;
24613                 if (boxLabelCfg) {
24614                     cfg.cn.push(boxLabelCfg);
24615                 }
24616
24617                 
24618         }
24619         
24620        
24621         
24622         if(this.inputType != 'radio'){
24623             cfg.cn.push(hidden);
24624         }
24625         
24626         return cfg;
24627         
24628     },
24629     
24630     /**
24631      * return the real input element.
24632      */
24633     inputEl: function ()
24634     {
24635         return this.el.select('input.roo-' + this.inputType,true).first();
24636     },
24637     hiddenEl: function ()
24638     {
24639         return this.el.select('input.roo-hidden-value',true).first();
24640     },
24641     
24642     labelEl: function()
24643     {
24644         return this.el.select('label.control-label',true).first();
24645     },
24646     /* depricated... */
24647     
24648     label: function()
24649     {
24650         return this.labelEl();
24651     },
24652     
24653     boxLabelEl: function()
24654     {
24655         return this.el.select('label.box-label',true).first();
24656     },
24657     
24658     initEvents : function()
24659     {
24660 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24661         
24662         this.inputEl().on('click', this.onClick,  this);
24663         
24664         if (this.boxLabel) { 
24665             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24666         }
24667         
24668         this.startValue = this.getValue();
24669         
24670         if(this.groupId){
24671             Roo.bootstrap.CheckBox.register(this);
24672         }
24673     },
24674     
24675     onClick : function(e)
24676     {   
24677         if(this.fireEvent('click', this, e) !== false){
24678             this.setChecked(!this.checked);
24679         }
24680         
24681     },
24682     
24683     setChecked : function(state,suppressEvent)
24684     {
24685         this.startValue = this.getValue();
24686
24687         if(this.inputType == 'radio'){
24688             
24689             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24690                 e.dom.checked = false;
24691             });
24692             
24693             this.inputEl().dom.checked = true;
24694             
24695             this.inputEl().dom.value = this.inputValue;
24696             
24697             if(suppressEvent !== true){
24698                 this.fireEvent('check', this, true);
24699             }
24700             
24701             this.validate();
24702             
24703             return;
24704         }
24705         
24706         this.checked = state;
24707         
24708         this.inputEl().dom.checked = state;
24709         
24710         
24711         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24712         
24713         if(suppressEvent !== true){
24714             this.fireEvent('check', this, state);
24715         }
24716         
24717         this.validate();
24718     },
24719     
24720     getValue : function()
24721     {
24722         if(this.inputType == 'radio'){
24723             return this.getGroupValue();
24724         }
24725         
24726         return this.hiddenEl().dom.value;
24727         
24728     },
24729     
24730     getGroupValue : function()
24731     {
24732         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24733             return '';
24734         }
24735         
24736         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24737     },
24738     
24739     setValue : function(v,suppressEvent)
24740     {
24741         if(this.inputType == 'radio'){
24742             this.setGroupValue(v, suppressEvent);
24743             return;
24744         }
24745         
24746         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24747         
24748         this.validate();
24749     },
24750     
24751     setGroupValue : function(v, suppressEvent)
24752     {
24753         this.startValue = this.getValue();
24754         
24755         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24756             e.dom.checked = false;
24757             
24758             if(e.dom.value == v){
24759                 e.dom.checked = true;
24760             }
24761         });
24762         
24763         if(suppressEvent !== true){
24764             this.fireEvent('check', this, true);
24765         }
24766
24767         this.validate();
24768         
24769         return;
24770     },
24771     
24772     validate : function()
24773     {
24774         if(this.getVisibilityEl().hasClass('hidden')){
24775             return true;
24776         }
24777         
24778         if(
24779                 this.disabled || 
24780                 (this.inputType == 'radio' && this.validateRadio()) ||
24781                 (this.inputType == 'checkbox' && this.validateCheckbox())
24782         ){
24783             this.markValid();
24784             return true;
24785         }
24786         
24787         this.markInvalid();
24788         return false;
24789     },
24790     
24791     validateRadio : function()
24792     {
24793         if(this.getVisibilityEl().hasClass('hidden')){
24794             return true;
24795         }
24796         
24797         if(this.allowBlank){
24798             return true;
24799         }
24800         
24801         var valid = false;
24802         
24803         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24804             if(!e.dom.checked){
24805                 return;
24806             }
24807             
24808             valid = true;
24809             
24810             return false;
24811         });
24812         
24813         return valid;
24814     },
24815     
24816     validateCheckbox : function()
24817     {
24818         if(!this.groupId){
24819             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24820             //return (this.getValue() == this.inputValue) ? true : false;
24821         }
24822         
24823         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24824         
24825         if(!group){
24826             return false;
24827         }
24828         
24829         var r = false;
24830         
24831         for(var i in group){
24832             if(group[i].el.isVisible(true)){
24833                 r = false;
24834                 break;
24835             }
24836             
24837             r = true;
24838         }
24839         
24840         for(var i in group){
24841             if(r){
24842                 break;
24843             }
24844             
24845             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24846         }
24847         
24848         return r;
24849     },
24850     
24851     /**
24852      * Mark this field as valid
24853      */
24854     markValid : function()
24855     {
24856         var _this = this;
24857         
24858         this.fireEvent('valid', this);
24859         
24860         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24861         
24862         if(this.groupId){
24863             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24864         }
24865         
24866         if(label){
24867             label.markValid();
24868         }
24869
24870         if(this.inputType == 'radio'){
24871             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24872                 var fg = e.findParent('.form-group', false, true);
24873                 if (Roo.bootstrap.version == 3) {
24874                     fg.removeClass([_this.invalidClass, _this.validClass]);
24875                     fg.addClass(_this.validClass);
24876                 } else {
24877                     fg.removeClass(['is-valid', 'is-invalid']);
24878                     fg.addClass('is-valid');
24879                 }
24880             });
24881             
24882             return;
24883         }
24884
24885         if(!this.groupId){
24886             var fg = this.el.findParent('.form-group', false, true);
24887             if (Roo.bootstrap.version == 3) {
24888                 fg.removeClass([this.invalidClass, this.validClass]);
24889                 fg.addClass(this.validClass);
24890             } else {
24891                 fg.removeClass(['is-valid', 'is-invalid']);
24892                 fg.addClass('is-valid');
24893             }
24894             return;
24895         }
24896         
24897         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24898         
24899         if(!group){
24900             return;
24901         }
24902         
24903         for(var i in group){
24904             var fg = group[i].el.findParent('.form-group', false, true);
24905             if (Roo.bootstrap.version == 3) {
24906                 fg.removeClass([this.invalidClass, this.validClass]);
24907                 fg.addClass(this.validClass);
24908             } else {
24909                 fg.removeClass(['is-valid', 'is-invalid']);
24910                 fg.addClass('is-valid');
24911             }
24912         }
24913     },
24914     
24915      /**
24916      * Mark this field as invalid
24917      * @param {String} msg The validation message
24918      */
24919     markInvalid : function(msg)
24920     {
24921         if(this.allowBlank){
24922             return;
24923         }
24924         
24925         var _this = this;
24926         
24927         this.fireEvent('invalid', this, msg);
24928         
24929         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24930         
24931         if(this.groupId){
24932             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24933         }
24934         
24935         if(label){
24936             label.markInvalid();
24937         }
24938             
24939         if(this.inputType == 'radio'){
24940             
24941             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24942                 var fg = e.findParent('.form-group', false, true);
24943                 if (Roo.bootstrap.version == 3) {
24944                     fg.removeClass([_this.invalidClass, _this.validClass]);
24945                     fg.addClass(_this.invalidClass);
24946                 } else {
24947                     fg.removeClass(['is-invalid', 'is-valid']);
24948                     fg.addClass('is-invalid');
24949                 }
24950             });
24951             
24952             return;
24953         }
24954         
24955         if(!this.groupId){
24956             var fg = this.el.findParent('.form-group', false, true);
24957             if (Roo.bootstrap.version == 3) {
24958                 fg.removeClass([_this.invalidClass, _this.validClass]);
24959                 fg.addClass(_this.invalidClass);
24960             } else {
24961                 fg.removeClass(['is-invalid', 'is-valid']);
24962                 fg.addClass('is-invalid');
24963             }
24964             return;
24965         }
24966         
24967         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24968         
24969         if(!group){
24970             return;
24971         }
24972         
24973         for(var i in group){
24974             var fg = group[i].el.findParent('.form-group', false, true);
24975             if (Roo.bootstrap.version == 3) {
24976                 fg.removeClass([_this.invalidClass, _this.validClass]);
24977                 fg.addClass(_this.invalidClass);
24978             } else {
24979                 fg.removeClass(['is-invalid', 'is-valid']);
24980                 fg.addClass('is-invalid');
24981             }
24982         }
24983         
24984     },
24985     
24986     clearInvalid : function()
24987     {
24988         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24989         
24990         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24991         
24992         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24993         
24994         if (label && label.iconEl) {
24995             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24996             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24997         }
24998     },
24999     
25000     disable : function()
25001     {
25002         if(this.inputType != 'radio'){
25003             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25004             return;
25005         }
25006         
25007         var _this = this;
25008         
25009         if(this.rendered){
25010             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25011                 _this.getActionEl().addClass(this.disabledClass);
25012                 e.dom.disabled = true;
25013             });
25014         }
25015         
25016         this.disabled = true;
25017         this.fireEvent("disable", this);
25018         return this;
25019     },
25020
25021     enable : function()
25022     {
25023         if(this.inputType != 'radio'){
25024             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25025             return;
25026         }
25027         
25028         var _this = this;
25029         
25030         if(this.rendered){
25031             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25032                 _this.getActionEl().removeClass(this.disabledClass);
25033                 e.dom.disabled = false;
25034             });
25035         }
25036         
25037         this.disabled = false;
25038         this.fireEvent("enable", this);
25039         return this;
25040     },
25041     
25042     setBoxLabel : function(v)
25043     {
25044         this.boxLabel = v;
25045         
25046         if(this.rendered){
25047             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25048         }
25049     }
25050
25051 });
25052
25053 Roo.apply(Roo.bootstrap.CheckBox, {
25054     
25055     groups: {},
25056     
25057      /**
25058     * register a CheckBox Group
25059     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25060     */
25061     register : function(checkbox)
25062     {
25063         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25064             this.groups[checkbox.groupId] = {};
25065         }
25066         
25067         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25068             return;
25069         }
25070         
25071         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25072         
25073     },
25074     /**
25075     * fetch a CheckBox Group based on the group ID
25076     * @param {string} the group ID
25077     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25078     */
25079     get: function(groupId) {
25080         if (typeof(this.groups[groupId]) == 'undefined') {
25081             return false;
25082         }
25083         
25084         return this.groups[groupId] ;
25085     }
25086     
25087     
25088 });
25089 /*
25090  * - LGPL
25091  *
25092  * RadioItem
25093  * 
25094  */
25095
25096 /**
25097  * @class Roo.bootstrap.Radio
25098  * @extends Roo.bootstrap.Component
25099  * Bootstrap Radio class
25100  * @cfg {String} boxLabel - the label associated
25101  * @cfg {String} value - the value of radio
25102  * 
25103  * @constructor
25104  * Create a new Radio
25105  * @param {Object} config The config object
25106  */
25107 Roo.bootstrap.Radio = function(config){
25108     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25109     
25110 };
25111
25112 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25113     
25114     boxLabel : '',
25115     
25116     value : '',
25117     
25118     getAutoCreate : function()
25119     {
25120         var cfg = {
25121             tag : 'div',
25122             cls : 'form-group radio',
25123             cn : [
25124                 {
25125                     tag : 'label',
25126                     cls : 'box-label',
25127                     html : this.boxLabel
25128                 }
25129             ]
25130         };
25131         
25132         return cfg;
25133     },
25134     
25135     initEvents : function() 
25136     {
25137         this.parent().register(this);
25138         
25139         this.el.on('click', this.onClick, this);
25140         
25141     },
25142     
25143     onClick : function(e)
25144     {
25145         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25146             this.setChecked(true);
25147         }
25148     },
25149     
25150     setChecked : function(state, suppressEvent)
25151     {
25152         this.parent().setValue(this.value, suppressEvent);
25153         
25154     },
25155     
25156     setBoxLabel : function(v)
25157     {
25158         this.boxLabel = v;
25159         
25160         if(this.rendered){
25161             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25162         }
25163     }
25164     
25165 });
25166  
25167
25168  /*
25169  * - LGPL
25170  *
25171  * Input
25172  * 
25173  */
25174
25175 /**
25176  * @class Roo.bootstrap.SecurePass
25177  * @extends Roo.bootstrap.Input
25178  * Bootstrap SecurePass class
25179  *
25180  * 
25181  * @constructor
25182  * Create a new SecurePass
25183  * @param {Object} config The config object
25184  */
25185  
25186 Roo.bootstrap.SecurePass = function (config) {
25187     // these go here, so the translation tool can replace them..
25188     this.errors = {
25189         PwdEmpty: "Please type a password, and then retype it to confirm.",
25190         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25191         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25192         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25193         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25194         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25195         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25196         TooWeak: "Your password is Too Weak."
25197     },
25198     this.meterLabel = "Password strength:";
25199     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25200     this.meterClass = [
25201         "roo-password-meter-tooweak", 
25202         "roo-password-meter-weak", 
25203         "roo-password-meter-medium", 
25204         "roo-password-meter-strong", 
25205         "roo-password-meter-grey"
25206     ];
25207     
25208     this.errors = {};
25209     
25210     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25211 }
25212
25213 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25214     /**
25215      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25216      * {
25217      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25218      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25219      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25220      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25221      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25222      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25223      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25224      * })
25225      */
25226     // private
25227     
25228     meterWidth: 300,
25229     errorMsg :'',    
25230     errors: false,
25231     imageRoot: '/',
25232     /**
25233      * @cfg {String/Object} Label for the strength meter (defaults to
25234      * 'Password strength:')
25235      */
25236     // private
25237     meterLabel: '',
25238     /**
25239      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25240      * ['Weak', 'Medium', 'Strong'])
25241      */
25242     // private    
25243     pwdStrengths: false,    
25244     // private
25245     strength: 0,
25246     // private
25247     _lastPwd: null,
25248     // private
25249     kCapitalLetter: 0,
25250     kSmallLetter: 1,
25251     kDigit: 2,
25252     kPunctuation: 3,
25253     
25254     insecure: false,
25255     // private
25256     initEvents: function ()
25257     {
25258         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25259
25260         if (this.el.is('input[type=password]') && Roo.isSafari) {
25261             this.el.on('keydown', this.SafariOnKeyDown, this);
25262         }
25263
25264         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25265     },
25266     // private
25267     onRender: function (ct, position)
25268     {
25269         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25270         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25271         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25272
25273         this.trigger.createChild({
25274                    cn: [
25275                     {
25276                     //id: 'PwdMeter',
25277                     tag: 'div',
25278                     cls: 'roo-password-meter-grey col-xs-12',
25279                     style: {
25280                         //width: 0,
25281                         //width: this.meterWidth + 'px'                                                
25282                         }
25283                     },
25284                     {                            
25285                          cls: 'roo-password-meter-text'                          
25286                     }
25287                 ]            
25288         });
25289
25290          
25291         if (this.hideTrigger) {
25292             this.trigger.setDisplayed(false);
25293         }
25294         this.setSize(this.width || '', this.height || '');
25295     },
25296     // private
25297     onDestroy: function ()
25298     {
25299         if (this.trigger) {
25300             this.trigger.removeAllListeners();
25301             this.trigger.remove();
25302         }
25303         if (this.wrap) {
25304             this.wrap.remove();
25305         }
25306         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25307     },
25308     // private
25309     checkStrength: function ()
25310     {
25311         var pwd = this.inputEl().getValue();
25312         if (pwd == this._lastPwd) {
25313             return;
25314         }
25315
25316         var strength;
25317         if (this.ClientSideStrongPassword(pwd)) {
25318             strength = 3;
25319         } else if (this.ClientSideMediumPassword(pwd)) {
25320             strength = 2;
25321         } else if (this.ClientSideWeakPassword(pwd)) {
25322             strength = 1;
25323         } else {
25324             strength = 0;
25325         }
25326         
25327         Roo.log('strength1: ' + strength);
25328         
25329         //var pm = this.trigger.child('div/div/div').dom;
25330         var pm = this.trigger.child('div/div');
25331         pm.removeClass(this.meterClass);
25332         pm.addClass(this.meterClass[strength]);
25333                 
25334         
25335         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25336                 
25337         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25338         
25339         this._lastPwd = pwd;
25340     },
25341     reset: function ()
25342     {
25343         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25344         
25345         this._lastPwd = '';
25346         
25347         var pm = this.trigger.child('div/div');
25348         pm.removeClass(this.meterClass);
25349         pm.addClass('roo-password-meter-grey');        
25350         
25351         
25352         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25353         
25354         pt.innerHTML = '';
25355         this.inputEl().dom.type='password';
25356     },
25357     // private
25358     validateValue: function (value)
25359     {
25360         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25361             return false;
25362         }
25363         if (value.length == 0) {
25364             if (this.allowBlank) {
25365                 this.clearInvalid();
25366                 return true;
25367             }
25368
25369             this.markInvalid(this.errors.PwdEmpty);
25370             this.errorMsg = this.errors.PwdEmpty;
25371             return false;
25372         }
25373         
25374         if(this.insecure){
25375             return true;
25376         }
25377         
25378         if (!value.match(/[\x21-\x7e]+/)) {
25379             this.markInvalid(this.errors.PwdBadChar);
25380             this.errorMsg = this.errors.PwdBadChar;
25381             return false;
25382         }
25383         if (value.length < 6) {
25384             this.markInvalid(this.errors.PwdShort);
25385             this.errorMsg = this.errors.PwdShort;
25386             return false;
25387         }
25388         if (value.length > 16) {
25389             this.markInvalid(this.errors.PwdLong);
25390             this.errorMsg = this.errors.PwdLong;
25391             return false;
25392         }
25393         var strength;
25394         if (this.ClientSideStrongPassword(value)) {
25395             strength = 3;
25396         } else if (this.ClientSideMediumPassword(value)) {
25397             strength = 2;
25398         } else if (this.ClientSideWeakPassword(value)) {
25399             strength = 1;
25400         } else {
25401             strength = 0;
25402         }
25403
25404         
25405         if (strength < 2) {
25406             //this.markInvalid(this.errors.TooWeak);
25407             this.errorMsg = this.errors.TooWeak;
25408             //return false;
25409         }
25410         
25411         
25412         console.log('strength2: ' + strength);
25413         
25414         //var pm = this.trigger.child('div/div/div').dom;
25415         
25416         var pm = this.trigger.child('div/div');
25417         pm.removeClass(this.meterClass);
25418         pm.addClass(this.meterClass[strength]);
25419                 
25420         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25421                 
25422         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25423         
25424         this.errorMsg = ''; 
25425         return true;
25426     },
25427     // private
25428     CharacterSetChecks: function (type)
25429     {
25430         this.type = type;
25431         this.fResult = false;
25432     },
25433     // private
25434     isctype: function (character, type)
25435     {
25436         switch (type) {  
25437             case this.kCapitalLetter:
25438                 if (character >= 'A' && character <= 'Z') {
25439                     return true;
25440                 }
25441                 break;
25442             
25443             case this.kSmallLetter:
25444                 if (character >= 'a' && character <= 'z') {
25445                     return true;
25446                 }
25447                 break;
25448             
25449             case this.kDigit:
25450                 if (character >= '0' && character <= '9') {
25451                     return true;
25452                 }
25453                 break;
25454             
25455             case this.kPunctuation:
25456                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25457                     return true;
25458                 }
25459                 break;
25460             
25461             default:
25462                 return false;
25463         }
25464
25465     },
25466     // private
25467     IsLongEnough: function (pwd, size)
25468     {
25469         return !(pwd == null || isNaN(size) || pwd.length < size);
25470     },
25471     // private
25472     SpansEnoughCharacterSets: function (word, nb)
25473     {
25474         if (!this.IsLongEnough(word, nb))
25475         {
25476             return false;
25477         }
25478
25479         var characterSetChecks = new Array(
25480             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25481             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25482         );
25483         
25484         for (var index = 0; index < word.length; ++index) {
25485             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25486                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25487                     characterSetChecks[nCharSet].fResult = true;
25488                     break;
25489                 }
25490             }
25491         }
25492
25493         var nCharSets = 0;
25494         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25495             if (characterSetChecks[nCharSet].fResult) {
25496                 ++nCharSets;
25497             }
25498         }
25499
25500         if (nCharSets < nb) {
25501             return false;
25502         }
25503         return true;
25504     },
25505     // private
25506     ClientSideStrongPassword: function (pwd)
25507     {
25508         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25509     },
25510     // private
25511     ClientSideMediumPassword: function (pwd)
25512     {
25513         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25514     },
25515     // private
25516     ClientSideWeakPassword: function (pwd)
25517     {
25518         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25519     }
25520           
25521 })//<script type="text/javascript">
25522
25523 /*
25524  * Based  Ext JS Library 1.1.1
25525  * Copyright(c) 2006-2007, Ext JS, LLC.
25526  * LGPL
25527  *
25528  */
25529  
25530 /**
25531  * @class Roo.HtmlEditorCore
25532  * @extends Roo.Component
25533  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25534  *
25535  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25536  */
25537
25538 Roo.HtmlEditorCore = function(config){
25539     
25540     
25541     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25542     
25543     
25544     this.addEvents({
25545         /**
25546          * @event initialize
25547          * Fires when the editor is fully initialized (including the iframe)
25548          * @param {Roo.HtmlEditorCore} this
25549          */
25550         initialize: true,
25551         /**
25552          * @event activate
25553          * Fires when the editor is first receives the focus. Any insertion must wait
25554          * until after this event.
25555          * @param {Roo.HtmlEditorCore} this
25556          */
25557         activate: true,
25558          /**
25559          * @event beforesync
25560          * Fires before the textarea is updated with content from the editor iframe. Return false
25561          * to cancel the sync.
25562          * @param {Roo.HtmlEditorCore} this
25563          * @param {String} html
25564          */
25565         beforesync: true,
25566          /**
25567          * @event beforepush
25568          * Fires before the iframe editor is updated with content from the textarea. Return false
25569          * to cancel the push.
25570          * @param {Roo.HtmlEditorCore} this
25571          * @param {String} html
25572          */
25573         beforepush: true,
25574          /**
25575          * @event sync
25576          * Fires when the textarea is updated with content from the editor iframe.
25577          * @param {Roo.HtmlEditorCore} this
25578          * @param {String} html
25579          */
25580         sync: true,
25581          /**
25582          * @event push
25583          * Fires when the iframe editor is updated with content from the textarea.
25584          * @param {Roo.HtmlEditorCore} this
25585          * @param {String} html
25586          */
25587         push: true,
25588         
25589         /**
25590          * @event editorevent
25591          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25592          * @param {Roo.HtmlEditorCore} this
25593          */
25594         editorevent: true
25595         
25596     });
25597     
25598     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25599     
25600     // defaults : white / black...
25601     this.applyBlacklists();
25602     
25603     
25604     
25605 };
25606
25607
25608 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25609
25610
25611      /**
25612      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25613      */
25614     
25615     owner : false,
25616     
25617      /**
25618      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25619      *                        Roo.resizable.
25620      */
25621     resizable : false,
25622      /**
25623      * @cfg {Number} height (in pixels)
25624      */   
25625     height: 300,
25626    /**
25627      * @cfg {Number} width (in pixels)
25628      */   
25629     width: 500,
25630     
25631     /**
25632      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25633      * 
25634      */
25635     stylesheets: false,
25636     
25637     /**
25638      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25639      */
25640     allowComments: false,
25641     // id of frame..
25642     frameId: false,
25643     
25644     // private properties
25645     validationEvent : false,
25646     deferHeight: true,
25647     initialized : false,
25648     activated : false,
25649     sourceEditMode : false,
25650     onFocus : Roo.emptyFn,
25651     iframePad:3,
25652     hideMode:'offsets',
25653     
25654     clearUp: true,
25655     
25656     // blacklist + whitelisted elements..
25657     black: false,
25658     white: false,
25659      
25660     bodyCls : '',
25661
25662     /**
25663      * Protected method that will not generally be called directly. It
25664      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25665      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25666      */
25667     getDocMarkup : function(){
25668         // body styles..
25669         var st = '';
25670         
25671         // inherit styels from page...?? 
25672         if (this.stylesheets === false) {
25673             
25674             Roo.get(document.head).select('style').each(function(node) {
25675                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25676             });
25677             
25678             Roo.get(document.head).select('link').each(function(node) { 
25679                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25680             });
25681             
25682         } else if (!this.stylesheets.length) {
25683                 // simple..
25684                 st = '<style type="text/css">' +
25685                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25686                    '</style>';
25687         } else {
25688             for (var i in this.stylesheets) { 
25689                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25690             }
25691             
25692         }
25693         
25694         st +=  '<style type="text/css">' +
25695             'IMG { cursor: pointer } ' +
25696         '</style>';
25697
25698         var cls = 'roo-htmleditor-body';
25699         
25700         if(this.bodyCls.length){
25701             cls += ' ' + this.bodyCls;
25702         }
25703         
25704         return '<html><head>' + st  +
25705             //<style type="text/css">' +
25706             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25707             //'</style>' +
25708             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25709     },
25710
25711     // private
25712     onRender : function(ct, position)
25713     {
25714         var _t = this;
25715         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25716         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25717         
25718         
25719         this.el.dom.style.border = '0 none';
25720         this.el.dom.setAttribute('tabIndex', -1);
25721         this.el.addClass('x-hidden hide');
25722         
25723         
25724         
25725         if(Roo.isIE){ // fix IE 1px bogus margin
25726             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25727         }
25728        
25729         
25730         this.frameId = Roo.id();
25731         
25732          
25733         
25734         var iframe = this.owner.wrap.createChild({
25735             tag: 'iframe',
25736             cls: 'form-control', // bootstrap..
25737             id: this.frameId,
25738             name: this.frameId,
25739             frameBorder : 'no',
25740             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25741         }, this.el
25742         );
25743         
25744         
25745         this.iframe = iframe.dom;
25746
25747          this.assignDocWin();
25748         
25749         this.doc.designMode = 'on';
25750        
25751         this.doc.open();
25752         this.doc.write(this.getDocMarkup());
25753         this.doc.close();
25754
25755         
25756         var task = { // must defer to wait for browser to be ready
25757             run : function(){
25758                 //console.log("run task?" + this.doc.readyState);
25759                 this.assignDocWin();
25760                 if(this.doc.body || this.doc.readyState == 'complete'){
25761                     try {
25762                         this.doc.designMode="on";
25763                     } catch (e) {
25764                         return;
25765                     }
25766                     Roo.TaskMgr.stop(task);
25767                     this.initEditor.defer(10, this);
25768                 }
25769             },
25770             interval : 10,
25771             duration: 10000,
25772             scope: this
25773         };
25774         Roo.TaskMgr.start(task);
25775
25776     },
25777
25778     // private
25779     onResize : function(w, h)
25780     {
25781          Roo.log('resize: ' +w + ',' + h );
25782         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25783         if(!this.iframe){
25784             return;
25785         }
25786         if(typeof w == 'number'){
25787             
25788             this.iframe.style.width = w + 'px';
25789         }
25790         if(typeof h == 'number'){
25791             
25792             this.iframe.style.height = h + 'px';
25793             if(this.doc){
25794                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25795             }
25796         }
25797         
25798     },
25799
25800     /**
25801      * Toggles the editor between standard and source edit mode.
25802      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25803      */
25804     toggleSourceEdit : function(sourceEditMode){
25805         
25806         this.sourceEditMode = sourceEditMode === true;
25807         
25808         if(this.sourceEditMode){
25809  
25810             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25811             
25812         }else{
25813             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25814             //this.iframe.className = '';
25815             this.deferFocus();
25816         }
25817         //this.setSize(this.owner.wrap.getSize());
25818         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25819     },
25820
25821     
25822   
25823
25824     /**
25825      * Protected method that will not generally be called directly. If you need/want
25826      * custom HTML cleanup, this is the method you should override.
25827      * @param {String} html The HTML to be cleaned
25828      * return {String} The cleaned HTML
25829      */
25830     cleanHtml : function(html){
25831         html = String(html);
25832         if(html.length > 5){
25833             if(Roo.isSafari){ // strip safari nonsense
25834                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25835             }
25836         }
25837         if(html == '&nbsp;'){
25838             html = '';
25839         }
25840         return html;
25841     },
25842
25843     /**
25844      * HTML Editor -> Textarea
25845      * Protected method that will not generally be called directly. Syncs the contents
25846      * of the editor iframe with the textarea.
25847      */
25848     syncValue : function(){
25849         if(this.initialized){
25850             var bd = (this.doc.body || this.doc.documentElement);
25851             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25852             var html = bd.innerHTML;
25853             if(Roo.isSafari){
25854                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25855                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25856                 if(m && m[1]){
25857                     html = '<div style="'+m[0]+'">' + html + '</div>';
25858                 }
25859             }
25860             html = this.cleanHtml(html);
25861             // fix up the special chars.. normaly like back quotes in word...
25862             // however we do not want to do this with chinese..
25863             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25864                 
25865                 var cc = match.charCodeAt();
25866
25867                 // Get the character value, handling surrogate pairs
25868                 if (match.length == 2) {
25869                     // It's a surrogate pair, calculate the Unicode code point
25870                     var high = match.charCodeAt(0) - 0xD800;
25871                     var low  = match.charCodeAt(1) - 0xDC00;
25872                     cc = (high * 0x400) + low + 0x10000;
25873                 }  else if (
25874                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25875                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25876                     (cc >= 0xf900 && cc < 0xfb00 )
25877                 ) {
25878                         return match;
25879                 }  
25880          
25881                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25882                 return "&#" + cc + ";";
25883                 
25884                 
25885             });
25886             
25887             
25888              
25889             if(this.owner.fireEvent('beforesync', this, html) !== false){
25890                 this.el.dom.value = html;
25891                 this.owner.fireEvent('sync', this, html);
25892             }
25893         }
25894     },
25895
25896     /**
25897      * Protected method that will not generally be called directly. Pushes the value of the textarea
25898      * into the iframe editor.
25899      */
25900     pushValue : function(){
25901         if(this.initialized){
25902             var v = this.el.dom.value.trim();
25903             
25904 //            if(v.length < 1){
25905 //                v = '&#160;';
25906 //            }
25907             
25908             if(this.owner.fireEvent('beforepush', this, v) !== false){
25909                 var d = (this.doc.body || this.doc.documentElement);
25910                 d.innerHTML = v;
25911                 this.cleanUpPaste();
25912                 this.el.dom.value = d.innerHTML;
25913                 this.owner.fireEvent('push', this, v);
25914             }
25915         }
25916     },
25917
25918     // private
25919     deferFocus : function(){
25920         this.focus.defer(10, this);
25921     },
25922
25923     // doc'ed in Field
25924     focus : function(){
25925         if(this.win && !this.sourceEditMode){
25926             this.win.focus();
25927         }else{
25928             this.el.focus();
25929         }
25930     },
25931     
25932     assignDocWin: function()
25933     {
25934         var iframe = this.iframe;
25935         
25936          if(Roo.isIE){
25937             this.doc = iframe.contentWindow.document;
25938             this.win = iframe.contentWindow;
25939         } else {
25940 //            if (!Roo.get(this.frameId)) {
25941 //                return;
25942 //            }
25943 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25944 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25945             
25946             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25947                 return;
25948             }
25949             
25950             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25951             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25952         }
25953     },
25954     
25955     // private
25956     initEditor : function(){
25957         //console.log("INIT EDITOR");
25958         this.assignDocWin();
25959         
25960         
25961         
25962         this.doc.designMode="on";
25963         this.doc.open();
25964         this.doc.write(this.getDocMarkup());
25965         this.doc.close();
25966         
25967         var dbody = (this.doc.body || this.doc.documentElement);
25968         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25969         // this copies styles from the containing element into thsi one..
25970         // not sure why we need all of this..
25971         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25972         
25973         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25974         //ss['background-attachment'] = 'fixed'; // w3c
25975         dbody.bgProperties = 'fixed'; // ie
25976         //Roo.DomHelper.applyStyles(dbody, ss);
25977         Roo.EventManager.on(this.doc, {
25978             //'mousedown': this.onEditorEvent,
25979             'mouseup': this.onEditorEvent,
25980             'dblclick': this.onEditorEvent,
25981             'click': this.onEditorEvent,
25982             'keyup': this.onEditorEvent,
25983             buffer:100,
25984             scope: this
25985         });
25986         if(Roo.isGecko){
25987             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25988         }
25989         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25990             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25991         }
25992         this.initialized = true;
25993
25994         this.owner.fireEvent('initialize', this);
25995         this.pushValue();
25996     },
25997
25998     // private
25999     onDestroy : function(){
26000         
26001         
26002         
26003         if(this.rendered){
26004             
26005             //for (var i =0; i < this.toolbars.length;i++) {
26006             //    // fixme - ask toolbars for heights?
26007             //    this.toolbars[i].onDestroy();
26008            // }
26009             
26010             //this.wrap.dom.innerHTML = '';
26011             //this.wrap.remove();
26012         }
26013     },
26014
26015     // private
26016     onFirstFocus : function(){
26017         
26018         this.assignDocWin();
26019         
26020         
26021         this.activated = true;
26022          
26023     
26024         if(Roo.isGecko){ // prevent silly gecko errors
26025             this.win.focus();
26026             var s = this.win.getSelection();
26027             if(!s.focusNode || s.focusNode.nodeType != 3){
26028                 var r = s.getRangeAt(0);
26029                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26030                 r.collapse(true);
26031                 this.deferFocus();
26032             }
26033             try{
26034                 this.execCmd('useCSS', true);
26035                 this.execCmd('styleWithCSS', false);
26036             }catch(e){}
26037         }
26038         this.owner.fireEvent('activate', this);
26039     },
26040
26041     // private
26042     adjustFont: function(btn){
26043         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26044         //if(Roo.isSafari){ // safari
26045         //    adjust *= 2;
26046        // }
26047         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26048         if(Roo.isSafari){ // safari
26049             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26050             v =  (v < 10) ? 10 : v;
26051             v =  (v > 48) ? 48 : v;
26052             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26053             
26054         }
26055         
26056         
26057         v = Math.max(1, v+adjust);
26058         
26059         this.execCmd('FontSize', v  );
26060     },
26061
26062     onEditorEvent : function(e)
26063     {
26064         this.owner.fireEvent('editorevent', this, e);
26065       //  this.updateToolbar();
26066         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26067     },
26068
26069     insertTag : function(tg)
26070     {
26071         // could be a bit smarter... -> wrap the current selected tRoo..
26072         if (tg.toLowerCase() == 'span' ||
26073             tg.toLowerCase() == 'code' ||
26074             tg.toLowerCase() == 'sup' ||
26075             tg.toLowerCase() == 'sub' 
26076             ) {
26077             
26078             range = this.createRange(this.getSelection());
26079             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26080             wrappingNode.appendChild(range.extractContents());
26081             range.insertNode(wrappingNode);
26082
26083             return;
26084             
26085             
26086             
26087         }
26088         this.execCmd("formatblock",   tg);
26089         
26090     },
26091     
26092     insertText : function(txt)
26093     {
26094         
26095         
26096         var range = this.createRange();
26097         range.deleteContents();
26098                //alert(Sender.getAttribute('label'));
26099                
26100         range.insertNode(this.doc.createTextNode(txt));
26101     } ,
26102     
26103      
26104
26105     /**
26106      * Executes a Midas editor command on the editor document and performs necessary focus and
26107      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26108      * @param {String} cmd The Midas command
26109      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26110      */
26111     relayCmd : function(cmd, value){
26112         this.win.focus();
26113         this.execCmd(cmd, value);
26114         this.owner.fireEvent('editorevent', this);
26115         //this.updateToolbar();
26116         this.owner.deferFocus();
26117     },
26118
26119     /**
26120      * Executes a Midas editor command directly on the editor document.
26121      * For visual commands, you should use {@link #relayCmd} instead.
26122      * <b>This should only be called after the editor is initialized.</b>
26123      * @param {String} cmd The Midas command
26124      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26125      */
26126     execCmd : function(cmd, value){
26127         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26128         this.syncValue();
26129     },
26130  
26131  
26132    
26133     /**
26134      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26135      * to insert tRoo.
26136      * @param {String} text | dom node.. 
26137      */
26138     insertAtCursor : function(text)
26139     {
26140         
26141         if(!this.activated){
26142             return;
26143         }
26144         /*
26145         if(Roo.isIE){
26146             this.win.focus();
26147             var r = this.doc.selection.createRange();
26148             if(r){
26149                 r.collapse(true);
26150                 r.pasteHTML(text);
26151                 this.syncValue();
26152                 this.deferFocus();
26153             
26154             }
26155             return;
26156         }
26157         */
26158         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26159             this.win.focus();
26160             
26161             
26162             // from jquery ui (MIT licenced)
26163             var range, node;
26164             var win = this.win;
26165             
26166             if (win.getSelection && win.getSelection().getRangeAt) {
26167                 range = win.getSelection().getRangeAt(0);
26168                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26169                 range.insertNode(node);
26170             } else if (win.document.selection && win.document.selection.createRange) {
26171                 // no firefox support
26172                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26173                 win.document.selection.createRange().pasteHTML(txt);
26174             } else {
26175                 // no firefox support
26176                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26177                 this.execCmd('InsertHTML', txt);
26178             } 
26179             
26180             this.syncValue();
26181             
26182             this.deferFocus();
26183         }
26184     },
26185  // private
26186     mozKeyPress : function(e){
26187         if(e.ctrlKey){
26188             var c = e.getCharCode(), cmd;
26189           
26190             if(c > 0){
26191                 c = String.fromCharCode(c).toLowerCase();
26192                 switch(c){
26193                     case 'b':
26194                         cmd = 'bold';
26195                         break;
26196                     case 'i':
26197                         cmd = 'italic';
26198                         break;
26199                     
26200                     case 'u':
26201                         cmd = 'underline';
26202                         break;
26203                     
26204                     case 'v':
26205                         this.cleanUpPaste.defer(100, this);
26206                         return;
26207                         
26208                 }
26209                 if(cmd){
26210                     this.win.focus();
26211                     this.execCmd(cmd);
26212                     this.deferFocus();
26213                     e.preventDefault();
26214                 }
26215                 
26216             }
26217         }
26218     },
26219
26220     // private
26221     fixKeys : function(){ // load time branching for fastest keydown performance
26222         if(Roo.isIE){
26223             return function(e){
26224                 var k = e.getKey(), r;
26225                 if(k == e.TAB){
26226                     e.stopEvent();
26227                     r = this.doc.selection.createRange();
26228                     if(r){
26229                         r.collapse(true);
26230                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26231                         this.deferFocus();
26232                     }
26233                     return;
26234                 }
26235                 
26236                 if(k == e.ENTER){
26237                     r = this.doc.selection.createRange();
26238                     if(r){
26239                         var target = r.parentElement();
26240                         if(!target || target.tagName.toLowerCase() != 'li'){
26241                             e.stopEvent();
26242                             r.pasteHTML('<br />');
26243                             r.collapse(false);
26244                             r.select();
26245                         }
26246                     }
26247                 }
26248                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26249                     this.cleanUpPaste.defer(100, this);
26250                     return;
26251                 }
26252                 
26253                 
26254             };
26255         }else if(Roo.isOpera){
26256             return function(e){
26257                 var k = e.getKey();
26258                 if(k == e.TAB){
26259                     e.stopEvent();
26260                     this.win.focus();
26261                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26262                     this.deferFocus();
26263                 }
26264                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26265                     this.cleanUpPaste.defer(100, this);
26266                     return;
26267                 }
26268                 
26269             };
26270         }else if(Roo.isSafari){
26271             return function(e){
26272                 var k = e.getKey();
26273                 
26274                 if(k == e.TAB){
26275                     e.stopEvent();
26276                     this.execCmd('InsertText','\t');
26277                     this.deferFocus();
26278                     return;
26279                 }
26280                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26281                     this.cleanUpPaste.defer(100, this);
26282                     return;
26283                 }
26284                 
26285              };
26286         }
26287     }(),
26288     
26289     getAllAncestors: function()
26290     {
26291         var p = this.getSelectedNode();
26292         var a = [];
26293         if (!p) {
26294             a.push(p); // push blank onto stack..
26295             p = this.getParentElement();
26296         }
26297         
26298         
26299         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26300             a.push(p);
26301             p = p.parentNode;
26302         }
26303         a.push(this.doc.body);
26304         return a;
26305     },
26306     lastSel : false,
26307     lastSelNode : false,
26308     
26309     
26310     getSelection : function() 
26311     {
26312         this.assignDocWin();
26313         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26314     },
26315     
26316     getSelectedNode: function() 
26317     {
26318         // this may only work on Gecko!!!
26319         
26320         // should we cache this!!!!
26321         
26322         
26323         
26324          
26325         var range = this.createRange(this.getSelection()).cloneRange();
26326         
26327         if (Roo.isIE) {
26328             var parent = range.parentElement();
26329             while (true) {
26330                 var testRange = range.duplicate();
26331                 testRange.moveToElementText(parent);
26332                 if (testRange.inRange(range)) {
26333                     break;
26334                 }
26335                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26336                     break;
26337                 }
26338                 parent = parent.parentElement;
26339             }
26340             return parent;
26341         }
26342         
26343         // is ancestor a text element.
26344         var ac =  range.commonAncestorContainer;
26345         if (ac.nodeType == 3) {
26346             ac = ac.parentNode;
26347         }
26348         
26349         var ar = ac.childNodes;
26350          
26351         var nodes = [];
26352         var other_nodes = [];
26353         var has_other_nodes = false;
26354         for (var i=0;i<ar.length;i++) {
26355             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26356                 continue;
26357             }
26358             // fullly contained node.
26359             
26360             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26361                 nodes.push(ar[i]);
26362                 continue;
26363             }
26364             
26365             // probably selected..
26366             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26367                 other_nodes.push(ar[i]);
26368                 continue;
26369             }
26370             // outer..
26371             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26372                 continue;
26373             }
26374             
26375             
26376             has_other_nodes = true;
26377         }
26378         if (!nodes.length && other_nodes.length) {
26379             nodes= other_nodes;
26380         }
26381         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26382             return false;
26383         }
26384         
26385         return nodes[0];
26386     },
26387     createRange: function(sel)
26388     {
26389         // this has strange effects when using with 
26390         // top toolbar - not sure if it's a great idea.
26391         //this.editor.contentWindow.focus();
26392         if (typeof sel != "undefined") {
26393             try {
26394                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26395             } catch(e) {
26396                 return this.doc.createRange();
26397             }
26398         } else {
26399             return this.doc.createRange();
26400         }
26401     },
26402     getParentElement: function()
26403     {
26404         
26405         this.assignDocWin();
26406         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26407         
26408         var range = this.createRange(sel);
26409          
26410         try {
26411             var p = range.commonAncestorContainer;
26412             while (p.nodeType == 3) { // text node
26413                 p = p.parentNode;
26414             }
26415             return p;
26416         } catch (e) {
26417             return null;
26418         }
26419     
26420     },
26421     /***
26422      *
26423      * Range intersection.. the hard stuff...
26424      *  '-1' = before
26425      *  '0' = hits..
26426      *  '1' = after.
26427      *         [ -- selected range --- ]
26428      *   [fail]                        [fail]
26429      *
26430      *    basically..
26431      *      if end is before start or  hits it. fail.
26432      *      if start is after end or hits it fail.
26433      *
26434      *   if either hits (but other is outside. - then it's not 
26435      *   
26436      *    
26437      **/
26438     
26439     
26440     // @see http://www.thismuchiknow.co.uk/?p=64.
26441     rangeIntersectsNode : function(range, node)
26442     {
26443         var nodeRange = node.ownerDocument.createRange();
26444         try {
26445             nodeRange.selectNode(node);
26446         } catch (e) {
26447             nodeRange.selectNodeContents(node);
26448         }
26449     
26450         var rangeStartRange = range.cloneRange();
26451         rangeStartRange.collapse(true);
26452     
26453         var rangeEndRange = range.cloneRange();
26454         rangeEndRange.collapse(false);
26455     
26456         var nodeStartRange = nodeRange.cloneRange();
26457         nodeStartRange.collapse(true);
26458     
26459         var nodeEndRange = nodeRange.cloneRange();
26460         nodeEndRange.collapse(false);
26461     
26462         return rangeStartRange.compareBoundaryPoints(
26463                  Range.START_TO_START, nodeEndRange) == -1 &&
26464                rangeEndRange.compareBoundaryPoints(
26465                  Range.START_TO_START, nodeStartRange) == 1;
26466         
26467          
26468     },
26469     rangeCompareNode : function(range, node)
26470     {
26471         var nodeRange = node.ownerDocument.createRange();
26472         try {
26473             nodeRange.selectNode(node);
26474         } catch (e) {
26475             nodeRange.selectNodeContents(node);
26476         }
26477         
26478         
26479         range.collapse(true);
26480     
26481         nodeRange.collapse(true);
26482      
26483         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26484         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26485          
26486         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26487         
26488         var nodeIsBefore   =  ss == 1;
26489         var nodeIsAfter    = ee == -1;
26490         
26491         if (nodeIsBefore && nodeIsAfter) {
26492             return 0; // outer
26493         }
26494         if (!nodeIsBefore && nodeIsAfter) {
26495             return 1; //right trailed.
26496         }
26497         
26498         if (nodeIsBefore && !nodeIsAfter) {
26499             return 2;  // left trailed.
26500         }
26501         // fully contined.
26502         return 3;
26503     },
26504
26505     // private? - in a new class?
26506     cleanUpPaste :  function()
26507     {
26508         // cleans up the whole document..
26509         Roo.log('cleanuppaste');
26510         
26511         this.cleanUpChildren(this.doc.body);
26512         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26513         if (clean != this.doc.body.innerHTML) {
26514             this.doc.body.innerHTML = clean;
26515         }
26516         
26517     },
26518     
26519     cleanWordChars : function(input) {// change the chars to hex code
26520         var he = Roo.HtmlEditorCore;
26521         
26522         var output = input;
26523         Roo.each(he.swapCodes, function(sw) { 
26524             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26525             
26526             output = output.replace(swapper, sw[1]);
26527         });
26528         
26529         return output;
26530     },
26531     
26532     
26533     cleanUpChildren : function (n)
26534     {
26535         if (!n.childNodes.length) {
26536             return;
26537         }
26538         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26539            this.cleanUpChild(n.childNodes[i]);
26540         }
26541     },
26542     
26543     
26544         
26545     
26546     cleanUpChild : function (node)
26547     {
26548         var ed = this;
26549         //console.log(node);
26550         if (node.nodeName == "#text") {
26551             // clean up silly Windows -- stuff?
26552             return; 
26553         }
26554         if (node.nodeName == "#comment") {
26555             if (!this.allowComments) {
26556                 node.parentNode.removeChild(node);
26557             }
26558             // clean up silly Windows -- stuff?
26559             return; 
26560         }
26561         var lcname = node.tagName.toLowerCase();
26562         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26563         // whitelist of tags..
26564         
26565         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26566             // remove node.
26567             node.parentNode.removeChild(node);
26568             return;
26569             
26570         }
26571         
26572         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26573         
26574         // spans with no attributes - just remove them..
26575         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26576             remove_keep_children = true;
26577         }
26578         
26579         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26580         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26581         
26582         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26583         //    remove_keep_children = true;
26584         //}
26585         
26586         if (remove_keep_children) {
26587             this.cleanUpChildren(node);
26588             // inserts everything just before this node...
26589             while (node.childNodes.length) {
26590                 var cn = node.childNodes[0];
26591                 node.removeChild(cn);
26592                 node.parentNode.insertBefore(cn, node);
26593             }
26594             node.parentNode.removeChild(node);
26595             return;
26596         }
26597         
26598         if (!node.attributes || !node.attributes.length) {
26599             
26600           
26601             
26602             
26603             this.cleanUpChildren(node);
26604             return;
26605         }
26606         
26607         function cleanAttr(n,v)
26608         {
26609             
26610             if (v.match(/^\./) || v.match(/^\//)) {
26611                 return;
26612             }
26613             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26614                 return;
26615             }
26616             if (v.match(/^#/)) {
26617                 return;
26618             }
26619             if (v.match(/^\{/)) { // allow template editing.
26620                 return;
26621             }
26622 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26623             node.removeAttribute(n);
26624             
26625         }
26626         
26627         var cwhite = this.cwhite;
26628         var cblack = this.cblack;
26629             
26630         function cleanStyle(n,v)
26631         {
26632             if (v.match(/expression/)) { //XSS?? should we even bother..
26633                 node.removeAttribute(n);
26634                 return;
26635             }
26636             
26637             var parts = v.split(/;/);
26638             var clean = [];
26639             
26640             Roo.each(parts, function(p) {
26641                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26642                 if (!p.length) {
26643                     return true;
26644                 }
26645                 var l = p.split(':').shift().replace(/\s+/g,'');
26646                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26647                 
26648                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26649 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26650                     //node.removeAttribute(n);
26651                     return true;
26652                 }
26653                 //Roo.log()
26654                 // only allow 'c whitelisted system attributes'
26655                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26656 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26657                     //node.removeAttribute(n);
26658                     return true;
26659                 }
26660                 
26661                 
26662                  
26663                 
26664                 clean.push(p);
26665                 return true;
26666             });
26667             if (clean.length) { 
26668                 node.setAttribute(n, clean.join(';'));
26669             } else {
26670                 node.removeAttribute(n);
26671             }
26672             
26673         }
26674         
26675         
26676         for (var i = node.attributes.length-1; i > -1 ; i--) {
26677             var a = node.attributes[i];
26678             //console.log(a);
26679             
26680             if (a.name.toLowerCase().substr(0,2)=='on')  {
26681                 node.removeAttribute(a.name);
26682                 continue;
26683             }
26684             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26685                 node.removeAttribute(a.name);
26686                 continue;
26687             }
26688             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26689                 cleanAttr(a.name,a.value); // fixme..
26690                 continue;
26691             }
26692             if (a.name == 'style') {
26693                 cleanStyle(a.name,a.value);
26694                 continue;
26695             }
26696             /// clean up MS crap..
26697             // tecnically this should be a list of valid class'es..
26698             
26699             
26700             if (a.name == 'class') {
26701                 if (a.value.match(/^Mso/)) {
26702                     node.removeAttribute('class');
26703                 }
26704                 
26705                 if (a.value.match(/^body$/)) {
26706                     node.removeAttribute('class');
26707                 }
26708                 continue;
26709             }
26710             
26711             // style cleanup!?
26712             // class cleanup?
26713             
26714         }
26715         
26716         
26717         this.cleanUpChildren(node);
26718         
26719         
26720     },
26721     
26722     /**
26723      * Clean up MS wordisms...
26724      */
26725     cleanWord : function(node)
26726     {
26727         if (!node) {
26728             this.cleanWord(this.doc.body);
26729             return;
26730         }
26731         
26732         if(
26733                 node.nodeName == 'SPAN' &&
26734                 !node.hasAttributes() &&
26735                 node.childNodes.length == 1 &&
26736                 node.firstChild.nodeName == "#text"  
26737         ) {
26738             var textNode = node.firstChild;
26739             node.removeChild(textNode);
26740             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26741                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26742             }
26743             node.parentNode.insertBefore(textNode, node);
26744             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26745                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26746             }
26747             node.parentNode.removeChild(node);
26748         }
26749         
26750         if (node.nodeName == "#text") {
26751             // clean up silly Windows -- stuff?
26752             return; 
26753         }
26754         if (node.nodeName == "#comment") {
26755             node.parentNode.removeChild(node);
26756             // clean up silly Windows -- stuff?
26757             return; 
26758         }
26759         
26760         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26761             node.parentNode.removeChild(node);
26762             return;
26763         }
26764         //Roo.log(node.tagName);
26765         // remove - but keep children..
26766         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26767             //Roo.log('-- removed');
26768             while (node.childNodes.length) {
26769                 var cn = node.childNodes[0];
26770                 node.removeChild(cn);
26771                 node.parentNode.insertBefore(cn, node);
26772                 // move node to parent - and clean it..
26773                 this.cleanWord(cn);
26774             }
26775             node.parentNode.removeChild(node);
26776             /// no need to iterate chidlren = it's got none..
26777             //this.iterateChildren(node, this.cleanWord);
26778             return;
26779         }
26780         // clean styles
26781         if (node.className.length) {
26782             
26783             var cn = node.className.split(/\W+/);
26784             var cna = [];
26785             Roo.each(cn, function(cls) {
26786                 if (cls.match(/Mso[a-zA-Z]+/)) {
26787                     return;
26788                 }
26789                 cna.push(cls);
26790             });
26791             node.className = cna.length ? cna.join(' ') : '';
26792             if (!cna.length) {
26793                 node.removeAttribute("class");
26794             }
26795         }
26796         
26797         if (node.hasAttribute("lang")) {
26798             node.removeAttribute("lang");
26799         }
26800         
26801         if (node.hasAttribute("style")) {
26802             
26803             var styles = node.getAttribute("style").split(";");
26804             var nstyle = [];
26805             Roo.each(styles, function(s) {
26806                 if (!s.match(/:/)) {
26807                     return;
26808                 }
26809                 var kv = s.split(":");
26810                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26811                     return;
26812                 }
26813                 // what ever is left... we allow.
26814                 nstyle.push(s);
26815             });
26816             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26817             if (!nstyle.length) {
26818                 node.removeAttribute('style');
26819             }
26820         }
26821         this.iterateChildren(node, this.cleanWord);
26822         
26823         
26824         
26825     },
26826     /**
26827      * iterateChildren of a Node, calling fn each time, using this as the scole..
26828      * @param {DomNode} node node to iterate children of.
26829      * @param {Function} fn method of this class to call on each item.
26830      */
26831     iterateChildren : function(node, fn)
26832     {
26833         if (!node.childNodes.length) {
26834                 return;
26835         }
26836         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26837            fn.call(this, node.childNodes[i])
26838         }
26839     },
26840     
26841     
26842     /**
26843      * cleanTableWidths.
26844      *
26845      * Quite often pasting from word etc.. results in tables with column and widths.
26846      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26847      *
26848      */
26849     cleanTableWidths : function(node)
26850     {
26851          
26852          
26853         if (!node) {
26854             this.cleanTableWidths(this.doc.body);
26855             return;
26856         }
26857         
26858         // ignore list...
26859         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26860             return; 
26861         }
26862         Roo.log(node.tagName);
26863         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26864             this.iterateChildren(node, this.cleanTableWidths);
26865             return;
26866         }
26867         if (node.hasAttribute('width')) {
26868             node.removeAttribute('width');
26869         }
26870         
26871          
26872         if (node.hasAttribute("style")) {
26873             // pretty basic...
26874             
26875             var styles = node.getAttribute("style").split(";");
26876             var nstyle = [];
26877             Roo.each(styles, function(s) {
26878                 if (!s.match(/:/)) {
26879                     return;
26880                 }
26881                 var kv = s.split(":");
26882                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26883                     return;
26884                 }
26885                 // what ever is left... we allow.
26886                 nstyle.push(s);
26887             });
26888             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26889             if (!nstyle.length) {
26890                 node.removeAttribute('style');
26891             }
26892         }
26893         
26894         this.iterateChildren(node, this.cleanTableWidths);
26895         
26896         
26897     },
26898     
26899     
26900     
26901     
26902     domToHTML : function(currentElement, depth, nopadtext) {
26903         
26904         depth = depth || 0;
26905         nopadtext = nopadtext || false;
26906     
26907         if (!currentElement) {
26908             return this.domToHTML(this.doc.body);
26909         }
26910         
26911         //Roo.log(currentElement);
26912         var j;
26913         var allText = false;
26914         var nodeName = currentElement.nodeName;
26915         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26916         
26917         if  (nodeName == '#text') {
26918             
26919             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26920         }
26921         
26922         
26923         var ret = '';
26924         if (nodeName != 'BODY') {
26925              
26926             var i = 0;
26927             // Prints the node tagName, such as <A>, <IMG>, etc
26928             if (tagName) {
26929                 var attr = [];
26930                 for(i = 0; i < currentElement.attributes.length;i++) {
26931                     // quoting?
26932                     var aname = currentElement.attributes.item(i).name;
26933                     if (!currentElement.attributes.item(i).value.length) {
26934                         continue;
26935                     }
26936                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26937                 }
26938                 
26939                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26940             } 
26941             else {
26942                 
26943                 // eack
26944             }
26945         } else {
26946             tagName = false;
26947         }
26948         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26949             return ret;
26950         }
26951         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26952             nopadtext = true;
26953         }
26954         
26955         
26956         // Traverse the tree
26957         i = 0;
26958         var currentElementChild = currentElement.childNodes.item(i);
26959         var allText = true;
26960         var innerHTML  = '';
26961         lastnode = '';
26962         while (currentElementChild) {
26963             // Formatting code (indent the tree so it looks nice on the screen)
26964             var nopad = nopadtext;
26965             if (lastnode == 'SPAN') {
26966                 nopad  = true;
26967             }
26968             // text
26969             if  (currentElementChild.nodeName == '#text') {
26970                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26971                 toadd = nopadtext ? toadd : toadd.trim();
26972                 if (!nopad && toadd.length > 80) {
26973                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26974                 }
26975                 innerHTML  += toadd;
26976                 
26977                 i++;
26978                 currentElementChild = currentElement.childNodes.item(i);
26979                 lastNode = '';
26980                 continue;
26981             }
26982             allText = false;
26983             
26984             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26985                 
26986             // Recursively traverse the tree structure of the child node
26987             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26988             lastnode = currentElementChild.nodeName;
26989             i++;
26990             currentElementChild=currentElement.childNodes.item(i);
26991         }
26992         
26993         ret += innerHTML;
26994         
26995         if (!allText) {
26996                 // The remaining code is mostly for formatting the tree
26997             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26998         }
26999         
27000         
27001         if (tagName) {
27002             ret+= "</"+tagName+">";
27003         }
27004         return ret;
27005         
27006     },
27007         
27008     applyBlacklists : function()
27009     {
27010         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27011         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27012         
27013         this.white = [];
27014         this.black = [];
27015         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27016             if (b.indexOf(tag) > -1) {
27017                 return;
27018             }
27019             this.white.push(tag);
27020             
27021         }, this);
27022         
27023         Roo.each(w, function(tag) {
27024             if (b.indexOf(tag) > -1) {
27025                 return;
27026             }
27027             if (this.white.indexOf(tag) > -1) {
27028                 return;
27029             }
27030             this.white.push(tag);
27031             
27032         }, this);
27033         
27034         
27035         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27036             if (w.indexOf(tag) > -1) {
27037                 return;
27038             }
27039             this.black.push(tag);
27040             
27041         }, this);
27042         
27043         Roo.each(b, function(tag) {
27044             if (w.indexOf(tag) > -1) {
27045                 return;
27046             }
27047             if (this.black.indexOf(tag) > -1) {
27048                 return;
27049             }
27050             this.black.push(tag);
27051             
27052         }, this);
27053         
27054         
27055         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27056         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27057         
27058         this.cwhite = [];
27059         this.cblack = [];
27060         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27061             if (b.indexOf(tag) > -1) {
27062                 return;
27063             }
27064             this.cwhite.push(tag);
27065             
27066         }, this);
27067         
27068         Roo.each(w, function(tag) {
27069             if (b.indexOf(tag) > -1) {
27070                 return;
27071             }
27072             if (this.cwhite.indexOf(tag) > -1) {
27073                 return;
27074             }
27075             this.cwhite.push(tag);
27076             
27077         }, this);
27078         
27079         
27080         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27081             if (w.indexOf(tag) > -1) {
27082                 return;
27083             }
27084             this.cblack.push(tag);
27085             
27086         }, this);
27087         
27088         Roo.each(b, function(tag) {
27089             if (w.indexOf(tag) > -1) {
27090                 return;
27091             }
27092             if (this.cblack.indexOf(tag) > -1) {
27093                 return;
27094             }
27095             this.cblack.push(tag);
27096             
27097         }, this);
27098     },
27099     
27100     setStylesheets : function(stylesheets)
27101     {
27102         if(typeof(stylesheets) == 'string'){
27103             Roo.get(this.iframe.contentDocument.head).createChild({
27104                 tag : 'link',
27105                 rel : 'stylesheet',
27106                 type : 'text/css',
27107                 href : stylesheets
27108             });
27109             
27110             return;
27111         }
27112         var _this = this;
27113      
27114         Roo.each(stylesheets, function(s) {
27115             if(!s.length){
27116                 return;
27117             }
27118             
27119             Roo.get(_this.iframe.contentDocument.head).createChild({
27120                 tag : 'link',
27121                 rel : 'stylesheet',
27122                 type : 'text/css',
27123                 href : s
27124             });
27125         });
27126
27127         
27128     },
27129     
27130     removeStylesheets : function()
27131     {
27132         var _this = this;
27133         
27134         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27135             s.remove();
27136         });
27137     },
27138     
27139     setStyle : function(style)
27140     {
27141         Roo.get(this.iframe.contentDocument.head).createChild({
27142             tag : 'style',
27143             type : 'text/css',
27144             html : style
27145         });
27146
27147         return;
27148     }
27149     
27150     // hide stuff that is not compatible
27151     /**
27152      * @event blur
27153      * @hide
27154      */
27155     /**
27156      * @event change
27157      * @hide
27158      */
27159     /**
27160      * @event focus
27161      * @hide
27162      */
27163     /**
27164      * @event specialkey
27165      * @hide
27166      */
27167     /**
27168      * @cfg {String} fieldClass @hide
27169      */
27170     /**
27171      * @cfg {String} focusClass @hide
27172      */
27173     /**
27174      * @cfg {String} autoCreate @hide
27175      */
27176     /**
27177      * @cfg {String} inputType @hide
27178      */
27179     /**
27180      * @cfg {String} invalidClass @hide
27181      */
27182     /**
27183      * @cfg {String} invalidText @hide
27184      */
27185     /**
27186      * @cfg {String} msgFx @hide
27187      */
27188     /**
27189      * @cfg {String} validateOnBlur @hide
27190      */
27191 });
27192
27193 Roo.HtmlEditorCore.white = [
27194         'area', 'br', 'img', 'input', 'hr', 'wbr',
27195         
27196        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27197        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27198        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27199        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27200        'table',   'ul',         'xmp', 
27201        
27202        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27203       'thead',   'tr', 
27204      
27205       'dir', 'menu', 'ol', 'ul', 'dl',
27206        
27207       'embed',  'object'
27208 ];
27209
27210
27211 Roo.HtmlEditorCore.black = [
27212     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27213         'applet', // 
27214         'base',   'basefont', 'bgsound', 'blink',  'body', 
27215         'frame',  'frameset', 'head',    'html',   'ilayer', 
27216         'iframe', 'layer',  'link',     'meta',    'object',   
27217         'script', 'style' ,'title',  'xml' // clean later..
27218 ];
27219 Roo.HtmlEditorCore.clean = [
27220     'script', 'style', 'title', 'xml'
27221 ];
27222 Roo.HtmlEditorCore.remove = [
27223     'font'
27224 ];
27225 // attributes..
27226
27227 Roo.HtmlEditorCore.ablack = [
27228     'on'
27229 ];
27230     
27231 Roo.HtmlEditorCore.aclean = [ 
27232     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27233 ];
27234
27235 // protocols..
27236 Roo.HtmlEditorCore.pwhite= [
27237         'http',  'https',  'mailto'
27238 ];
27239
27240 // white listed style attributes.
27241 Roo.HtmlEditorCore.cwhite= [
27242       //  'text-align', /// default is to allow most things..
27243       
27244          
27245 //        'font-size'//??
27246 ];
27247
27248 // black listed style attributes.
27249 Roo.HtmlEditorCore.cblack= [
27250       //  'font-size' -- this can be set by the project 
27251 ];
27252
27253
27254 Roo.HtmlEditorCore.swapCodes   =[ 
27255     [    8211, "&#8211;" ], 
27256     [    8212, "&#8212;" ], 
27257     [    8216,  "'" ],  
27258     [    8217, "'" ],  
27259     [    8220, '"' ],  
27260     [    8221, '"' ],  
27261     [    8226, "*" ],  
27262     [    8230, "..." ]
27263 ]; 
27264
27265     /*
27266  * - LGPL
27267  *
27268  * HtmlEditor
27269  * 
27270  */
27271
27272 /**
27273  * @class Roo.bootstrap.HtmlEditor
27274  * @extends Roo.bootstrap.TextArea
27275  * Bootstrap HtmlEditor class
27276
27277  * @constructor
27278  * Create a new HtmlEditor
27279  * @param {Object} config The config object
27280  */
27281
27282 Roo.bootstrap.HtmlEditor = function(config){
27283     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27284     if (!this.toolbars) {
27285         this.toolbars = [];
27286     }
27287     
27288     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27289     this.addEvents({
27290             /**
27291              * @event initialize
27292              * Fires when the editor is fully initialized (including the iframe)
27293              * @param {HtmlEditor} this
27294              */
27295             initialize: true,
27296             /**
27297              * @event activate
27298              * Fires when the editor is first receives the focus. Any insertion must wait
27299              * until after this event.
27300              * @param {HtmlEditor} this
27301              */
27302             activate: true,
27303              /**
27304              * @event beforesync
27305              * Fires before the textarea is updated with content from the editor iframe. Return false
27306              * to cancel the sync.
27307              * @param {HtmlEditor} this
27308              * @param {String} html
27309              */
27310             beforesync: true,
27311              /**
27312              * @event beforepush
27313              * Fires before the iframe editor is updated with content from the textarea. Return false
27314              * to cancel the push.
27315              * @param {HtmlEditor} this
27316              * @param {String} html
27317              */
27318             beforepush: true,
27319              /**
27320              * @event sync
27321              * Fires when the textarea is updated with content from the editor iframe.
27322              * @param {HtmlEditor} this
27323              * @param {String} html
27324              */
27325             sync: true,
27326              /**
27327              * @event push
27328              * Fires when the iframe editor is updated with content from the textarea.
27329              * @param {HtmlEditor} this
27330              * @param {String} html
27331              */
27332             push: true,
27333              /**
27334              * @event editmodechange
27335              * Fires when the editor switches edit modes
27336              * @param {HtmlEditor} this
27337              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27338              */
27339             editmodechange: true,
27340             /**
27341              * @event editorevent
27342              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27343              * @param {HtmlEditor} this
27344              */
27345             editorevent: true,
27346             /**
27347              * @event firstfocus
27348              * Fires when on first focus - needed by toolbars..
27349              * @param {HtmlEditor} this
27350              */
27351             firstfocus: true,
27352             /**
27353              * @event autosave
27354              * Auto save the htmlEditor value as a file into Events
27355              * @param {HtmlEditor} this
27356              */
27357             autosave: true,
27358             /**
27359              * @event savedpreview
27360              * preview the saved version of htmlEditor
27361              * @param {HtmlEditor} this
27362              */
27363             savedpreview: true
27364         });
27365 };
27366
27367
27368 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27369     
27370     
27371       /**
27372      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27373      */
27374     toolbars : false,
27375     
27376      /**
27377     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27378     */
27379     btns : [],
27380    
27381      /**
27382      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27383      *                        Roo.resizable.
27384      */
27385     resizable : false,
27386      /**
27387      * @cfg {Number} height (in pixels)
27388      */   
27389     height: 300,
27390    /**
27391      * @cfg {Number} width (in pixels)
27392      */   
27393     width: false,
27394     
27395     /**
27396      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27397      * 
27398      */
27399     stylesheets: false,
27400     
27401     // id of frame..
27402     frameId: false,
27403     
27404     // private properties
27405     validationEvent : false,
27406     deferHeight: true,
27407     initialized : false,
27408     activated : false,
27409     
27410     onFocus : Roo.emptyFn,
27411     iframePad:3,
27412     hideMode:'offsets',
27413     
27414     tbContainer : false,
27415     
27416     bodyCls : '',
27417     
27418     toolbarContainer :function() {
27419         return this.wrap.select('.x-html-editor-tb',true).first();
27420     },
27421
27422     /**
27423      * Protected method that will not generally be called directly. It
27424      * is called when the editor creates its toolbar. Override this method if you need to
27425      * add custom toolbar buttons.
27426      * @param {HtmlEditor} editor
27427      */
27428     createToolbar : function(){
27429         Roo.log('renewing');
27430         Roo.log("create toolbars");
27431         
27432         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27433         this.toolbars[0].render(this.toolbarContainer());
27434         
27435         return;
27436         
27437 //        if (!editor.toolbars || !editor.toolbars.length) {
27438 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27439 //        }
27440 //        
27441 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27442 //            editor.toolbars[i] = Roo.factory(
27443 //                    typeof(editor.toolbars[i]) == 'string' ?
27444 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27445 //                Roo.bootstrap.HtmlEditor);
27446 //            editor.toolbars[i].init(editor);
27447 //        }
27448     },
27449
27450      
27451     // private
27452     onRender : function(ct, position)
27453     {
27454        // Roo.log("Call onRender: " + this.xtype);
27455         var _t = this;
27456         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27457       
27458         this.wrap = this.inputEl().wrap({
27459             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27460         });
27461         
27462         this.editorcore.onRender(ct, position);
27463          
27464         if (this.resizable) {
27465             this.resizeEl = new Roo.Resizable(this.wrap, {
27466                 pinned : true,
27467                 wrap: true,
27468                 dynamic : true,
27469                 minHeight : this.height,
27470                 height: this.height,
27471                 handles : this.resizable,
27472                 width: this.width,
27473                 listeners : {
27474                     resize : function(r, w, h) {
27475                         _t.onResize(w,h); // -something
27476                     }
27477                 }
27478             });
27479             
27480         }
27481         this.createToolbar(this);
27482        
27483         
27484         if(!this.width && this.resizable){
27485             this.setSize(this.wrap.getSize());
27486         }
27487         if (this.resizeEl) {
27488             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27489             // should trigger onReize..
27490         }
27491         
27492     },
27493
27494     // private
27495     onResize : function(w, h)
27496     {
27497         Roo.log('resize: ' +w + ',' + h );
27498         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27499         var ew = false;
27500         var eh = false;
27501         
27502         if(this.inputEl() ){
27503             if(typeof w == 'number'){
27504                 var aw = w - this.wrap.getFrameWidth('lr');
27505                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27506                 ew = aw;
27507             }
27508             if(typeof h == 'number'){
27509                  var tbh = -11;  // fixme it needs to tool bar size!
27510                 for (var i =0; i < this.toolbars.length;i++) {
27511                     // fixme - ask toolbars for heights?
27512                     tbh += this.toolbars[i].el.getHeight();
27513                     //if (this.toolbars[i].footer) {
27514                     //    tbh += this.toolbars[i].footer.el.getHeight();
27515                     //}
27516                 }
27517               
27518                 
27519                 
27520                 
27521                 
27522                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27523                 ah -= 5; // knock a few pixes off for look..
27524                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27525                 var eh = ah;
27526             }
27527         }
27528         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27529         this.editorcore.onResize(ew,eh);
27530         
27531     },
27532
27533     /**
27534      * Toggles the editor between standard and source edit mode.
27535      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27536      */
27537     toggleSourceEdit : function(sourceEditMode)
27538     {
27539         this.editorcore.toggleSourceEdit(sourceEditMode);
27540         
27541         if(this.editorcore.sourceEditMode){
27542             Roo.log('editor - showing textarea');
27543             
27544 //            Roo.log('in');
27545 //            Roo.log(this.syncValue());
27546             this.syncValue();
27547             this.inputEl().removeClass(['hide', 'x-hidden']);
27548             this.inputEl().dom.removeAttribute('tabIndex');
27549             this.inputEl().focus();
27550         }else{
27551             Roo.log('editor - hiding textarea');
27552 //            Roo.log('out')
27553 //            Roo.log(this.pushValue()); 
27554             this.pushValue();
27555             
27556             this.inputEl().addClass(['hide', 'x-hidden']);
27557             this.inputEl().dom.setAttribute('tabIndex', -1);
27558             //this.deferFocus();
27559         }
27560          
27561         if(this.resizable){
27562             this.setSize(this.wrap.getSize());
27563         }
27564         
27565         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27566     },
27567  
27568     // private (for BoxComponent)
27569     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27570
27571     // private (for BoxComponent)
27572     getResizeEl : function(){
27573         return this.wrap;
27574     },
27575
27576     // private (for BoxComponent)
27577     getPositionEl : function(){
27578         return this.wrap;
27579     },
27580
27581     // private
27582     initEvents : function(){
27583         this.originalValue = this.getValue();
27584     },
27585
27586 //    /**
27587 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27588 //     * @method
27589 //     */
27590 //    markInvalid : Roo.emptyFn,
27591 //    /**
27592 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27593 //     * @method
27594 //     */
27595 //    clearInvalid : Roo.emptyFn,
27596
27597     setValue : function(v){
27598         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27599         this.editorcore.pushValue();
27600     },
27601
27602      
27603     // private
27604     deferFocus : function(){
27605         this.focus.defer(10, this);
27606     },
27607
27608     // doc'ed in Field
27609     focus : function(){
27610         this.editorcore.focus();
27611         
27612     },
27613       
27614
27615     // private
27616     onDestroy : function(){
27617         
27618         
27619         
27620         if(this.rendered){
27621             
27622             for (var i =0; i < this.toolbars.length;i++) {
27623                 // fixme - ask toolbars for heights?
27624                 this.toolbars[i].onDestroy();
27625             }
27626             
27627             this.wrap.dom.innerHTML = '';
27628             this.wrap.remove();
27629         }
27630     },
27631
27632     // private
27633     onFirstFocus : function(){
27634         //Roo.log("onFirstFocus");
27635         this.editorcore.onFirstFocus();
27636          for (var i =0; i < this.toolbars.length;i++) {
27637             this.toolbars[i].onFirstFocus();
27638         }
27639         
27640     },
27641     
27642     // private
27643     syncValue : function()
27644     {   
27645         this.editorcore.syncValue();
27646     },
27647     
27648     pushValue : function()
27649     {   
27650         this.editorcore.pushValue();
27651     }
27652      
27653     
27654     // hide stuff that is not compatible
27655     /**
27656      * @event blur
27657      * @hide
27658      */
27659     /**
27660      * @event change
27661      * @hide
27662      */
27663     /**
27664      * @event focus
27665      * @hide
27666      */
27667     /**
27668      * @event specialkey
27669      * @hide
27670      */
27671     /**
27672      * @cfg {String} fieldClass @hide
27673      */
27674     /**
27675      * @cfg {String} focusClass @hide
27676      */
27677     /**
27678      * @cfg {String} autoCreate @hide
27679      */
27680     /**
27681      * @cfg {String} inputType @hide
27682      */
27683      
27684     /**
27685      * @cfg {String} invalidText @hide
27686      */
27687     /**
27688      * @cfg {String} msgFx @hide
27689      */
27690     /**
27691      * @cfg {String} validateOnBlur @hide
27692      */
27693 });
27694  
27695     
27696    
27697    
27698    
27699       
27700 Roo.namespace('Roo.bootstrap.htmleditor');
27701 /**
27702  * @class Roo.bootstrap.HtmlEditorToolbar1
27703  * Basic Toolbar
27704  * 
27705  * @example
27706  * Usage:
27707  *
27708  new Roo.bootstrap.HtmlEditor({
27709     ....
27710     toolbars : [
27711         new Roo.bootstrap.HtmlEditorToolbar1({
27712             disable : { fonts: 1 , format: 1, ..., ... , ...],
27713             btns : [ .... ]
27714         })
27715     }
27716      
27717  * 
27718  * @cfg {Object} disable List of elements to disable..
27719  * @cfg {Array} btns List of additional buttons.
27720  * 
27721  * 
27722  * NEEDS Extra CSS? 
27723  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27724  */
27725  
27726 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27727 {
27728     
27729     Roo.apply(this, config);
27730     
27731     // default disabled, based on 'good practice'..
27732     this.disable = this.disable || {};
27733     Roo.applyIf(this.disable, {
27734         fontSize : true,
27735         colors : true,
27736         specialElements : true
27737     });
27738     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27739     
27740     this.editor = config.editor;
27741     this.editorcore = config.editor.editorcore;
27742     
27743     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27744     
27745     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27746     // dont call parent... till later.
27747 }
27748 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27749      
27750     bar : true,
27751     
27752     editor : false,
27753     editorcore : false,
27754     
27755     
27756     formats : [
27757         "p" ,  
27758         "h1","h2","h3","h4","h5","h6", 
27759         "pre", "code", 
27760         "abbr", "acronym", "address", "cite", "samp", "var",
27761         'div','span'
27762     ],
27763     
27764     onRender : function(ct, position)
27765     {
27766        // Roo.log("Call onRender: " + this.xtype);
27767         
27768        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27769        Roo.log(this.el);
27770        this.el.dom.style.marginBottom = '0';
27771        var _this = this;
27772        var editorcore = this.editorcore;
27773        var editor= this.editor;
27774        
27775        var children = [];
27776        var btn = function(id,cmd , toggle, handler, html){
27777        
27778             var  event = toggle ? 'toggle' : 'click';
27779        
27780             var a = {
27781                 size : 'sm',
27782                 xtype: 'Button',
27783                 xns: Roo.bootstrap,
27784                 //glyphicon : id,
27785                 fa: id,
27786                 cmd : id || cmd,
27787                 enableToggle:toggle !== false,
27788                 html : html || '',
27789                 pressed : toggle ? false : null,
27790                 listeners : {}
27791             };
27792             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27793                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27794             };
27795             children.push(a);
27796             return a;
27797        }
27798        
27799     //    var cb_box = function...
27800         
27801         var style = {
27802                 xtype: 'Button',
27803                 size : 'sm',
27804                 xns: Roo.bootstrap,
27805                 fa : 'font',
27806                 //html : 'submit'
27807                 menu : {
27808                     xtype: 'Menu',
27809                     xns: Roo.bootstrap,
27810                     items:  []
27811                 }
27812         };
27813         Roo.each(this.formats, function(f) {
27814             style.menu.items.push({
27815                 xtype :'MenuItem',
27816                 xns: Roo.bootstrap,
27817                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27818                 tagname : f,
27819                 listeners : {
27820                     click : function()
27821                     {
27822                         editorcore.insertTag(this.tagname);
27823                         editor.focus();
27824                     }
27825                 }
27826                 
27827             });
27828         });
27829         children.push(style);   
27830         
27831         btn('bold',false,true);
27832         btn('italic',false,true);
27833         btn('align-left', 'justifyleft',true);
27834         btn('align-center', 'justifycenter',true);
27835         btn('align-right' , 'justifyright',true);
27836         btn('link', false, false, function(btn) {
27837             //Roo.log("create link?");
27838             var url = prompt(this.createLinkText, this.defaultLinkValue);
27839             if(url && url != 'http:/'+'/'){
27840                 this.editorcore.relayCmd('createlink', url);
27841             }
27842         }),
27843         btn('list','insertunorderedlist',true);
27844         btn('pencil', false,true, function(btn){
27845                 Roo.log(this);
27846                 this.toggleSourceEdit(btn.pressed);
27847         });
27848         
27849         if (this.editor.btns.length > 0) {
27850             for (var i = 0; i<this.editor.btns.length; i++) {
27851                 children.push(this.editor.btns[i]);
27852             }
27853         }
27854         
27855         /*
27856         var cog = {
27857                 xtype: 'Button',
27858                 size : 'sm',
27859                 xns: Roo.bootstrap,
27860                 glyphicon : 'cog',
27861                 //html : 'submit'
27862                 menu : {
27863                     xtype: 'Menu',
27864                     xns: Roo.bootstrap,
27865                     items:  []
27866                 }
27867         };
27868         
27869         cog.menu.items.push({
27870             xtype :'MenuItem',
27871             xns: Roo.bootstrap,
27872             html : Clean styles,
27873             tagname : f,
27874             listeners : {
27875                 click : function()
27876                 {
27877                     editorcore.insertTag(this.tagname);
27878                     editor.focus();
27879                 }
27880             }
27881             
27882         });
27883        */
27884         
27885          
27886        this.xtype = 'NavSimplebar';
27887         
27888         for(var i=0;i< children.length;i++) {
27889             
27890             this.buttons.add(this.addxtypeChild(children[i]));
27891             
27892         }
27893         
27894         editor.on('editorevent', this.updateToolbar, this);
27895     },
27896     onBtnClick : function(id)
27897     {
27898        this.editorcore.relayCmd(id);
27899        this.editorcore.focus();
27900     },
27901     
27902     /**
27903      * Protected method that will not generally be called directly. It triggers
27904      * a toolbar update by reading the markup state of the current selection in the editor.
27905      */
27906     updateToolbar: function(){
27907
27908         if(!this.editorcore.activated){
27909             this.editor.onFirstFocus(); // is this neeed?
27910             return;
27911         }
27912
27913         var btns = this.buttons; 
27914         var doc = this.editorcore.doc;
27915         btns.get('bold').setActive(doc.queryCommandState('bold'));
27916         btns.get('italic').setActive(doc.queryCommandState('italic'));
27917         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27918         
27919         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27920         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27921         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27922         
27923         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27924         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27925          /*
27926         
27927         var ans = this.editorcore.getAllAncestors();
27928         if (this.formatCombo) {
27929             
27930             
27931             var store = this.formatCombo.store;
27932             this.formatCombo.setValue("");
27933             for (var i =0; i < ans.length;i++) {
27934                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27935                     // select it..
27936                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27937                     break;
27938                 }
27939             }
27940         }
27941         
27942         
27943         
27944         // hides menus... - so this cant be on a menu...
27945         Roo.bootstrap.MenuMgr.hideAll();
27946         */
27947         Roo.bootstrap.MenuMgr.hideAll();
27948         //this.editorsyncValue();
27949     },
27950     onFirstFocus: function() {
27951         this.buttons.each(function(item){
27952            item.enable();
27953         });
27954     },
27955     toggleSourceEdit : function(sourceEditMode){
27956         
27957           
27958         if(sourceEditMode){
27959             Roo.log("disabling buttons");
27960            this.buttons.each( function(item){
27961                 if(item.cmd != 'pencil'){
27962                     item.disable();
27963                 }
27964             });
27965           
27966         }else{
27967             Roo.log("enabling buttons");
27968             if(this.editorcore.initialized){
27969                 this.buttons.each( function(item){
27970                     item.enable();
27971                 });
27972             }
27973             
27974         }
27975         Roo.log("calling toggole on editor");
27976         // tell the editor that it's been pressed..
27977         this.editor.toggleSourceEdit(sourceEditMode);
27978        
27979     }
27980 });
27981
27982
27983
27984
27985  
27986 /*
27987  * - LGPL
27988  */
27989
27990 /**
27991  * @class Roo.bootstrap.Markdown
27992  * @extends Roo.bootstrap.TextArea
27993  * Bootstrap Showdown editable area
27994  * @cfg {string} content
27995  * 
27996  * @constructor
27997  * Create a new Showdown
27998  */
27999
28000 Roo.bootstrap.Markdown = function(config){
28001     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28002    
28003 };
28004
28005 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28006     
28007     editing :false,
28008     
28009     initEvents : function()
28010     {
28011         
28012         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28013         this.markdownEl = this.el.createChild({
28014             cls : 'roo-markdown-area'
28015         });
28016         this.inputEl().addClass('d-none');
28017         if (this.getValue() == '') {
28018             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28019             
28020         } else {
28021             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28022         }
28023         this.markdownEl.on('click', this.toggleTextEdit, this);
28024         this.on('blur', this.toggleTextEdit, this);
28025         this.on('specialkey', this.resizeTextArea, this);
28026     },
28027     
28028     toggleTextEdit : function()
28029     {
28030         var sh = this.markdownEl.getHeight();
28031         this.inputEl().addClass('d-none');
28032         this.markdownEl.addClass('d-none');
28033         if (!this.editing) {
28034             // show editor?
28035             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28036             this.inputEl().removeClass('d-none');
28037             this.inputEl().focus();
28038             this.editing = true;
28039             return;
28040         }
28041         // show showdown...
28042         this.updateMarkdown();
28043         this.markdownEl.removeClass('d-none');
28044         this.editing = false;
28045         return;
28046     },
28047     updateMarkdown : function()
28048     {
28049         if (this.getValue() == '') {
28050             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28051             return;
28052         }
28053  
28054         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28055     },
28056     
28057     resizeTextArea: function () {
28058         
28059         var sh = 100;
28060         Roo.log([sh, this.getValue().split("\n").length * 30]);
28061         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28062     },
28063     setValue : function(val)
28064     {
28065         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28066         if (!this.editing) {
28067             this.updateMarkdown();
28068         }
28069         
28070     },
28071     focus : function()
28072     {
28073         if (!this.editing) {
28074             this.toggleTextEdit();
28075         }
28076         
28077     }
28078
28079
28080 });/*
28081  * Based on:
28082  * Ext JS Library 1.1.1
28083  * Copyright(c) 2006-2007, Ext JS, LLC.
28084  *
28085  * Originally Released Under LGPL - original licence link has changed is not relivant.
28086  *
28087  * Fork - LGPL
28088  * <script type="text/javascript">
28089  */
28090  
28091 /**
28092  * @class Roo.bootstrap.PagingToolbar
28093  * @extends Roo.bootstrap.NavSimplebar
28094  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28095  * @constructor
28096  * Create a new PagingToolbar
28097  * @param {Object} config The config object
28098  * @param {Roo.data.Store} store
28099  */
28100 Roo.bootstrap.PagingToolbar = function(config)
28101 {
28102     // old args format still supported... - xtype is prefered..
28103         // created from xtype...
28104     
28105     this.ds = config.dataSource;
28106     
28107     if (config.store && !this.ds) {
28108         this.store= Roo.factory(config.store, Roo.data);
28109         this.ds = this.store;
28110         this.ds.xmodule = this.xmodule || false;
28111     }
28112     
28113     this.toolbarItems = [];
28114     if (config.items) {
28115         this.toolbarItems = config.items;
28116     }
28117     
28118     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28119     
28120     this.cursor = 0;
28121     
28122     if (this.ds) { 
28123         this.bind(this.ds);
28124     }
28125     
28126     if (Roo.bootstrap.version == 4) {
28127         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28128     } else {
28129         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28130     }
28131     
28132 };
28133
28134 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28135     /**
28136      * @cfg {Roo.data.Store} dataSource
28137      * The underlying data store providing the paged data
28138      */
28139     /**
28140      * @cfg {String/HTMLElement/Element} container
28141      * container The id or element that will contain the toolbar
28142      */
28143     /**
28144      * @cfg {Boolean} displayInfo
28145      * True to display the displayMsg (defaults to false)
28146      */
28147     /**
28148      * @cfg {Number} pageSize
28149      * The number of records to display per page (defaults to 20)
28150      */
28151     pageSize: 20,
28152     /**
28153      * @cfg {String} displayMsg
28154      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28155      */
28156     displayMsg : 'Displaying {0} - {1} of {2}',
28157     /**
28158      * @cfg {String} emptyMsg
28159      * The message to display when no records are found (defaults to "No data to display")
28160      */
28161     emptyMsg : 'No data to display',
28162     /**
28163      * Customizable piece of the default paging text (defaults to "Page")
28164      * @type String
28165      */
28166     beforePageText : "Page",
28167     /**
28168      * Customizable piece of the default paging text (defaults to "of %0")
28169      * @type String
28170      */
28171     afterPageText : "of {0}",
28172     /**
28173      * Customizable piece of the default paging text (defaults to "First Page")
28174      * @type String
28175      */
28176     firstText : "First Page",
28177     /**
28178      * Customizable piece of the default paging text (defaults to "Previous Page")
28179      * @type String
28180      */
28181     prevText : "Previous Page",
28182     /**
28183      * Customizable piece of the default paging text (defaults to "Next Page")
28184      * @type String
28185      */
28186     nextText : "Next Page",
28187     /**
28188      * Customizable piece of the default paging text (defaults to "Last Page")
28189      * @type String
28190      */
28191     lastText : "Last Page",
28192     /**
28193      * Customizable piece of the default paging text (defaults to "Refresh")
28194      * @type String
28195      */
28196     refreshText : "Refresh",
28197
28198     buttons : false,
28199     // private
28200     onRender : function(ct, position) 
28201     {
28202         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28203         this.navgroup.parentId = this.id;
28204         this.navgroup.onRender(this.el, null);
28205         // add the buttons to the navgroup
28206         
28207         if(this.displayInfo){
28208             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28209             this.displayEl = this.el.select('.x-paging-info', true).first();
28210 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28211 //            this.displayEl = navel.el.select('span',true).first();
28212         }
28213         
28214         var _this = this;
28215         
28216         if(this.buttons){
28217             Roo.each(_this.buttons, function(e){ // this might need to use render????
28218                Roo.factory(e).render(_this.el);
28219             });
28220         }
28221             
28222         Roo.each(_this.toolbarItems, function(e) {
28223             _this.navgroup.addItem(e);
28224         });
28225         
28226         
28227         this.first = this.navgroup.addItem({
28228             tooltip: this.firstText,
28229             cls: "prev btn-outline-secondary",
28230             html : ' <i class="fa fa-step-backward"></i>',
28231             disabled: true,
28232             preventDefault: true,
28233             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28234         });
28235         
28236         this.prev =  this.navgroup.addItem({
28237             tooltip: this.prevText,
28238             cls: "prev btn-outline-secondary",
28239             html : ' <i class="fa fa-backward"></i>',
28240             disabled: true,
28241             preventDefault: true,
28242             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28243         });
28244     //this.addSeparator();
28245         
28246         
28247         var field = this.navgroup.addItem( {
28248             tagtype : 'span',
28249             cls : 'x-paging-position  btn-outline-secondary',
28250              disabled: true,
28251             html : this.beforePageText  +
28252                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28253                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28254          } ); //?? escaped?
28255         
28256         this.field = field.el.select('input', true).first();
28257         this.field.on("keydown", this.onPagingKeydown, this);
28258         this.field.on("focus", function(){this.dom.select();});
28259     
28260     
28261         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28262         //this.field.setHeight(18);
28263         //this.addSeparator();
28264         this.next = this.navgroup.addItem({
28265             tooltip: this.nextText,
28266             cls: "next btn-outline-secondary",
28267             html : ' <i class="fa fa-forward"></i>',
28268             disabled: true,
28269             preventDefault: true,
28270             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28271         });
28272         this.last = this.navgroup.addItem({
28273             tooltip: this.lastText,
28274             html : ' <i class="fa fa-step-forward"></i>',
28275             cls: "next btn-outline-secondary",
28276             disabled: true,
28277             preventDefault: true,
28278             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28279         });
28280     //this.addSeparator();
28281         this.loading = this.navgroup.addItem({
28282             tooltip: this.refreshText,
28283             cls: "btn-outline-secondary",
28284             html : ' <i class="fa fa-refresh"></i>',
28285             preventDefault: true,
28286             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28287         });
28288         
28289     },
28290
28291     // private
28292     updateInfo : function(){
28293         if(this.displayEl){
28294             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28295             var msg = count == 0 ?
28296                 this.emptyMsg :
28297                 String.format(
28298                     this.displayMsg,
28299                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28300                 );
28301             this.displayEl.update(msg);
28302         }
28303     },
28304
28305     // private
28306     onLoad : function(ds, r, o)
28307     {
28308         this.cursor = o.params && o.params.start ? o.params.start : 0;
28309         
28310         var d = this.getPageData(),
28311             ap = d.activePage,
28312             ps = d.pages;
28313         
28314         
28315         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28316         this.field.dom.value = ap;
28317         this.first.setDisabled(ap == 1);
28318         this.prev.setDisabled(ap == 1);
28319         this.next.setDisabled(ap == ps);
28320         this.last.setDisabled(ap == ps);
28321         this.loading.enable();
28322         this.updateInfo();
28323     },
28324
28325     // private
28326     getPageData : function(){
28327         var total = this.ds.getTotalCount();
28328         return {
28329             total : total,
28330             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28331             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28332         };
28333     },
28334
28335     // private
28336     onLoadError : function(){
28337         this.loading.enable();
28338     },
28339
28340     // private
28341     onPagingKeydown : function(e){
28342         var k = e.getKey();
28343         var d = this.getPageData();
28344         if(k == e.RETURN){
28345             var v = this.field.dom.value, pageNum;
28346             if(!v || isNaN(pageNum = parseInt(v, 10))){
28347                 this.field.dom.value = d.activePage;
28348                 return;
28349             }
28350             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28351             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28352             e.stopEvent();
28353         }
28354         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))
28355         {
28356           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28357           this.field.dom.value = pageNum;
28358           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28359           e.stopEvent();
28360         }
28361         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28362         {
28363           var v = this.field.dom.value, pageNum; 
28364           var increment = (e.shiftKey) ? 10 : 1;
28365           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28366                 increment *= -1;
28367           }
28368           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28369             this.field.dom.value = d.activePage;
28370             return;
28371           }
28372           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28373           {
28374             this.field.dom.value = parseInt(v, 10) + increment;
28375             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28376             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28377           }
28378           e.stopEvent();
28379         }
28380     },
28381
28382     // private
28383     beforeLoad : function(){
28384         if(this.loading){
28385             this.loading.disable();
28386         }
28387     },
28388
28389     // private
28390     onClick : function(which){
28391         
28392         var ds = this.ds;
28393         if (!ds) {
28394             return;
28395         }
28396         
28397         switch(which){
28398             case "first":
28399                 ds.load({params:{start: 0, limit: this.pageSize}});
28400             break;
28401             case "prev":
28402                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28403             break;
28404             case "next":
28405                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28406             break;
28407             case "last":
28408                 var total = ds.getTotalCount();
28409                 var extra = total % this.pageSize;
28410                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28411                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28412             break;
28413             case "refresh":
28414                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28415             break;
28416         }
28417     },
28418
28419     /**
28420      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28421      * @param {Roo.data.Store} store The data store to unbind
28422      */
28423     unbind : function(ds){
28424         ds.un("beforeload", this.beforeLoad, this);
28425         ds.un("load", this.onLoad, this);
28426         ds.un("loadexception", this.onLoadError, this);
28427         ds.un("remove", this.updateInfo, this);
28428         ds.un("add", this.updateInfo, this);
28429         this.ds = undefined;
28430     },
28431
28432     /**
28433      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28434      * @param {Roo.data.Store} store The data store to bind
28435      */
28436     bind : function(ds){
28437         ds.on("beforeload", this.beforeLoad, this);
28438         ds.on("load", this.onLoad, this);
28439         ds.on("loadexception", this.onLoadError, this);
28440         ds.on("remove", this.updateInfo, this);
28441         ds.on("add", this.updateInfo, this);
28442         this.ds = ds;
28443     }
28444 });/*
28445  * - LGPL
28446  *
28447  * element
28448  * 
28449  */
28450
28451 /**
28452  * @class Roo.bootstrap.MessageBar
28453  * @extends Roo.bootstrap.Component
28454  * Bootstrap MessageBar class
28455  * @cfg {String} html contents of the MessageBar
28456  * @cfg {String} weight (info | success | warning | danger) default info
28457  * @cfg {String} beforeClass insert the bar before the given class
28458  * @cfg {Boolean} closable (true | false) default false
28459  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28460  * 
28461  * @constructor
28462  * Create a new Element
28463  * @param {Object} config The config object
28464  */
28465
28466 Roo.bootstrap.MessageBar = function(config){
28467     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28468 };
28469
28470 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28471     
28472     html: '',
28473     weight: 'info',
28474     closable: false,
28475     fixed: false,
28476     beforeClass: 'bootstrap-sticky-wrap',
28477     
28478     getAutoCreate : function(){
28479         
28480         var cfg = {
28481             tag: 'div',
28482             cls: 'alert alert-dismissable alert-' + this.weight,
28483             cn: [
28484                 {
28485                     tag: 'span',
28486                     cls: 'message',
28487                     html: this.html || ''
28488                 }
28489             ]
28490         };
28491         
28492         if(this.fixed){
28493             cfg.cls += ' alert-messages-fixed';
28494         }
28495         
28496         if(this.closable){
28497             cfg.cn.push({
28498                 tag: 'button',
28499                 cls: 'close',
28500                 html: 'x'
28501             });
28502         }
28503         
28504         return cfg;
28505     },
28506     
28507     onRender : function(ct, position)
28508     {
28509         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28510         
28511         if(!this.el){
28512             var cfg = Roo.apply({},  this.getAutoCreate());
28513             cfg.id = Roo.id();
28514             
28515             if (this.cls) {
28516                 cfg.cls += ' ' + this.cls;
28517             }
28518             if (this.style) {
28519                 cfg.style = this.style;
28520             }
28521             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28522             
28523             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28524         }
28525         
28526         this.el.select('>button.close').on('click', this.hide, this);
28527         
28528     },
28529     
28530     show : function()
28531     {
28532         if (!this.rendered) {
28533             this.render();
28534         }
28535         
28536         this.el.show();
28537         
28538         this.fireEvent('show', this);
28539         
28540     },
28541     
28542     hide : function()
28543     {
28544         if (!this.rendered) {
28545             this.render();
28546         }
28547         
28548         this.el.hide();
28549         
28550         this.fireEvent('hide', this);
28551     },
28552     
28553     update : function()
28554     {
28555 //        var e = this.el.dom.firstChild;
28556 //        
28557 //        if(this.closable){
28558 //            e = e.nextSibling;
28559 //        }
28560 //        
28561 //        e.data = this.html || '';
28562
28563         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28564     }
28565    
28566 });
28567
28568  
28569
28570      /*
28571  * - LGPL
28572  *
28573  * Graph
28574  * 
28575  */
28576
28577
28578 /**
28579  * @class Roo.bootstrap.Graph
28580  * @extends Roo.bootstrap.Component
28581  * Bootstrap Graph class
28582 > Prameters
28583  -sm {number} sm 4
28584  -md {number} md 5
28585  @cfg {String} graphtype  bar | vbar | pie
28586  @cfg {number} g_x coodinator | centre x (pie)
28587  @cfg {number} g_y coodinator | centre y (pie)
28588  @cfg {number} g_r radius (pie)
28589  @cfg {number} g_height height of the chart (respected by all elements in the set)
28590  @cfg {number} g_width width of the chart (respected by all elements in the set)
28591  @cfg {Object} title The title of the chart
28592     
28593  -{Array}  values
28594  -opts (object) options for the chart 
28595      o {
28596      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28597      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28598      o vgutter (number)
28599      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.
28600      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28601      o to
28602      o stretch (boolean)
28603      o }
28604  -opts (object) options for the pie
28605      o{
28606      o cut
28607      o startAngle (number)
28608      o endAngle (number)
28609      } 
28610  *
28611  * @constructor
28612  * Create a new Input
28613  * @param {Object} config The config object
28614  */
28615
28616 Roo.bootstrap.Graph = function(config){
28617     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28618     
28619     this.addEvents({
28620         // img events
28621         /**
28622          * @event click
28623          * The img click event for the img.
28624          * @param {Roo.EventObject} e
28625          */
28626         "click" : true
28627     });
28628 };
28629
28630 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28631     
28632     sm: 4,
28633     md: 5,
28634     graphtype: 'bar',
28635     g_height: 250,
28636     g_width: 400,
28637     g_x: 50,
28638     g_y: 50,
28639     g_r: 30,
28640     opts:{
28641         //g_colors: this.colors,
28642         g_type: 'soft',
28643         g_gutter: '20%'
28644
28645     },
28646     title : false,
28647
28648     getAutoCreate : function(){
28649         
28650         var cfg = {
28651             tag: 'div',
28652             html : null
28653         };
28654         
28655         
28656         return  cfg;
28657     },
28658
28659     onRender : function(ct,position){
28660         
28661         
28662         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28663         
28664         if (typeof(Raphael) == 'undefined') {
28665             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28666             return;
28667         }
28668         
28669         this.raphael = Raphael(this.el.dom);
28670         
28671                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28672                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28673                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28674                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28675                 /*
28676                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28677                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28678                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28679                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28680                 
28681                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28682                 r.barchart(330, 10, 300, 220, data1);
28683                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28684                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28685                 */
28686                 
28687                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28688                 // r.barchart(30, 30, 560, 250,  xdata, {
28689                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28690                 //     axis : "0 0 1 1",
28691                 //     axisxlabels :  xdata
28692                 //     //yvalues : cols,
28693                    
28694                 // });
28695 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28696 //        
28697 //        this.load(null,xdata,{
28698 //                axis : "0 0 1 1",
28699 //                axisxlabels :  xdata
28700 //                });
28701
28702     },
28703
28704     load : function(graphtype,xdata,opts)
28705     {
28706         this.raphael.clear();
28707         if(!graphtype) {
28708             graphtype = this.graphtype;
28709         }
28710         if(!opts){
28711             opts = this.opts;
28712         }
28713         var r = this.raphael,
28714             fin = function () {
28715                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28716             },
28717             fout = function () {
28718                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28719             },
28720             pfin = function() {
28721                 this.sector.stop();
28722                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28723
28724                 if (this.label) {
28725                     this.label[0].stop();
28726                     this.label[0].attr({ r: 7.5 });
28727                     this.label[1].attr({ "font-weight": 800 });
28728                 }
28729             },
28730             pfout = function() {
28731                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28732
28733                 if (this.label) {
28734                     this.label[0].animate({ r: 5 }, 500, "bounce");
28735                     this.label[1].attr({ "font-weight": 400 });
28736                 }
28737             };
28738
28739         switch(graphtype){
28740             case 'bar':
28741                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28742                 break;
28743             case 'hbar':
28744                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28745                 break;
28746             case 'pie':
28747 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28748 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28749 //            
28750                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28751                 
28752                 break;
28753
28754         }
28755         
28756         if(this.title){
28757             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28758         }
28759         
28760     },
28761     
28762     setTitle: function(o)
28763     {
28764         this.title = o;
28765     },
28766     
28767     initEvents: function() {
28768         
28769         if(!this.href){
28770             this.el.on('click', this.onClick, this);
28771         }
28772     },
28773     
28774     onClick : function(e)
28775     {
28776         Roo.log('img onclick');
28777         this.fireEvent('click', this, e);
28778     }
28779    
28780 });
28781
28782  
28783 /*
28784  * - LGPL
28785  *
28786  * numberBox
28787  * 
28788  */
28789 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28790
28791 /**
28792  * @class Roo.bootstrap.dash.NumberBox
28793  * @extends Roo.bootstrap.Component
28794  * Bootstrap NumberBox class
28795  * @cfg {String} headline Box headline
28796  * @cfg {String} content Box content
28797  * @cfg {String} icon Box icon
28798  * @cfg {String} footer Footer text
28799  * @cfg {String} fhref Footer href
28800  * 
28801  * @constructor
28802  * Create a new NumberBox
28803  * @param {Object} config The config object
28804  */
28805
28806
28807 Roo.bootstrap.dash.NumberBox = function(config){
28808     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28809     
28810 };
28811
28812 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28813     
28814     headline : '',
28815     content : '',
28816     icon : '',
28817     footer : '',
28818     fhref : '',
28819     ficon : '',
28820     
28821     getAutoCreate : function(){
28822         
28823         var cfg = {
28824             tag : 'div',
28825             cls : 'small-box ',
28826             cn : [
28827                 {
28828                     tag : 'div',
28829                     cls : 'inner',
28830                     cn :[
28831                         {
28832                             tag : 'h3',
28833                             cls : 'roo-headline',
28834                             html : this.headline
28835                         },
28836                         {
28837                             tag : 'p',
28838                             cls : 'roo-content',
28839                             html : this.content
28840                         }
28841                     ]
28842                 }
28843             ]
28844         };
28845         
28846         if(this.icon){
28847             cfg.cn.push({
28848                 tag : 'div',
28849                 cls : 'icon',
28850                 cn :[
28851                     {
28852                         tag : 'i',
28853                         cls : 'ion ' + this.icon
28854                     }
28855                 ]
28856             });
28857         }
28858         
28859         if(this.footer){
28860             var footer = {
28861                 tag : 'a',
28862                 cls : 'small-box-footer',
28863                 href : this.fhref || '#',
28864                 html : this.footer
28865             };
28866             
28867             cfg.cn.push(footer);
28868             
28869         }
28870         
28871         return  cfg;
28872     },
28873
28874     onRender : function(ct,position){
28875         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28876
28877
28878        
28879                 
28880     },
28881
28882     setHeadline: function (value)
28883     {
28884         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28885     },
28886     
28887     setFooter: function (value, href)
28888     {
28889         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28890         
28891         if(href){
28892             this.el.select('a.small-box-footer',true).first().attr('href', href);
28893         }
28894         
28895     },
28896
28897     setContent: function (value)
28898     {
28899         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28900     },
28901
28902     initEvents: function() 
28903     {   
28904         
28905     }
28906     
28907 });
28908
28909  
28910 /*
28911  * - LGPL
28912  *
28913  * TabBox
28914  * 
28915  */
28916 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28917
28918 /**
28919  * @class Roo.bootstrap.dash.TabBox
28920  * @extends Roo.bootstrap.Component
28921  * Bootstrap TabBox class
28922  * @cfg {String} title Title of the TabBox
28923  * @cfg {String} icon Icon of the TabBox
28924  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28925  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28926  * 
28927  * @constructor
28928  * Create a new TabBox
28929  * @param {Object} config The config object
28930  */
28931
28932
28933 Roo.bootstrap.dash.TabBox = function(config){
28934     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28935     this.addEvents({
28936         // raw events
28937         /**
28938          * @event addpane
28939          * When a pane is added
28940          * @param {Roo.bootstrap.dash.TabPane} pane
28941          */
28942         "addpane" : true,
28943         /**
28944          * @event activatepane
28945          * When a pane is activated
28946          * @param {Roo.bootstrap.dash.TabPane} pane
28947          */
28948         "activatepane" : true
28949         
28950          
28951     });
28952     
28953     this.panes = [];
28954 };
28955
28956 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28957
28958     title : '',
28959     icon : false,
28960     showtabs : true,
28961     tabScrollable : false,
28962     
28963     getChildContainer : function()
28964     {
28965         return this.el.select('.tab-content', true).first();
28966     },
28967     
28968     getAutoCreate : function(){
28969         
28970         var header = {
28971             tag: 'li',
28972             cls: 'pull-left header',
28973             html: this.title,
28974             cn : []
28975         };
28976         
28977         if(this.icon){
28978             header.cn.push({
28979                 tag: 'i',
28980                 cls: 'fa ' + this.icon
28981             });
28982         }
28983         
28984         var h = {
28985             tag: 'ul',
28986             cls: 'nav nav-tabs pull-right',
28987             cn: [
28988                 header
28989             ]
28990         };
28991         
28992         if(this.tabScrollable){
28993             h = {
28994                 tag: 'div',
28995                 cls: 'tab-header',
28996                 cn: [
28997                     {
28998                         tag: 'ul',
28999                         cls: 'nav nav-tabs pull-right',
29000                         cn: [
29001                             header
29002                         ]
29003                     }
29004                 ]
29005             };
29006         }
29007         
29008         var cfg = {
29009             tag: 'div',
29010             cls: 'nav-tabs-custom',
29011             cn: [
29012                 h,
29013                 {
29014                     tag: 'div',
29015                     cls: 'tab-content no-padding',
29016                     cn: []
29017                 }
29018             ]
29019         };
29020
29021         return  cfg;
29022     },
29023     initEvents : function()
29024     {
29025         //Roo.log('add add pane handler');
29026         this.on('addpane', this.onAddPane, this);
29027     },
29028      /**
29029      * Updates the box title
29030      * @param {String} html to set the title to.
29031      */
29032     setTitle : function(value)
29033     {
29034         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29035     },
29036     onAddPane : function(pane)
29037     {
29038         this.panes.push(pane);
29039         //Roo.log('addpane');
29040         //Roo.log(pane);
29041         // tabs are rendere left to right..
29042         if(!this.showtabs){
29043             return;
29044         }
29045         
29046         var ctr = this.el.select('.nav-tabs', true).first();
29047          
29048          
29049         var existing = ctr.select('.nav-tab',true);
29050         var qty = existing.getCount();;
29051         
29052         
29053         var tab = ctr.createChild({
29054             tag : 'li',
29055             cls : 'nav-tab' + (qty ? '' : ' active'),
29056             cn : [
29057                 {
29058                     tag : 'a',
29059                     href:'#',
29060                     html : pane.title
29061                 }
29062             ]
29063         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29064         pane.tab = tab;
29065         
29066         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29067         if (!qty) {
29068             pane.el.addClass('active');
29069         }
29070         
29071                 
29072     },
29073     onTabClick : function(ev,un,ob,pane)
29074     {
29075         //Roo.log('tab - prev default');
29076         ev.preventDefault();
29077         
29078         
29079         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29080         pane.tab.addClass('active');
29081         //Roo.log(pane.title);
29082         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29083         // technically we should have a deactivate event.. but maybe add later.
29084         // and it should not de-activate the selected tab...
29085         this.fireEvent('activatepane', pane);
29086         pane.el.addClass('active');
29087         pane.fireEvent('activate');
29088         
29089         
29090     },
29091     
29092     getActivePane : function()
29093     {
29094         var r = false;
29095         Roo.each(this.panes, function(p) {
29096             if(p.el.hasClass('active')){
29097                 r = p;
29098                 return false;
29099             }
29100             
29101             return;
29102         });
29103         
29104         return r;
29105     }
29106     
29107     
29108 });
29109
29110  
29111 /*
29112  * - LGPL
29113  *
29114  * Tab pane
29115  * 
29116  */
29117 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29118 /**
29119  * @class Roo.bootstrap.TabPane
29120  * @extends Roo.bootstrap.Component
29121  * Bootstrap TabPane class
29122  * @cfg {Boolean} active (false | true) Default false
29123  * @cfg {String} title title of panel
29124
29125  * 
29126  * @constructor
29127  * Create a new TabPane
29128  * @param {Object} config The config object
29129  */
29130
29131 Roo.bootstrap.dash.TabPane = function(config){
29132     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29133     
29134     this.addEvents({
29135         // raw events
29136         /**
29137          * @event activate
29138          * When a pane is activated
29139          * @param {Roo.bootstrap.dash.TabPane} pane
29140          */
29141         "activate" : true
29142          
29143     });
29144 };
29145
29146 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29147     
29148     active : false,
29149     title : '',
29150     
29151     // the tabBox that this is attached to.
29152     tab : false,
29153      
29154     getAutoCreate : function() 
29155     {
29156         var cfg = {
29157             tag: 'div',
29158             cls: 'tab-pane'
29159         };
29160         
29161         if(this.active){
29162             cfg.cls += ' active';
29163         }
29164         
29165         return cfg;
29166     },
29167     initEvents  : function()
29168     {
29169         //Roo.log('trigger add pane handler');
29170         this.parent().fireEvent('addpane', this)
29171     },
29172     
29173      /**
29174      * Updates the tab title 
29175      * @param {String} html to set the title to.
29176      */
29177     setTitle: function(str)
29178     {
29179         if (!this.tab) {
29180             return;
29181         }
29182         this.title = str;
29183         this.tab.select('a', true).first().dom.innerHTML = str;
29184         
29185     }
29186     
29187     
29188     
29189 });
29190
29191  
29192
29193
29194  /*
29195  * - LGPL
29196  *
29197  * menu
29198  * 
29199  */
29200 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29201
29202 /**
29203  * @class Roo.bootstrap.menu.Menu
29204  * @extends Roo.bootstrap.Component
29205  * Bootstrap Menu class - container for Menu
29206  * @cfg {String} html Text of the menu
29207  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29208  * @cfg {String} icon Font awesome icon
29209  * @cfg {String} pos Menu align to (top | bottom) default bottom
29210  * 
29211  * 
29212  * @constructor
29213  * Create a new Menu
29214  * @param {Object} config The config object
29215  */
29216
29217
29218 Roo.bootstrap.menu.Menu = function(config){
29219     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29220     
29221     this.addEvents({
29222         /**
29223          * @event beforeshow
29224          * Fires before this menu is displayed
29225          * @param {Roo.bootstrap.menu.Menu} this
29226          */
29227         beforeshow : true,
29228         /**
29229          * @event beforehide
29230          * Fires before this menu is hidden
29231          * @param {Roo.bootstrap.menu.Menu} this
29232          */
29233         beforehide : true,
29234         /**
29235          * @event show
29236          * Fires after this menu is displayed
29237          * @param {Roo.bootstrap.menu.Menu} this
29238          */
29239         show : true,
29240         /**
29241          * @event hide
29242          * Fires after this menu is hidden
29243          * @param {Roo.bootstrap.menu.Menu} this
29244          */
29245         hide : true,
29246         /**
29247          * @event click
29248          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29249          * @param {Roo.bootstrap.menu.Menu} this
29250          * @param {Roo.EventObject} e
29251          */
29252         click : true
29253     });
29254     
29255 };
29256
29257 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29258     
29259     submenu : false,
29260     html : '',
29261     weight : 'default',
29262     icon : false,
29263     pos : 'bottom',
29264     
29265     
29266     getChildContainer : function() {
29267         if(this.isSubMenu){
29268             return this.el;
29269         }
29270         
29271         return this.el.select('ul.dropdown-menu', true).first();  
29272     },
29273     
29274     getAutoCreate : function()
29275     {
29276         var text = [
29277             {
29278                 tag : 'span',
29279                 cls : 'roo-menu-text',
29280                 html : this.html
29281             }
29282         ];
29283         
29284         if(this.icon){
29285             text.unshift({
29286                 tag : 'i',
29287                 cls : 'fa ' + this.icon
29288             })
29289         }
29290         
29291         
29292         var cfg = {
29293             tag : 'div',
29294             cls : 'btn-group',
29295             cn : [
29296                 {
29297                     tag : 'button',
29298                     cls : 'dropdown-button btn btn-' + this.weight,
29299                     cn : text
29300                 },
29301                 {
29302                     tag : 'button',
29303                     cls : 'dropdown-toggle btn btn-' + this.weight,
29304                     cn : [
29305                         {
29306                             tag : 'span',
29307                             cls : 'caret'
29308                         }
29309                     ]
29310                 },
29311                 {
29312                     tag : 'ul',
29313                     cls : 'dropdown-menu'
29314                 }
29315             ]
29316             
29317         };
29318         
29319         if(this.pos == 'top'){
29320             cfg.cls += ' dropup';
29321         }
29322         
29323         if(this.isSubMenu){
29324             cfg = {
29325                 tag : 'ul',
29326                 cls : 'dropdown-menu'
29327             }
29328         }
29329         
29330         return cfg;
29331     },
29332     
29333     onRender : function(ct, position)
29334     {
29335         this.isSubMenu = ct.hasClass('dropdown-submenu');
29336         
29337         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29338     },
29339     
29340     initEvents : function() 
29341     {
29342         if(this.isSubMenu){
29343             return;
29344         }
29345         
29346         this.hidden = true;
29347         
29348         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29349         this.triggerEl.on('click', this.onTriggerPress, this);
29350         
29351         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29352         this.buttonEl.on('click', this.onClick, this);
29353         
29354     },
29355     
29356     list : function()
29357     {
29358         if(this.isSubMenu){
29359             return this.el;
29360         }
29361         
29362         return this.el.select('ul.dropdown-menu', true).first();
29363     },
29364     
29365     onClick : function(e)
29366     {
29367         this.fireEvent("click", this, e);
29368     },
29369     
29370     onTriggerPress  : function(e)
29371     {   
29372         if (this.isVisible()) {
29373             this.hide();
29374         } else {
29375             this.show();
29376         }
29377     },
29378     
29379     isVisible : function(){
29380         return !this.hidden;
29381     },
29382     
29383     show : function()
29384     {
29385         this.fireEvent("beforeshow", this);
29386         
29387         this.hidden = false;
29388         this.el.addClass('open');
29389         
29390         Roo.get(document).on("mouseup", this.onMouseUp, this);
29391         
29392         this.fireEvent("show", this);
29393         
29394         
29395     },
29396     
29397     hide : function()
29398     {
29399         this.fireEvent("beforehide", this);
29400         
29401         this.hidden = true;
29402         this.el.removeClass('open');
29403         
29404         Roo.get(document).un("mouseup", this.onMouseUp);
29405         
29406         this.fireEvent("hide", this);
29407     },
29408     
29409     onMouseUp : function()
29410     {
29411         this.hide();
29412     }
29413     
29414 });
29415
29416  
29417  /*
29418  * - LGPL
29419  *
29420  * menu item
29421  * 
29422  */
29423 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29424
29425 /**
29426  * @class Roo.bootstrap.menu.Item
29427  * @extends Roo.bootstrap.Component
29428  * Bootstrap MenuItem class
29429  * @cfg {Boolean} submenu (true | false) default false
29430  * @cfg {String} html text of the item
29431  * @cfg {String} href the link
29432  * @cfg {Boolean} disable (true | false) default false
29433  * @cfg {Boolean} preventDefault (true | false) default true
29434  * @cfg {String} icon Font awesome icon
29435  * @cfg {String} pos Submenu align to (left | right) default right 
29436  * 
29437  * 
29438  * @constructor
29439  * Create a new Item
29440  * @param {Object} config The config object
29441  */
29442
29443
29444 Roo.bootstrap.menu.Item = function(config){
29445     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29446     this.addEvents({
29447         /**
29448          * @event mouseover
29449          * Fires when the mouse is hovering over this menu
29450          * @param {Roo.bootstrap.menu.Item} this
29451          * @param {Roo.EventObject} e
29452          */
29453         mouseover : true,
29454         /**
29455          * @event mouseout
29456          * Fires when the mouse exits this menu
29457          * @param {Roo.bootstrap.menu.Item} this
29458          * @param {Roo.EventObject} e
29459          */
29460         mouseout : true,
29461         // raw events
29462         /**
29463          * @event click
29464          * The raw click event for the entire grid.
29465          * @param {Roo.EventObject} e
29466          */
29467         click : true
29468     });
29469 };
29470
29471 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29472     
29473     submenu : false,
29474     href : '',
29475     html : '',
29476     preventDefault: true,
29477     disable : false,
29478     icon : false,
29479     pos : 'right',
29480     
29481     getAutoCreate : function()
29482     {
29483         var text = [
29484             {
29485                 tag : 'span',
29486                 cls : 'roo-menu-item-text',
29487                 html : this.html
29488             }
29489         ];
29490         
29491         if(this.icon){
29492             text.unshift({
29493                 tag : 'i',
29494                 cls : 'fa ' + this.icon
29495             })
29496         }
29497         
29498         var cfg = {
29499             tag : 'li',
29500             cn : [
29501                 {
29502                     tag : 'a',
29503                     href : this.href || '#',
29504                     cn : text
29505                 }
29506             ]
29507         };
29508         
29509         if(this.disable){
29510             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29511         }
29512         
29513         if(this.submenu){
29514             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29515             
29516             if(this.pos == 'left'){
29517                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29518             }
29519         }
29520         
29521         return cfg;
29522     },
29523     
29524     initEvents : function() 
29525     {
29526         this.el.on('mouseover', this.onMouseOver, this);
29527         this.el.on('mouseout', this.onMouseOut, this);
29528         
29529         this.el.select('a', true).first().on('click', this.onClick, this);
29530         
29531     },
29532     
29533     onClick : function(e)
29534     {
29535         if(this.preventDefault){
29536             e.preventDefault();
29537         }
29538         
29539         this.fireEvent("click", this, e);
29540     },
29541     
29542     onMouseOver : function(e)
29543     {
29544         if(this.submenu && this.pos == 'left'){
29545             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29546         }
29547         
29548         this.fireEvent("mouseover", this, e);
29549     },
29550     
29551     onMouseOut : function(e)
29552     {
29553         this.fireEvent("mouseout", this, e);
29554     }
29555 });
29556
29557  
29558
29559  /*
29560  * - LGPL
29561  *
29562  * menu separator
29563  * 
29564  */
29565 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29566
29567 /**
29568  * @class Roo.bootstrap.menu.Separator
29569  * @extends Roo.bootstrap.Component
29570  * Bootstrap Separator class
29571  * 
29572  * @constructor
29573  * Create a new Separator
29574  * @param {Object} config The config object
29575  */
29576
29577
29578 Roo.bootstrap.menu.Separator = function(config){
29579     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29580 };
29581
29582 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29583     
29584     getAutoCreate : function(){
29585         var cfg = {
29586             tag : 'li',
29587             cls: 'dropdown-divider divider'
29588         };
29589         
29590         return cfg;
29591     }
29592    
29593 });
29594
29595  
29596
29597  /*
29598  * - LGPL
29599  *
29600  * Tooltip
29601  * 
29602  */
29603
29604 /**
29605  * @class Roo.bootstrap.Tooltip
29606  * Bootstrap Tooltip class
29607  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29608  * to determine which dom element triggers the tooltip.
29609  * 
29610  * It needs to add support for additional attributes like tooltip-position
29611  * 
29612  * @constructor
29613  * Create a new Toolti
29614  * @param {Object} config The config object
29615  */
29616
29617 Roo.bootstrap.Tooltip = function(config){
29618     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29619     
29620     this.alignment = Roo.bootstrap.Tooltip.alignment;
29621     
29622     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29623         this.alignment = config.alignment;
29624     }
29625     
29626 };
29627
29628 Roo.apply(Roo.bootstrap.Tooltip, {
29629     /**
29630      * @function init initialize tooltip monitoring.
29631      * @static
29632      */
29633     currentEl : false,
29634     currentTip : false,
29635     currentRegion : false,
29636     
29637     //  init : delay?
29638     
29639     init : function()
29640     {
29641         Roo.get(document).on('mouseover', this.enter ,this);
29642         Roo.get(document).on('mouseout', this.leave, this);
29643          
29644         
29645         this.currentTip = new Roo.bootstrap.Tooltip();
29646     },
29647     
29648     enter : function(ev)
29649     {
29650         var dom = ev.getTarget();
29651         
29652         //Roo.log(['enter',dom]);
29653         var el = Roo.fly(dom);
29654         if (this.currentEl) {
29655             //Roo.log(dom);
29656             //Roo.log(this.currentEl);
29657             //Roo.log(this.currentEl.contains(dom));
29658             if (this.currentEl == el) {
29659                 return;
29660             }
29661             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29662                 return;
29663             }
29664
29665         }
29666         
29667         if (this.currentTip.el) {
29668             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29669         }    
29670         //Roo.log(ev);
29671         
29672         if(!el || el.dom == document){
29673             return;
29674         }
29675         
29676         var bindEl = el; 
29677         var pel = false;
29678         if (!el.attr('tooltip')) {
29679             pel = el.findParent("[tooltip]");
29680             if (pel) {
29681                 bindEl = Roo.get(pel);
29682             }
29683         }
29684         
29685        
29686         
29687         // you can not look for children, as if el is the body.. then everythign is the child..
29688         if (!pel && !el.attr('tooltip')) { //
29689             if (!el.select("[tooltip]").elements.length) {
29690                 return;
29691             }
29692             // is the mouse over this child...?
29693             bindEl = el.select("[tooltip]").first();
29694             var xy = ev.getXY();
29695             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29696                 //Roo.log("not in region.");
29697                 return;
29698             }
29699             //Roo.log("child element over..");
29700             
29701         }
29702         this.currentEl = el;
29703         this.currentTip.bind(bindEl);
29704         this.currentRegion = Roo.lib.Region.getRegion(dom);
29705         this.currentTip.enter();
29706         
29707     },
29708     leave : function(ev)
29709     {
29710         var dom = ev.getTarget();
29711         //Roo.log(['leave',dom]);
29712         if (!this.currentEl) {
29713             return;
29714         }
29715         
29716         
29717         if (dom != this.currentEl.dom) {
29718             return;
29719         }
29720         var xy = ev.getXY();
29721         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29722             return;
29723         }
29724         // only activate leave if mouse cursor is outside... bounding box..
29725         
29726         
29727         
29728         
29729         if (this.currentTip) {
29730             this.currentTip.leave();
29731         }
29732         //Roo.log('clear currentEl');
29733         this.currentEl = false;
29734         
29735         
29736     },
29737     alignment : {
29738         'left' : ['r-l', [-2,0], 'right'],
29739         'right' : ['l-r', [2,0], 'left'],
29740         'bottom' : ['t-b', [0,2], 'top'],
29741         'top' : [ 'b-t', [0,-2], 'bottom']
29742     }
29743     
29744 });
29745
29746
29747 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29748     
29749     
29750     bindEl : false,
29751     
29752     delay : null, // can be { show : 300 , hide: 500}
29753     
29754     timeout : null,
29755     
29756     hoverState : null, //???
29757     
29758     placement : 'bottom', 
29759     
29760     alignment : false,
29761     
29762     getAutoCreate : function(){
29763     
29764         var cfg = {
29765            cls : 'tooltip',   
29766            role : 'tooltip',
29767            cn : [
29768                 {
29769                     cls : 'tooltip-arrow arrow'
29770                 },
29771                 {
29772                     cls : 'tooltip-inner'
29773                 }
29774            ]
29775         };
29776         
29777         return cfg;
29778     },
29779     bind : function(el)
29780     {
29781         this.bindEl = el;
29782     },
29783     
29784     initEvents : function()
29785     {
29786         this.arrowEl = this.el.select('.arrow', true).first();
29787         this.innerEl = this.el.select('.tooltip-inner', true).first();
29788     },
29789     
29790     enter : function () {
29791        
29792         if (this.timeout != null) {
29793             clearTimeout(this.timeout);
29794         }
29795         
29796         this.hoverState = 'in';
29797          //Roo.log("enter - show");
29798         if (!this.delay || !this.delay.show) {
29799             this.show();
29800             return;
29801         }
29802         var _t = this;
29803         this.timeout = setTimeout(function () {
29804             if (_t.hoverState == 'in') {
29805                 _t.show();
29806             }
29807         }, this.delay.show);
29808     },
29809     leave : function()
29810     {
29811         clearTimeout(this.timeout);
29812     
29813         this.hoverState = 'out';
29814          if (!this.delay || !this.delay.hide) {
29815             this.hide();
29816             return;
29817         }
29818        
29819         var _t = this;
29820         this.timeout = setTimeout(function () {
29821             //Roo.log("leave - timeout");
29822             
29823             if (_t.hoverState == 'out') {
29824                 _t.hide();
29825                 Roo.bootstrap.Tooltip.currentEl = false;
29826             }
29827         }, delay);
29828     },
29829     
29830     show : function (msg)
29831     {
29832         if (!this.el) {
29833             this.render(document.body);
29834         }
29835         // set content.
29836         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29837         
29838         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29839         
29840         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29841         
29842         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29843                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29844         
29845         var placement = typeof this.placement == 'function' ?
29846             this.placement.call(this, this.el, on_el) :
29847             this.placement;
29848             
29849         var autoToken = /\s?auto?\s?/i;
29850         var autoPlace = autoToken.test(placement);
29851         if (autoPlace) {
29852             placement = placement.replace(autoToken, '') || 'top';
29853         }
29854         
29855         //this.el.detach()
29856         //this.el.setXY([0,0]);
29857         this.el.show();
29858         //this.el.dom.style.display='block';
29859         
29860         //this.el.appendTo(on_el);
29861         
29862         var p = this.getPosition();
29863         var box = this.el.getBox();
29864         
29865         if (autoPlace) {
29866             // fixme..
29867         }
29868         
29869         var align = this.alignment[placement];
29870         
29871         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29872         
29873         if(placement == 'top' || placement == 'bottom'){
29874             if(xy[0] < 0){
29875                 placement = 'right';
29876             }
29877             
29878             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29879                 placement = 'left';
29880             }
29881             
29882             var scroll = Roo.select('body', true).first().getScroll();
29883             
29884             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29885                 placement = 'top';
29886             }
29887             
29888             align = this.alignment[placement];
29889             
29890             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29891             
29892         }
29893         
29894         var elems = document.getElementsByTagName('div');
29895         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29896         for (var i = 0; i < elems.length; i++) {
29897           var zindex = Number.parseInt(
29898                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29899                 10
29900           );
29901           if (zindex > highest) {
29902             highest = zindex;
29903           }
29904         }
29905         
29906         
29907         
29908         this.el.dom.style.zIndex = highest;
29909         
29910         this.el.alignTo(this.bindEl, align[0],align[1]);
29911         //var arrow = this.el.select('.arrow',true).first();
29912         //arrow.set(align[2], 
29913         
29914         this.el.addClass(placement);
29915         this.el.addClass("bs-tooltip-"+ placement);
29916         
29917         this.el.addClass('in fade show');
29918         
29919         this.hoverState = null;
29920         
29921         if (this.el.hasClass('fade')) {
29922             // fade it?
29923         }
29924         
29925         
29926         
29927         
29928         
29929     },
29930     hide : function()
29931     {
29932          
29933         if (!this.el) {
29934             return;
29935         }
29936         //this.el.setXY([0,0]);
29937         this.el.removeClass(['show', 'in']);
29938         //this.el.hide();
29939         
29940     }
29941     
29942 });
29943  
29944
29945  /*
29946  * - LGPL
29947  *
29948  * Location Picker
29949  * 
29950  */
29951
29952 /**
29953  * @class Roo.bootstrap.LocationPicker
29954  * @extends Roo.bootstrap.Component
29955  * Bootstrap LocationPicker class
29956  * @cfg {Number} latitude Position when init default 0
29957  * @cfg {Number} longitude Position when init default 0
29958  * @cfg {Number} zoom default 15
29959  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29960  * @cfg {Boolean} mapTypeControl default false
29961  * @cfg {Boolean} disableDoubleClickZoom default false
29962  * @cfg {Boolean} scrollwheel default true
29963  * @cfg {Boolean} streetViewControl default false
29964  * @cfg {Number} radius default 0
29965  * @cfg {String} locationName
29966  * @cfg {Boolean} draggable default true
29967  * @cfg {Boolean} enableAutocomplete default false
29968  * @cfg {Boolean} enableReverseGeocode default true
29969  * @cfg {String} markerTitle
29970  * 
29971  * @constructor
29972  * Create a new LocationPicker
29973  * @param {Object} config The config object
29974  */
29975
29976
29977 Roo.bootstrap.LocationPicker = function(config){
29978     
29979     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29980     
29981     this.addEvents({
29982         /**
29983          * @event initial
29984          * Fires when the picker initialized.
29985          * @param {Roo.bootstrap.LocationPicker} this
29986          * @param {Google Location} location
29987          */
29988         initial : true,
29989         /**
29990          * @event positionchanged
29991          * Fires when the picker position changed.
29992          * @param {Roo.bootstrap.LocationPicker} this
29993          * @param {Google Location} location
29994          */
29995         positionchanged : true,
29996         /**
29997          * @event resize
29998          * Fires when the map resize.
29999          * @param {Roo.bootstrap.LocationPicker} this
30000          */
30001         resize : true,
30002         /**
30003          * @event show
30004          * Fires when the map show.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          */
30007         show : true,
30008         /**
30009          * @event hide
30010          * Fires when the map hide.
30011          * @param {Roo.bootstrap.LocationPicker} this
30012          */
30013         hide : true,
30014         /**
30015          * @event mapClick
30016          * Fires when click the map.
30017          * @param {Roo.bootstrap.LocationPicker} this
30018          * @param {Map event} e
30019          */
30020         mapClick : true,
30021         /**
30022          * @event mapRightClick
30023          * Fires when right click the map.
30024          * @param {Roo.bootstrap.LocationPicker} this
30025          * @param {Map event} e
30026          */
30027         mapRightClick : true,
30028         /**
30029          * @event markerClick
30030          * Fires when click the marker.
30031          * @param {Roo.bootstrap.LocationPicker} this
30032          * @param {Map event} e
30033          */
30034         markerClick : true,
30035         /**
30036          * @event markerRightClick
30037          * Fires when right click the marker.
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          * @param {Map event} e
30040          */
30041         markerRightClick : true,
30042         /**
30043          * @event OverlayViewDraw
30044          * Fires when OverlayView Draw
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          */
30047         OverlayViewDraw : true,
30048         /**
30049          * @event OverlayViewOnAdd
30050          * Fires when OverlayView Draw
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          */
30053         OverlayViewOnAdd : true,
30054         /**
30055          * @event OverlayViewOnRemove
30056          * Fires when OverlayView Draw
30057          * @param {Roo.bootstrap.LocationPicker} this
30058          */
30059         OverlayViewOnRemove : true,
30060         /**
30061          * @event OverlayViewShow
30062          * Fires when OverlayView Draw
30063          * @param {Roo.bootstrap.LocationPicker} this
30064          * @param {Pixel} cpx
30065          */
30066         OverlayViewShow : true,
30067         /**
30068          * @event OverlayViewHide
30069          * Fires when OverlayView Draw
30070          * @param {Roo.bootstrap.LocationPicker} this
30071          */
30072         OverlayViewHide : true,
30073         /**
30074          * @event loadexception
30075          * Fires when load google lib failed.
30076          * @param {Roo.bootstrap.LocationPicker} this
30077          */
30078         loadexception : true
30079     });
30080         
30081 };
30082
30083 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30084     
30085     gMapContext: false,
30086     
30087     latitude: 0,
30088     longitude: 0,
30089     zoom: 15,
30090     mapTypeId: false,
30091     mapTypeControl: false,
30092     disableDoubleClickZoom: false,
30093     scrollwheel: true,
30094     streetViewControl: false,
30095     radius: 0,
30096     locationName: '',
30097     draggable: true,
30098     enableAutocomplete: false,
30099     enableReverseGeocode: true,
30100     markerTitle: '',
30101     
30102     getAutoCreate: function()
30103     {
30104
30105         var cfg = {
30106             tag: 'div',
30107             cls: 'roo-location-picker'
30108         };
30109         
30110         return cfg
30111     },
30112     
30113     initEvents: function(ct, position)
30114     {       
30115         if(!this.el.getWidth() || this.isApplied()){
30116             return;
30117         }
30118         
30119         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30120         
30121         this.initial();
30122     },
30123     
30124     initial: function()
30125     {
30126         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30127             this.fireEvent('loadexception', this);
30128             return;
30129         }
30130         
30131         if(!this.mapTypeId){
30132             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30133         }
30134         
30135         this.gMapContext = this.GMapContext();
30136         
30137         this.initOverlayView();
30138         
30139         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30140         
30141         var _this = this;
30142                 
30143         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30144             _this.setPosition(_this.gMapContext.marker.position);
30145         });
30146         
30147         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30148             _this.fireEvent('mapClick', this, event);
30149             
30150         });
30151
30152         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30153             _this.fireEvent('mapRightClick', this, event);
30154             
30155         });
30156         
30157         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30158             _this.fireEvent('markerClick', this, event);
30159             
30160         });
30161
30162         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30163             _this.fireEvent('markerRightClick', this, event);
30164             
30165         });
30166         
30167         this.setPosition(this.gMapContext.location);
30168         
30169         this.fireEvent('initial', this, this.gMapContext.location);
30170     },
30171     
30172     initOverlayView: function()
30173     {
30174         var _this = this;
30175         
30176         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30177             
30178             draw: function()
30179             {
30180                 _this.fireEvent('OverlayViewDraw', _this);
30181             },
30182             
30183             onAdd: function()
30184             {
30185                 _this.fireEvent('OverlayViewOnAdd', _this);
30186             },
30187             
30188             onRemove: function()
30189             {
30190                 _this.fireEvent('OverlayViewOnRemove', _this);
30191             },
30192             
30193             show: function(cpx)
30194             {
30195                 _this.fireEvent('OverlayViewShow', _this, cpx);
30196             },
30197             
30198             hide: function()
30199             {
30200                 _this.fireEvent('OverlayViewHide', _this);
30201             }
30202             
30203         });
30204     },
30205     
30206     fromLatLngToContainerPixel: function(event)
30207     {
30208         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30209     },
30210     
30211     isApplied: function() 
30212     {
30213         return this.getGmapContext() == false ? false : true;
30214     },
30215     
30216     getGmapContext: function() 
30217     {
30218         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30219     },
30220     
30221     GMapContext: function() 
30222     {
30223         var position = new google.maps.LatLng(this.latitude, this.longitude);
30224         
30225         var _map = new google.maps.Map(this.el.dom, {
30226             center: position,
30227             zoom: this.zoom,
30228             mapTypeId: this.mapTypeId,
30229             mapTypeControl: this.mapTypeControl,
30230             disableDoubleClickZoom: this.disableDoubleClickZoom,
30231             scrollwheel: this.scrollwheel,
30232             streetViewControl: this.streetViewControl,
30233             locationName: this.locationName,
30234             draggable: this.draggable,
30235             enableAutocomplete: this.enableAutocomplete,
30236             enableReverseGeocode: this.enableReverseGeocode
30237         });
30238         
30239         var _marker = new google.maps.Marker({
30240             position: position,
30241             map: _map,
30242             title: this.markerTitle,
30243             draggable: this.draggable
30244         });
30245         
30246         return {
30247             map: _map,
30248             marker: _marker,
30249             circle: null,
30250             location: position,
30251             radius: this.radius,
30252             locationName: this.locationName,
30253             addressComponents: {
30254                 formatted_address: null,
30255                 addressLine1: null,
30256                 addressLine2: null,
30257                 streetName: null,
30258                 streetNumber: null,
30259                 city: null,
30260                 district: null,
30261                 state: null,
30262                 stateOrProvince: null
30263             },
30264             settings: this,
30265             domContainer: this.el.dom,
30266             geodecoder: new google.maps.Geocoder()
30267         };
30268     },
30269     
30270     drawCircle: function(center, radius, options) 
30271     {
30272         if (this.gMapContext.circle != null) {
30273             this.gMapContext.circle.setMap(null);
30274         }
30275         if (radius > 0) {
30276             radius *= 1;
30277             options = Roo.apply({}, options, {
30278                 strokeColor: "#0000FF",
30279                 strokeOpacity: .35,
30280                 strokeWeight: 2,
30281                 fillColor: "#0000FF",
30282                 fillOpacity: .2
30283             });
30284             
30285             options.map = this.gMapContext.map;
30286             options.radius = radius;
30287             options.center = center;
30288             this.gMapContext.circle = new google.maps.Circle(options);
30289             return this.gMapContext.circle;
30290         }
30291         
30292         return null;
30293     },
30294     
30295     setPosition: function(location) 
30296     {
30297         this.gMapContext.location = location;
30298         this.gMapContext.marker.setPosition(location);
30299         this.gMapContext.map.panTo(location);
30300         this.drawCircle(location, this.gMapContext.radius, {});
30301         
30302         var _this = this;
30303         
30304         if (this.gMapContext.settings.enableReverseGeocode) {
30305             this.gMapContext.geodecoder.geocode({
30306                 latLng: this.gMapContext.location
30307             }, function(results, status) {
30308                 
30309                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30310                     _this.gMapContext.locationName = results[0].formatted_address;
30311                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30312                     
30313                     _this.fireEvent('positionchanged', this, location);
30314                 }
30315             });
30316             
30317             return;
30318         }
30319         
30320         this.fireEvent('positionchanged', this, location);
30321     },
30322     
30323     resize: function()
30324     {
30325         google.maps.event.trigger(this.gMapContext.map, "resize");
30326         
30327         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30328         
30329         this.fireEvent('resize', this);
30330     },
30331     
30332     setPositionByLatLng: function(latitude, longitude)
30333     {
30334         this.setPosition(new google.maps.LatLng(latitude, longitude));
30335     },
30336     
30337     getCurrentPosition: function() 
30338     {
30339         return {
30340             latitude: this.gMapContext.location.lat(),
30341             longitude: this.gMapContext.location.lng()
30342         };
30343     },
30344     
30345     getAddressName: function() 
30346     {
30347         return this.gMapContext.locationName;
30348     },
30349     
30350     getAddressComponents: function() 
30351     {
30352         return this.gMapContext.addressComponents;
30353     },
30354     
30355     address_component_from_google_geocode: function(address_components) 
30356     {
30357         var result = {};
30358         
30359         for (var i = 0; i < address_components.length; i++) {
30360             var component = address_components[i];
30361             if (component.types.indexOf("postal_code") >= 0) {
30362                 result.postalCode = component.short_name;
30363             } else if (component.types.indexOf("street_number") >= 0) {
30364                 result.streetNumber = component.short_name;
30365             } else if (component.types.indexOf("route") >= 0) {
30366                 result.streetName = component.short_name;
30367             } else if (component.types.indexOf("neighborhood") >= 0) {
30368                 result.city = component.short_name;
30369             } else if (component.types.indexOf("locality") >= 0) {
30370                 result.city = component.short_name;
30371             } else if (component.types.indexOf("sublocality") >= 0) {
30372                 result.district = component.short_name;
30373             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30374                 result.stateOrProvince = component.short_name;
30375             } else if (component.types.indexOf("country") >= 0) {
30376                 result.country = component.short_name;
30377             }
30378         }
30379         
30380         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30381         result.addressLine2 = "";
30382         return result;
30383     },
30384     
30385     setZoomLevel: function(zoom)
30386     {
30387         this.gMapContext.map.setZoom(zoom);
30388     },
30389     
30390     show: function()
30391     {
30392         if(!this.el){
30393             return;
30394         }
30395         
30396         this.el.show();
30397         
30398         this.resize();
30399         
30400         this.fireEvent('show', this);
30401     },
30402     
30403     hide: function()
30404     {
30405         if(!this.el){
30406             return;
30407         }
30408         
30409         this.el.hide();
30410         
30411         this.fireEvent('hide', this);
30412     }
30413     
30414 });
30415
30416 Roo.apply(Roo.bootstrap.LocationPicker, {
30417     
30418     OverlayView : function(map, options)
30419     {
30420         options = options || {};
30421         
30422         this.setMap(map);
30423     }
30424     
30425     
30426 });/**
30427  * @class Roo.bootstrap.Alert
30428  * @extends Roo.bootstrap.Component
30429  * Bootstrap Alert class - shows an alert area box
30430  * eg
30431  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30432   Enter a valid email address
30433 </div>
30434  * @licence LGPL
30435  * @cfg {String} title The title of alert
30436  * @cfg {String} html The content of alert
30437  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30438  * @cfg {String} fa font-awesomeicon
30439  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30440  * @cfg {Boolean} close true to show a x closer
30441  * 
30442  * 
30443  * @constructor
30444  * Create a new alert
30445  * @param {Object} config The config object
30446  */
30447
30448
30449 Roo.bootstrap.Alert = function(config){
30450     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30451     
30452 };
30453
30454 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30455     
30456     title: '',
30457     html: '',
30458     weight: false,
30459     fa: false,
30460     faicon: false, // BC
30461     close : false,
30462     
30463     
30464     getAutoCreate : function()
30465     {
30466         
30467         var cfg = {
30468             tag : 'div',
30469             cls : 'alert',
30470             cn : [
30471                 {
30472                     tag: 'button',
30473                     type :  "button",
30474                     cls: "close",
30475                     html : '×',
30476                     style : this.close ? '' : 'display:none'
30477                 },
30478                 {
30479                     tag : 'i',
30480                     cls : 'roo-alert-icon'
30481                     
30482                 },
30483                 {
30484                     tag : 'b',
30485                     cls : 'roo-alert-title',
30486                     html : this.title
30487                 },
30488                 {
30489                     tag : 'span',
30490                     cls : 'roo-alert-text',
30491                     html : this.html
30492                 }
30493             ]
30494         };
30495         
30496         if(this.faicon){
30497             cfg.cn[0].cls += ' fa ' + this.faicon;
30498         }
30499         if(this.fa){
30500             cfg.cn[0].cls += ' fa ' + this.fa;
30501         }
30502         
30503         if(this.weight){
30504             cfg.cls += ' alert-' + this.weight;
30505         }
30506         
30507         return cfg;
30508     },
30509     
30510     initEvents: function() 
30511     {
30512         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30513         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30514         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30515         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30516         if (this.seconds > 0) {
30517             this.hide.defer(this.seconds, this);
30518         }
30519     },
30520     /**
30521      * Set the Title Message HTML
30522      * @param {String} html
30523      */
30524     setTitle : function(str)
30525     {
30526         this.titleEl.dom.innerHTML = str;
30527     },
30528      
30529      /**
30530      * Set the Body Message HTML
30531      * @param {String} html
30532      */
30533     setHtml : function(str)
30534     {
30535         this.htmlEl.dom.innerHTML = str;
30536     },
30537     /**
30538      * Set the Weight of the alert
30539      * @param {String} (success|info|warning|danger) weight
30540      */
30541     
30542     setWeight : function(weight)
30543     {
30544         if(this.weight){
30545             this.el.removeClass('alert-' + this.weight);
30546         }
30547         
30548         this.weight = weight;
30549         
30550         this.el.addClass('alert-' + this.weight);
30551     },
30552       /**
30553      * Set the Icon of the alert
30554      * @param {String} see fontawsome names (name without the 'fa-' bit)
30555      */
30556     setIcon : function(icon)
30557     {
30558         if(this.faicon){
30559             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30560         }
30561         
30562         this.faicon = icon;
30563         
30564         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30565     },
30566     /**
30567      * Hide the Alert
30568      */
30569     hide: function() 
30570     {
30571         this.el.hide();   
30572     },
30573     /**
30574      * Show the Alert
30575      */
30576     show: function() 
30577     {  
30578         this.el.show();   
30579     }
30580     
30581 });
30582
30583  
30584 /*
30585 * Licence: LGPL
30586 */
30587
30588 /**
30589  * @class Roo.bootstrap.UploadCropbox
30590  * @extends Roo.bootstrap.Component
30591  * Bootstrap UploadCropbox class
30592  * @cfg {String} emptyText show when image has been loaded
30593  * @cfg {String} rotateNotify show when image too small to rotate
30594  * @cfg {Number} errorTimeout default 3000
30595  * @cfg {Number} minWidth default 300
30596  * @cfg {Number} minHeight default 300
30597  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30598  * @cfg {Boolean} isDocument (true|false) default false
30599  * @cfg {String} url action url
30600  * @cfg {String} paramName default 'imageUpload'
30601  * @cfg {String} method default POST
30602  * @cfg {Boolean} loadMask (true|false) default true
30603  * @cfg {Boolean} loadingText default 'Loading...'
30604  * 
30605  * @constructor
30606  * Create a new UploadCropbox
30607  * @param {Object} config The config object
30608  */
30609
30610 Roo.bootstrap.UploadCropbox = function(config){
30611     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30612     
30613     this.addEvents({
30614         /**
30615          * @event beforeselectfile
30616          * Fire before select file
30617          * @param {Roo.bootstrap.UploadCropbox} this
30618          */
30619         "beforeselectfile" : true,
30620         /**
30621          * @event initial
30622          * Fire after initEvent
30623          * @param {Roo.bootstrap.UploadCropbox} this
30624          */
30625         "initial" : true,
30626         /**
30627          * @event crop
30628          * Fire after initEvent
30629          * @param {Roo.bootstrap.UploadCropbox} this
30630          * @param {String} data
30631          */
30632         "crop" : true,
30633         /**
30634          * @event prepare
30635          * Fire when preparing the file data
30636          * @param {Roo.bootstrap.UploadCropbox} this
30637          * @param {Object} file
30638          */
30639         "prepare" : true,
30640         /**
30641          * @event exception
30642          * Fire when get exception
30643          * @param {Roo.bootstrap.UploadCropbox} this
30644          * @param {XMLHttpRequest} xhr
30645          */
30646         "exception" : true,
30647         /**
30648          * @event beforeloadcanvas
30649          * Fire before load the canvas
30650          * @param {Roo.bootstrap.UploadCropbox} this
30651          * @param {String} src
30652          */
30653         "beforeloadcanvas" : true,
30654         /**
30655          * @event trash
30656          * Fire when trash image
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          */
30659         "trash" : true,
30660         /**
30661          * @event download
30662          * Fire when download the image
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          */
30665         "download" : true,
30666         /**
30667          * @event footerbuttonclick
30668          * Fire when footerbuttonclick
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          * @param {String} type
30671          */
30672         "footerbuttonclick" : true,
30673         /**
30674          * @event resize
30675          * Fire when resize
30676          * @param {Roo.bootstrap.UploadCropbox} this
30677          */
30678         "resize" : true,
30679         /**
30680          * @event rotate
30681          * Fire when rotate the image
30682          * @param {Roo.bootstrap.UploadCropbox} this
30683          * @param {String} pos
30684          */
30685         "rotate" : true,
30686         /**
30687          * @event inspect
30688          * Fire when inspect the file
30689          * @param {Roo.bootstrap.UploadCropbox} this
30690          * @param {Object} file
30691          */
30692         "inspect" : true,
30693         /**
30694          * @event upload
30695          * Fire when xhr upload the file
30696          * @param {Roo.bootstrap.UploadCropbox} this
30697          * @param {Object} data
30698          */
30699         "upload" : true,
30700         /**
30701          * @event arrange
30702          * Fire when arrange the file data
30703          * @param {Roo.bootstrap.UploadCropbox} this
30704          * @param {Object} formData
30705          */
30706         "arrange" : true
30707     });
30708     
30709     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30710 };
30711
30712 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30713     
30714     emptyText : 'Click to upload image',
30715     rotateNotify : 'Image is too small to rotate',
30716     errorTimeout : 3000,
30717     scale : 0,
30718     baseScale : 1,
30719     rotate : 0,
30720     dragable : false,
30721     pinching : false,
30722     mouseX : 0,
30723     mouseY : 0,
30724     cropData : false,
30725     minWidth : 300,
30726     minHeight : 300,
30727     file : false,
30728     exif : {},
30729     baseRotate : 1,
30730     cropType : 'image/jpeg',
30731     buttons : false,
30732     canvasLoaded : false,
30733     isDocument : false,
30734     method : 'POST',
30735     paramName : 'imageUpload',
30736     loadMask : true,
30737     loadingText : 'Loading...',
30738     maskEl : false,
30739     
30740     getAutoCreate : function()
30741     {
30742         var cfg = {
30743             tag : 'div',
30744             cls : 'roo-upload-cropbox',
30745             cn : [
30746                 {
30747                     tag : 'input',
30748                     cls : 'roo-upload-cropbox-selector',
30749                     type : 'file'
30750                 },
30751                 {
30752                     tag : 'div',
30753                     cls : 'roo-upload-cropbox-body',
30754                     style : 'cursor:pointer',
30755                     cn : [
30756                         {
30757                             tag : 'div',
30758                             cls : 'roo-upload-cropbox-preview'
30759                         },
30760                         {
30761                             tag : 'div',
30762                             cls : 'roo-upload-cropbox-thumb'
30763                         },
30764                         {
30765                             tag : 'div',
30766                             cls : 'roo-upload-cropbox-empty-notify',
30767                             html : this.emptyText
30768                         },
30769                         {
30770                             tag : 'div',
30771                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30772                             html : this.rotateNotify
30773                         }
30774                     ]
30775                 },
30776                 {
30777                     tag : 'div',
30778                     cls : 'roo-upload-cropbox-footer',
30779                     cn : {
30780                         tag : 'div',
30781                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30782                         cn : []
30783                     }
30784                 }
30785             ]
30786         };
30787         
30788         return cfg;
30789     },
30790     
30791     onRender : function(ct, position)
30792     {
30793         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30794         
30795         if (this.buttons.length) {
30796             
30797             Roo.each(this.buttons, function(bb) {
30798                 
30799                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30800                 
30801                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30802                 
30803             }, this);
30804         }
30805         
30806         if(this.loadMask){
30807             this.maskEl = this.el;
30808         }
30809     },
30810     
30811     initEvents : function()
30812     {
30813         this.urlAPI = (window.createObjectURL && window) || 
30814                                 (window.URL && URL.revokeObjectURL && URL) || 
30815                                 (window.webkitURL && webkitURL);
30816                         
30817         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30818         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819         
30820         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30821         this.selectorEl.hide();
30822         
30823         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30824         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30825         
30826         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30827         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30828         this.thumbEl.hide();
30829         
30830         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30831         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30832         
30833         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30834         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30835         this.errorEl.hide();
30836         
30837         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30838         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30839         this.footerEl.hide();
30840         
30841         this.setThumbBoxSize();
30842         
30843         this.bind();
30844         
30845         this.resize();
30846         
30847         this.fireEvent('initial', this);
30848     },
30849
30850     bind : function()
30851     {
30852         var _this = this;
30853         
30854         window.addEventListener("resize", function() { _this.resize(); } );
30855         
30856         this.bodyEl.on('click', this.beforeSelectFile, this);
30857         
30858         if(Roo.isTouch){
30859             this.bodyEl.on('touchstart', this.onTouchStart, this);
30860             this.bodyEl.on('touchmove', this.onTouchMove, this);
30861             this.bodyEl.on('touchend', this.onTouchEnd, this);
30862         }
30863         
30864         if(!Roo.isTouch){
30865             this.bodyEl.on('mousedown', this.onMouseDown, this);
30866             this.bodyEl.on('mousemove', this.onMouseMove, this);
30867             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30868             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30869             Roo.get(document).on('mouseup', this.onMouseUp, this);
30870         }
30871         
30872         this.selectorEl.on('change', this.onFileSelected, this);
30873     },
30874     
30875     reset : function()
30876     {    
30877         this.scale = 0;
30878         this.baseScale = 1;
30879         this.rotate = 0;
30880         this.baseRotate = 1;
30881         this.dragable = false;
30882         this.pinching = false;
30883         this.mouseX = 0;
30884         this.mouseY = 0;
30885         this.cropData = false;
30886         this.notifyEl.dom.innerHTML = this.emptyText;
30887         
30888         this.selectorEl.dom.value = '';
30889         
30890     },
30891     
30892     resize : function()
30893     {
30894         if(this.fireEvent('resize', this) != false){
30895             this.setThumbBoxPosition();
30896             this.setCanvasPosition();
30897         }
30898     },
30899     
30900     onFooterButtonClick : function(e, el, o, type)
30901     {
30902         switch (type) {
30903             case 'rotate-left' :
30904                 this.onRotateLeft(e);
30905                 break;
30906             case 'rotate-right' :
30907                 this.onRotateRight(e);
30908                 break;
30909             case 'picture' :
30910                 this.beforeSelectFile(e);
30911                 break;
30912             case 'trash' :
30913                 this.trash(e);
30914                 break;
30915             case 'crop' :
30916                 this.crop(e);
30917                 break;
30918             case 'download' :
30919                 this.download(e);
30920                 break;
30921             default :
30922                 break;
30923         }
30924         
30925         this.fireEvent('footerbuttonclick', this, type);
30926     },
30927     
30928     beforeSelectFile : function(e)
30929     {
30930         e.preventDefault();
30931         
30932         if(this.fireEvent('beforeselectfile', this) != false){
30933             this.selectorEl.dom.click();
30934         }
30935     },
30936     
30937     onFileSelected : function(e)
30938     {
30939         e.preventDefault();
30940         
30941         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30942             return;
30943         }
30944         
30945         var file = this.selectorEl.dom.files[0];
30946         
30947         if(this.fireEvent('inspect', this, file) != false){
30948             this.prepare(file);
30949         }
30950         
30951     },
30952     
30953     trash : function(e)
30954     {
30955         this.fireEvent('trash', this);
30956     },
30957     
30958     download : function(e)
30959     {
30960         this.fireEvent('download', this);
30961     },
30962     
30963     loadCanvas : function(src)
30964     {   
30965         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30966             
30967             this.reset();
30968             
30969             this.imageEl = document.createElement('img');
30970             
30971             var _this = this;
30972             
30973             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30974             
30975             this.imageEl.src = src;
30976         }
30977     },
30978     
30979     onLoadCanvas : function()
30980     {   
30981         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30982         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30983         
30984         this.bodyEl.un('click', this.beforeSelectFile, this);
30985         
30986         this.notifyEl.hide();
30987         this.thumbEl.show();
30988         this.footerEl.show();
30989         
30990         this.baseRotateLevel();
30991         
30992         if(this.isDocument){
30993             this.setThumbBoxSize();
30994         }
30995         
30996         this.setThumbBoxPosition();
30997         
30998         this.baseScaleLevel();
30999         
31000         this.draw();
31001         
31002         this.resize();
31003         
31004         this.canvasLoaded = true;
31005         
31006         if(this.loadMask){
31007             this.maskEl.unmask();
31008         }
31009         
31010     },
31011     
31012     setCanvasPosition : function()
31013     {   
31014         if(!this.canvasEl){
31015             return;
31016         }
31017         
31018         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31019         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31020         
31021         this.previewEl.setLeft(pw);
31022         this.previewEl.setTop(ph);
31023         
31024     },
31025     
31026     onMouseDown : function(e)
31027     {   
31028         e.stopEvent();
31029         
31030         this.dragable = true;
31031         this.pinching = false;
31032         
31033         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31034             this.dragable = false;
31035             return;
31036         }
31037         
31038         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31039         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31040         
31041     },
31042     
31043     onMouseMove : function(e)
31044     {   
31045         e.stopEvent();
31046         
31047         if(!this.canvasLoaded){
31048             return;
31049         }
31050         
31051         if (!this.dragable){
31052             return;
31053         }
31054         
31055         var minX = Math.ceil(this.thumbEl.getLeft(true));
31056         var minY = Math.ceil(this.thumbEl.getTop(true));
31057         
31058         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31059         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31060         
31061         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31062         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31063         
31064         x = x - this.mouseX;
31065         y = y - this.mouseY;
31066         
31067         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31068         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31069         
31070         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31071         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31072         
31073         this.previewEl.setLeft(bgX);
31074         this.previewEl.setTop(bgY);
31075         
31076         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31077         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31078     },
31079     
31080     onMouseUp : function(e)
31081     {   
31082         e.stopEvent();
31083         
31084         this.dragable = false;
31085     },
31086     
31087     onMouseWheel : function(e)
31088     {   
31089         e.stopEvent();
31090         
31091         this.startScale = this.scale;
31092         
31093         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31094         
31095         if(!this.zoomable()){
31096             this.scale = this.startScale;
31097             return;
31098         }
31099         
31100         this.draw();
31101         
31102         return;
31103     },
31104     
31105     zoomable : function()
31106     {
31107         var minScale = this.thumbEl.getWidth() / this.minWidth;
31108         
31109         if(this.minWidth < this.minHeight){
31110             minScale = this.thumbEl.getHeight() / this.minHeight;
31111         }
31112         
31113         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31114         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31115         
31116         if(
31117                 this.isDocument &&
31118                 (this.rotate == 0 || this.rotate == 180) && 
31119                 (
31120                     width > this.imageEl.OriginWidth || 
31121                     height > this.imageEl.OriginHeight ||
31122                     (width < this.minWidth && height < this.minHeight)
31123                 )
31124         ){
31125             return false;
31126         }
31127         
31128         if(
31129                 this.isDocument &&
31130                 (this.rotate == 90 || this.rotate == 270) && 
31131                 (
31132                     width > this.imageEl.OriginWidth || 
31133                     height > this.imageEl.OriginHeight ||
31134                     (width < this.minHeight && height < this.minWidth)
31135                 )
31136         ){
31137             return false;
31138         }
31139         
31140         if(
31141                 !this.isDocument &&
31142                 (this.rotate == 0 || this.rotate == 180) && 
31143                 (
31144                     width < this.minWidth || 
31145                     width > this.imageEl.OriginWidth || 
31146                     height < this.minHeight || 
31147                     height > this.imageEl.OriginHeight
31148                 )
31149         ){
31150             return false;
31151         }
31152         
31153         if(
31154                 !this.isDocument &&
31155                 (this.rotate == 90 || this.rotate == 270) && 
31156                 (
31157                     width < this.minHeight || 
31158                     width > this.imageEl.OriginWidth || 
31159                     height < this.minWidth || 
31160                     height > this.imageEl.OriginHeight
31161                 )
31162         ){
31163             return false;
31164         }
31165         
31166         return true;
31167         
31168     },
31169     
31170     onRotateLeft : function(e)
31171     {   
31172         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31173             
31174             var minScale = this.thumbEl.getWidth() / this.minWidth;
31175             
31176             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31177             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31178             
31179             this.startScale = this.scale;
31180             
31181             while (this.getScaleLevel() < minScale){
31182             
31183                 this.scale = this.scale + 1;
31184                 
31185                 if(!this.zoomable()){
31186                     break;
31187                 }
31188                 
31189                 if(
31190                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31191                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31192                 ){
31193                     continue;
31194                 }
31195                 
31196                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31197
31198                 this.draw();
31199                 
31200                 return;
31201             }
31202             
31203             this.scale = this.startScale;
31204             
31205             this.onRotateFail();
31206             
31207             return false;
31208         }
31209         
31210         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31211
31212         if(this.isDocument){
31213             this.setThumbBoxSize();
31214             this.setThumbBoxPosition();
31215             this.setCanvasPosition();
31216         }
31217         
31218         this.draw();
31219         
31220         this.fireEvent('rotate', this, 'left');
31221         
31222     },
31223     
31224     onRotateRight : function(e)
31225     {
31226         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31227             
31228             var minScale = this.thumbEl.getWidth() / this.minWidth;
31229         
31230             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31231             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31232             
31233             this.startScale = this.scale;
31234             
31235             while (this.getScaleLevel() < minScale){
31236             
31237                 this.scale = this.scale + 1;
31238                 
31239                 if(!this.zoomable()){
31240                     break;
31241                 }
31242                 
31243                 if(
31244                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31245                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31246                 ){
31247                     continue;
31248                 }
31249                 
31250                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31251
31252                 this.draw();
31253                 
31254                 return;
31255             }
31256             
31257             this.scale = this.startScale;
31258             
31259             this.onRotateFail();
31260             
31261             return false;
31262         }
31263         
31264         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31265
31266         if(this.isDocument){
31267             this.setThumbBoxSize();
31268             this.setThumbBoxPosition();
31269             this.setCanvasPosition();
31270         }
31271         
31272         this.draw();
31273         
31274         this.fireEvent('rotate', this, 'right');
31275     },
31276     
31277     onRotateFail : function()
31278     {
31279         this.errorEl.show(true);
31280         
31281         var _this = this;
31282         
31283         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31284     },
31285     
31286     draw : function()
31287     {
31288         this.previewEl.dom.innerHTML = '';
31289         
31290         var canvasEl = document.createElement("canvas");
31291         
31292         var contextEl = canvasEl.getContext("2d");
31293         
31294         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31295         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31296         var center = this.imageEl.OriginWidth / 2;
31297         
31298         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31299             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31300             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31301             center = this.imageEl.OriginHeight / 2;
31302         }
31303         
31304         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31305         
31306         contextEl.translate(center, center);
31307         contextEl.rotate(this.rotate * Math.PI / 180);
31308
31309         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31310         
31311         this.canvasEl = document.createElement("canvas");
31312         
31313         this.contextEl = this.canvasEl.getContext("2d");
31314         
31315         switch (this.rotate) {
31316             case 0 :
31317                 
31318                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31319                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31320                 
31321                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31322                 
31323                 break;
31324             case 90 : 
31325                 
31326                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31327                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31328                 
31329                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31330                     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);
31331                     break;
31332                 }
31333                 
31334                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31335                 
31336                 break;
31337             case 180 :
31338                 
31339                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31340                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31341                 
31342                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31343                     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);
31344                     break;
31345                 }
31346                 
31347                 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);
31348                 
31349                 break;
31350             case 270 :
31351                 
31352                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31353                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31354         
31355                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31356                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31357                     break;
31358                 }
31359                 
31360                 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);
31361                 
31362                 break;
31363             default : 
31364                 break;
31365         }
31366         
31367         this.previewEl.appendChild(this.canvasEl);
31368         
31369         this.setCanvasPosition();
31370     },
31371     
31372     crop : function()
31373     {
31374         if(!this.canvasLoaded){
31375             return;
31376         }
31377         
31378         var imageCanvas = document.createElement("canvas");
31379         
31380         var imageContext = imageCanvas.getContext("2d");
31381         
31382         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31383         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31384         
31385         var center = imageCanvas.width / 2;
31386         
31387         imageContext.translate(center, center);
31388         
31389         imageContext.rotate(this.rotate * Math.PI / 180);
31390         
31391         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31392         
31393         var canvas = document.createElement("canvas");
31394         
31395         var context = canvas.getContext("2d");
31396                 
31397         canvas.width = this.minWidth;
31398         canvas.height = this.minHeight;
31399
31400         switch (this.rotate) {
31401             case 0 :
31402                 
31403                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31404                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31405                 
31406                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31407                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31408                 
31409                 var targetWidth = this.minWidth - 2 * x;
31410                 var targetHeight = this.minHeight - 2 * y;
31411                 
31412                 var scale = 1;
31413                 
31414                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31415                     scale = targetWidth / width;
31416                 }
31417                 
31418                 if(x > 0 && y == 0){
31419                     scale = targetHeight / height;
31420                 }
31421                 
31422                 if(x > 0 && y > 0){
31423                     scale = targetWidth / width;
31424                     
31425                     if(width < height){
31426                         scale = targetHeight / height;
31427                     }
31428                 }
31429                 
31430                 context.scale(scale, scale);
31431                 
31432                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31433                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31434
31435                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31436                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31437
31438                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31439                 
31440                 break;
31441             case 90 : 
31442                 
31443                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31444                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31445                 
31446                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31447                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31448                 
31449                 var targetWidth = this.minWidth - 2 * x;
31450                 var targetHeight = this.minHeight - 2 * y;
31451                 
31452                 var scale = 1;
31453                 
31454                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31455                     scale = targetWidth / width;
31456                 }
31457                 
31458                 if(x > 0 && y == 0){
31459                     scale = targetHeight / height;
31460                 }
31461                 
31462                 if(x > 0 && y > 0){
31463                     scale = targetWidth / width;
31464                     
31465                     if(width < height){
31466                         scale = targetHeight / height;
31467                     }
31468                 }
31469                 
31470                 context.scale(scale, scale);
31471                 
31472                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31473                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31474
31475                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31476                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31477                 
31478                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31479                 
31480                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31481                 
31482                 break;
31483             case 180 :
31484                 
31485                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31486                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31487                 
31488                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31489                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31490                 
31491                 var targetWidth = this.minWidth - 2 * x;
31492                 var targetHeight = this.minHeight - 2 * y;
31493                 
31494                 var scale = 1;
31495                 
31496                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31497                     scale = targetWidth / width;
31498                 }
31499                 
31500                 if(x > 0 && y == 0){
31501                     scale = targetHeight / height;
31502                 }
31503                 
31504                 if(x > 0 && y > 0){
31505                     scale = targetWidth / width;
31506                     
31507                     if(width < height){
31508                         scale = targetHeight / height;
31509                     }
31510                 }
31511                 
31512                 context.scale(scale, scale);
31513                 
31514                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31515                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31516
31517                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31518                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31519
31520                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31521                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31522                 
31523                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31524                 
31525                 break;
31526             case 270 :
31527                 
31528                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31529                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31530                 
31531                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31532                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31533                 
31534                 var targetWidth = this.minWidth - 2 * x;
31535                 var targetHeight = this.minHeight - 2 * y;
31536                 
31537                 var scale = 1;
31538                 
31539                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31540                     scale = targetWidth / width;
31541                 }
31542                 
31543                 if(x > 0 && y == 0){
31544                     scale = targetHeight / height;
31545                 }
31546                 
31547                 if(x > 0 && y > 0){
31548                     scale = targetWidth / width;
31549                     
31550                     if(width < height){
31551                         scale = targetHeight / height;
31552                     }
31553                 }
31554                 
31555                 context.scale(scale, scale);
31556                 
31557                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31558                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31559
31560                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31561                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31562                 
31563                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31564                 
31565                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31566                 
31567                 break;
31568             default : 
31569                 break;
31570         }
31571         
31572         this.cropData = canvas.toDataURL(this.cropType);
31573         
31574         if(this.fireEvent('crop', this, this.cropData) !== false){
31575             this.process(this.file, this.cropData);
31576         }
31577         
31578         return;
31579         
31580     },
31581     
31582     setThumbBoxSize : function()
31583     {
31584         var width, height;
31585         
31586         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31587             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31588             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31589             
31590             this.minWidth = width;
31591             this.minHeight = height;
31592             
31593             if(this.rotate == 90 || this.rotate == 270){
31594                 this.minWidth = height;
31595                 this.minHeight = width;
31596             }
31597         }
31598         
31599         height = 300;
31600         width = Math.ceil(this.minWidth * height / this.minHeight);
31601         
31602         if(this.minWidth > this.minHeight){
31603             width = 300;
31604             height = Math.ceil(this.minHeight * width / this.minWidth);
31605         }
31606         
31607         this.thumbEl.setStyle({
31608             width : width + 'px',
31609             height : height + 'px'
31610         });
31611
31612         return;
31613             
31614     },
31615     
31616     setThumbBoxPosition : function()
31617     {
31618         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31619         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31620         
31621         this.thumbEl.setLeft(x);
31622         this.thumbEl.setTop(y);
31623         
31624     },
31625     
31626     baseRotateLevel : function()
31627     {
31628         this.baseRotate = 1;
31629         
31630         if(
31631                 typeof(this.exif) != 'undefined' &&
31632                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31633                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31634         ){
31635             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31636         }
31637         
31638         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31639         
31640     },
31641     
31642     baseScaleLevel : function()
31643     {
31644         var width, height;
31645         
31646         if(this.isDocument){
31647             
31648             if(this.baseRotate == 6 || this.baseRotate == 8){
31649             
31650                 height = this.thumbEl.getHeight();
31651                 this.baseScale = height / this.imageEl.OriginWidth;
31652
31653                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31654                     width = this.thumbEl.getWidth();
31655                     this.baseScale = width / this.imageEl.OriginHeight;
31656                 }
31657
31658                 return;
31659             }
31660
31661             height = this.thumbEl.getHeight();
31662             this.baseScale = height / this.imageEl.OriginHeight;
31663
31664             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31665                 width = this.thumbEl.getWidth();
31666                 this.baseScale = width / this.imageEl.OriginWidth;
31667             }
31668
31669             return;
31670         }
31671         
31672         if(this.baseRotate == 6 || this.baseRotate == 8){
31673             
31674             width = this.thumbEl.getHeight();
31675             this.baseScale = width / this.imageEl.OriginHeight;
31676             
31677             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31678                 height = this.thumbEl.getWidth();
31679                 this.baseScale = height / this.imageEl.OriginHeight;
31680             }
31681             
31682             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31683                 height = this.thumbEl.getWidth();
31684                 this.baseScale = height / this.imageEl.OriginHeight;
31685                 
31686                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31687                     width = this.thumbEl.getHeight();
31688                     this.baseScale = width / this.imageEl.OriginWidth;
31689                 }
31690             }
31691             
31692             return;
31693         }
31694         
31695         width = this.thumbEl.getWidth();
31696         this.baseScale = width / this.imageEl.OriginWidth;
31697         
31698         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31699             height = this.thumbEl.getHeight();
31700             this.baseScale = height / this.imageEl.OriginHeight;
31701         }
31702         
31703         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31704             
31705             height = this.thumbEl.getHeight();
31706             this.baseScale = height / this.imageEl.OriginHeight;
31707             
31708             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31709                 width = this.thumbEl.getWidth();
31710                 this.baseScale = width / this.imageEl.OriginWidth;
31711             }
31712             
31713         }
31714         
31715         return;
31716     },
31717     
31718     getScaleLevel : function()
31719     {
31720         return this.baseScale * Math.pow(1.1, this.scale);
31721     },
31722     
31723     onTouchStart : function(e)
31724     {
31725         if(!this.canvasLoaded){
31726             this.beforeSelectFile(e);
31727             return;
31728         }
31729         
31730         var touches = e.browserEvent.touches;
31731         
31732         if(!touches){
31733             return;
31734         }
31735         
31736         if(touches.length == 1){
31737             this.onMouseDown(e);
31738             return;
31739         }
31740         
31741         if(touches.length != 2){
31742             return;
31743         }
31744         
31745         var coords = [];
31746         
31747         for(var i = 0, finger; finger = touches[i]; i++){
31748             coords.push(finger.pageX, finger.pageY);
31749         }
31750         
31751         var x = Math.pow(coords[0] - coords[2], 2);
31752         var y = Math.pow(coords[1] - coords[3], 2);
31753         
31754         this.startDistance = Math.sqrt(x + y);
31755         
31756         this.startScale = this.scale;
31757         
31758         this.pinching = true;
31759         this.dragable = false;
31760         
31761     },
31762     
31763     onTouchMove : function(e)
31764     {
31765         if(!this.pinching && !this.dragable){
31766             return;
31767         }
31768         
31769         var touches = e.browserEvent.touches;
31770         
31771         if(!touches){
31772             return;
31773         }
31774         
31775         if(this.dragable){
31776             this.onMouseMove(e);
31777             return;
31778         }
31779         
31780         var coords = [];
31781         
31782         for(var i = 0, finger; finger = touches[i]; i++){
31783             coords.push(finger.pageX, finger.pageY);
31784         }
31785         
31786         var x = Math.pow(coords[0] - coords[2], 2);
31787         var y = Math.pow(coords[1] - coords[3], 2);
31788         
31789         this.endDistance = Math.sqrt(x + y);
31790         
31791         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31792         
31793         if(!this.zoomable()){
31794             this.scale = this.startScale;
31795             return;
31796         }
31797         
31798         this.draw();
31799         
31800     },
31801     
31802     onTouchEnd : function(e)
31803     {
31804         this.pinching = false;
31805         this.dragable = false;
31806         
31807     },
31808     
31809     process : function(file, crop)
31810     {
31811         if(this.loadMask){
31812             this.maskEl.mask(this.loadingText);
31813         }
31814         
31815         this.xhr = new XMLHttpRequest();
31816         
31817         file.xhr = this.xhr;
31818
31819         this.xhr.open(this.method, this.url, true);
31820         
31821         var headers = {
31822             "Accept": "application/json",
31823             "Cache-Control": "no-cache",
31824             "X-Requested-With": "XMLHttpRequest"
31825         };
31826         
31827         for (var headerName in headers) {
31828             var headerValue = headers[headerName];
31829             if (headerValue) {
31830                 this.xhr.setRequestHeader(headerName, headerValue);
31831             }
31832         }
31833         
31834         var _this = this;
31835         
31836         this.xhr.onload = function()
31837         {
31838             _this.xhrOnLoad(_this.xhr);
31839         }
31840         
31841         this.xhr.onerror = function()
31842         {
31843             _this.xhrOnError(_this.xhr);
31844         }
31845         
31846         var formData = new FormData();
31847
31848         formData.append('returnHTML', 'NO');
31849         
31850         if(crop){
31851             formData.append('crop', crop);
31852         }
31853         
31854         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31855             formData.append(this.paramName, file, file.name);
31856         }
31857         
31858         if(typeof(file.filename) != 'undefined'){
31859             formData.append('filename', file.filename);
31860         }
31861         
31862         if(typeof(file.mimetype) != 'undefined'){
31863             formData.append('mimetype', file.mimetype);
31864         }
31865         
31866         if(this.fireEvent('arrange', this, formData) != false){
31867             this.xhr.send(formData);
31868         };
31869     },
31870     
31871     xhrOnLoad : function(xhr)
31872     {
31873         if(this.loadMask){
31874             this.maskEl.unmask();
31875         }
31876         
31877         if (xhr.readyState !== 4) {
31878             this.fireEvent('exception', this, xhr);
31879             return;
31880         }
31881
31882         var response = Roo.decode(xhr.responseText);
31883         
31884         if(!response.success){
31885             this.fireEvent('exception', this, xhr);
31886             return;
31887         }
31888         
31889         var response = Roo.decode(xhr.responseText);
31890         
31891         this.fireEvent('upload', this, response);
31892         
31893     },
31894     
31895     xhrOnError : function()
31896     {
31897         if(this.loadMask){
31898             this.maskEl.unmask();
31899         }
31900         
31901         Roo.log('xhr on error');
31902         
31903         var response = Roo.decode(xhr.responseText);
31904           
31905         Roo.log(response);
31906         
31907     },
31908     
31909     prepare : function(file)
31910     {   
31911         if(this.loadMask){
31912             this.maskEl.mask(this.loadingText);
31913         }
31914         
31915         this.file = false;
31916         this.exif = {};
31917         
31918         if(typeof(file) === 'string'){
31919             this.loadCanvas(file);
31920             return;
31921         }
31922         
31923         if(!file || !this.urlAPI){
31924             return;
31925         }
31926         
31927         this.file = file;
31928         this.cropType = file.type;
31929         
31930         var _this = this;
31931         
31932         if(this.fireEvent('prepare', this, this.file) != false){
31933             
31934             var reader = new FileReader();
31935             
31936             reader.onload = function (e) {
31937                 if (e.target.error) {
31938                     Roo.log(e.target.error);
31939                     return;
31940                 }
31941                 
31942                 var buffer = e.target.result,
31943                     dataView = new DataView(buffer),
31944                     offset = 2,
31945                     maxOffset = dataView.byteLength - 4,
31946                     markerBytes,
31947                     markerLength;
31948                 
31949                 if (dataView.getUint16(0) === 0xffd8) {
31950                     while (offset < maxOffset) {
31951                         markerBytes = dataView.getUint16(offset);
31952                         
31953                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31954                             markerLength = dataView.getUint16(offset + 2) + 2;
31955                             if (offset + markerLength > dataView.byteLength) {
31956                                 Roo.log('Invalid meta data: Invalid segment size.');
31957                                 break;
31958                             }
31959                             
31960                             if(markerBytes == 0xffe1){
31961                                 _this.parseExifData(
31962                                     dataView,
31963                                     offset,
31964                                     markerLength
31965                                 );
31966                             }
31967                             
31968                             offset += markerLength;
31969                             
31970                             continue;
31971                         }
31972                         
31973                         break;
31974                     }
31975                     
31976                 }
31977                 
31978                 var url = _this.urlAPI.createObjectURL(_this.file);
31979                 
31980                 _this.loadCanvas(url);
31981                 
31982                 return;
31983             }
31984             
31985             reader.readAsArrayBuffer(this.file);
31986             
31987         }
31988         
31989     },
31990     
31991     parseExifData : function(dataView, offset, length)
31992     {
31993         var tiffOffset = offset + 10,
31994             littleEndian,
31995             dirOffset;
31996     
31997         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31998             // No Exif data, might be XMP data instead
31999             return;
32000         }
32001         
32002         // Check for the ASCII code for "Exif" (0x45786966):
32003         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32004             // No Exif data, might be XMP data instead
32005             return;
32006         }
32007         if (tiffOffset + 8 > dataView.byteLength) {
32008             Roo.log('Invalid Exif data: Invalid segment size.');
32009             return;
32010         }
32011         // Check for the two null bytes:
32012         if (dataView.getUint16(offset + 8) !== 0x0000) {
32013             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32014             return;
32015         }
32016         // Check the byte alignment:
32017         switch (dataView.getUint16(tiffOffset)) {
32018         case 0x4949:
32019             littleEndian = true;
32020             break;
32021         case 0x4D4D:
32022             littleEndian = false;
32023             break;
32024         default:
32025             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32026             return;
32027         }
32028         // Check for the TIFF tag marker (0x002A):
32029         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32030             Roo.log('Invalid Exif data: Missing TIFF marker.');
32031             return;
32032         }
32033         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32034         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32035         
32036         this.parseExifTags(
32037             dataView,
32038             tiffOffset,
32039             tiffOffset + dirOffset,
32040             littleEndian
32041         );
32042     },
32043     
32044     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32045     {
32046         var tagsNumber,
32047             dirEndOffset,
32048             i;
32049         if (dirOffset + 6 > dataView.byteLength) {
32050             Roo.log('Invalid Exif data: Invalid directory offset.');
32051             return;
32052         }
32053         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32054         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32055         if (dirEndOffset + 4 > dataView.byteLength) {
32056             Roo.log('Invalid Exif data: Invalid directory size.');
32057             return;
32058         }
32059         for (i = 0; i < tagsNumber; i += 1) {
32060             this.parseExifTag(
32061                 dataView,
32062                 tiffOffset,
32063                 dirOffset + 2 + 12 * i, // tag offset
32064                 littleEndian
32065             );
32066         }
32067         // Return the offset to the next directory:
32068         return dataView.getUint32(dirEndOffset, littleEndian);
32069     },
32070     
32071     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32072     {
32073         var tag = dataView.getUint16(offset, littleEndian);
32074         
32075         this.exif[tag] = this.getExifValue(
32076             dataView,
32077             tiffOffset,
32078             offset,
32079             dataView.getUint16(offset + 2, littleEndian), // tag type
32080             dataView.getUint32(offset + 4, littleEndian), // tag length
32081             littleEndian
32082         );
32083     },
32084     
32085     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32086     {
32087         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32088             tagSize,
32089             dataOffset,
32090             values,
32091             i,
32092             str,
32093             c;
32094     
32095         if (!tagType) {
32096             Roo.log('Invalid Exif data: Invalid tag type.');
32097             return;
32098         }
32099         
32100         tagSize = tagType.size * length;
32101         // Determine if the value is contained in the dataOffset bytes,
32102         // or if the value at the dataOffset is a pointer to the actual data:
32103         dataOffset = tagSize > 4 ?
32104                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32105         if (dataOffset + tagSize > dataView.byteLength) {
32106             Roo.log('Invalid Exif data: Invalid data offset.');
32107             return;
32108         }
32109         if (length === 1) {
32110             return tagType.getValue(dataView, dataOffset, littleEndian);
32111         }
32112         values = [];
32113         for (i = 0; i < length; i += 1) {
32114             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32115         }
32116         
32117         if (tagType.ascii) {
32118             str = '';
32119             // Concatenate the chars:
32120             for (i = 0; i < values.length; i += 1) {
32121                 c = values[i];
32122                 // Ignore the terminating NULL byte(s):
32123                 if (c === '\u0000') {
32124                     break;
32125                 }
32126                 str += c;
32127             }
32128             return str;
32129         }
32130         return values;
32131     }
32132     
32133 });
32134
32135 Roo.apply(Roo.bootstrap.UploadCropbox, {
32136     tags : {
32137         'Orientation': 0x0112
32138     },
32139     
32140     Orientation: {
32141             1: 0, //'top-left',
32142 //            2: 'top-right',
32143             3: 180, //'bottom-right',
32144 //            4: 'bottom-left',
32145 //            5: 'left-top',
32146             6: 90, //'right-top',
32147 //            7: 'right-bottom',
32148             8: 270 //'left-bottom'
32149     },
32150     
32151     exifTagTypes : {
32152         // byte, 8-bit unsigned int:
32153         1: {
32154             getValue: function (dataView, dataOffset) {
32155                 return dataView.getUint8(dataOffset);
32156             },
32157             size: 1
32158         },
32159         // ascii, 8-bit byte:
32160         2: {
32161             getValue: function (dataView, dataOffset) {
32162                 return String.fromCharCode(dataView.getUint8(dataOffset));
32163             },
32164             size: 1,
32165             ascii: true
32166         },
32167         // short, 16 bit int:
32168         3: {
32169             getValue: function (dataView, dataOffset, littleEndian) {
32170                 return dataView.getUint16(dataOffset, littleEndian);
32171             },
32172             size: 2
32173         },
32174         // long, 32 bit int:
32175         4: {
32176             getValue: function (dataView, dataOffset, littleEndian) {
32177                 return dataView.getUint32(dataOffset, littleEndian);
32178             },
32179             size: 4
32180         },
32181         // rational = two long values, first is numerator, second is denominator:
32182         5: {
32183             getValue: function (dataView, dataOffset, littleEndian) {
32184                 return dataView.getUint32(dataOffset, littleEndian) /
32185                     dataView.getUint32(dataOffset + 4, littleEndian);
32186             },
32187             size: 8
32188         },
32189         // slong, 32 bit signed int:
32190         9: {
32191             getValue: function (dataView, dataOffset, littleEndian) {
32192                 return dataView.getInt32(dataOffset, littleEndian);
32193             },
32194             size: 4
32195         },
32196         // srational, two slongs, first is numerator, second is denominator:
32197         10: {
32198             getValue: function (dataView, dataOffset, littleEndian) {
32199                 return dataView.getInt32(dataOffset, littleEndian) /
32200                     dataView.getInt32(dataOffset + 4, littleEndian);
32201             },
32202             size: 8
32203         }
32204     },
32205     
32206     footer : {
32207         STANDARD : [
32208             {
32209                 tag : 'div',
32210                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32211                 action : 'rotate-left',
32212                 cn : [
32213                     {
32214                         tag : 'button',
32215                         cls : 'btn btn-default',
32216                         html : '<i class="fa fa-undo"></i>'
32217                     }
32218                 ]
32219             },
32220             {
32221                 tag : 'div',
32222                 cls : 'btn-group roo-upload-cropbox-picture',
32223                 action : 'picture',
32224                 cn : [
32225                     {
32226                         tag : 'button',
32227                         cls : 'btn btn-default',
32228                         html : '<i class="fa fa-picture-o"></i>'
32229                     }
32230                 ]
32231             },
32232             {
32233                 tag : 'div',
32234                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32235                 action : 'rotate-right',
32236                 cn : [
32237                     {
32238                         tag : 'button',
32239                         cls : 'btn btn-default',
32240                         html : '<i class="fa fa-repeat"></i>'
32241                     }
32242                 ]
32243             }
32244         ],
32245         DOCUMENT : [
32246             {
32247                 tag : 'div',
32248                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32249                 action : 'rotate-left',
32250                 cn : [
32251                     {
32252                         tag : 'button',
32253                         cls : 'btn btn-default',
32254                         html : '<i class="fa fa-undo"></i>'
32255                     }
32256                 ]
32257             },
32258             {
32259                 tag : 'div',
32260                 cls : 'btn-group roo-upload-cropbox-download',
32261                 action : 'download',
32262                 cn : [
32263                     {
32264                         tag : 'button',
32265                         cls : 'btn btn-default',
32266                         html : '<i class="fa fa-download"></i>'
32267                     }
32268                 ]
32269             },
32270             {
32271                 tag : 'div',
32272                 cls : 'btn-group roo-upload-cropbox-crop',
32273                 action : 'crop',
32274                 cn : [
32275                     {
32276                         tag : 'button',
32277                         cls : 'btn btn-default',
32278                         html : '<i class="fa fa-crop"></i>'
32279                     }
32280                 ]
32281             },
32282             {
32283                 tag : 'div',
32284                 cls : 'btn-group roo-upload-cropbox-trash',
32285                 action : 'trash',
32286                 cn : [
32287                     {
32288                         tag : 'button',
32289                         cls : 'btn btn-default',
32290                         html : '<i class="fa fa-trash"></i>'
32291                     }
32292                 ]
32293             },
32294             {
32295                 tag : 'div',
32296                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32297                 action : 'rotate-right',
32298                 cn : [
32299                     {
32300                         tag : 'button',
32301                         cls : 'btn btn-default',
32302                         html : '<i class="fa fa-repeat"></i>'
32303                     }
32304                 ]
32305             }
32306         ],
32307         ROTATOR : [
32308             {
32309                 tag : 'div',
32310                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32311                 action : 'rotate-left',
32312                 cn : [
32313                     {
32314                         tag : 'button',
32315                         cls : 'btn btn-default',
32316                         html : '<i class="fa fa-undo"></i>'
32317                     }
32318                 ]
32319             },
32320             {
32321                 tag : 'div',
32322                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32323                 action : 'rotate-right',
32324                 cn : [
32325                     {
32326                         tag : 'button',
32327                         cls : 'btn btn-default',
32328                         html : '<i class="fa fa-repeat"></i>'
32329                     }
32330                 ]
32331             }
32332         ]
32333     }
32334 });
32335
32336 /*
32337 * Licence: LGPL
32338 */
32339
32340 /**
32341  * @class Roo.bootstrap.DocumentManager
32342  * @extends Roo.bootstrap.Component
32343  * Bootstrap DocumentManager class
32344  * @cfg {String} paramName default 'imageUpload'
32345  * @cfg {String} toolTipName default 'filename'
32346  * @cfg {String} method default POST
32347  * @cfg {String} url action url
32348  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32349  * @cfg {Boolean} multiple multiple upload default true
32350  * @cfg {Number} thumbSize default 300
32351  * @cfg {String} fieldLabel
32352  * @cfg {Number} labelWidth default 4
32353  * @cfg {String} labelAlign (left|top) default left
32354  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32355 * @cfg {Number} labellg set the width of label (1-12)
32356  * @cfg {Number} labelmd set the width of label (1-12)
32357  * @cfg {Number} labelsm set the width of label (1-12)
32358  * @cfg {Number} labelxs set the width of label (1-12)
32359  * 
32360  * @constructor
32361  * Create a new DocumentManager
32362  * @param {Object} config The config object
32363  */
32364
32365 Roo.bootstrap.DocumentManager = function(config){
32366     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32367     
32368     this.files = [];
32369     this.delegates = [];
32370     
32371     this.addEvents({
32372         /**
32373          * @event initial
32374          * Fire when initial the DocumentManager
32375          * @param {Roo.bootstrap.DocumentManager} this
32376          */
32377         "initial" : true,
32378         /**
32379          * @event inspect
32380          * inspect selected file
32381          * @param {Roo.bootstrap.DocumentManager} this
32382          * @param {File} file
32383          */
32384         "inspect" : true,
32385         /**
32386          * @event exception
32387          * Fire when xhr load exception
32388          * @param {Roo.bootstrap.DocumentManager} this
32389          * @param {XMLHttpRequest} xhr
32390          */
32391         "exception" : true,
32392         /**
32393          * @event afterupload
32394          * Fire when xhr load exception
32395          * @param {Roo.bootstrap.DocumentManager} this
32396          * @param {XMLHttpRequest} xhr
32397          */
32398         "afterupload" : true,
32399         /**
32400          * @event prepare
32401          * prepare the form data
32402          * @param {Roo.bootstrap.DocumentManager} this
32403          * @param {Object} formData
32404          */
32405         "prepare" : true,
32406         /**
32407          * @event remove
32408          * Fire when remove the file
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          * @param {Object} file
32411          */
32412         "remove" : true,
32413         /**
32414          * @event refresh
32415          * Fire after refresh the file
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          */
32418         "refresh" : true,
32419         /**
32420          * @event click
32421          * Fire after click the image
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {Object} file
32424          */
32425         "click" : true,
32426         /**
32427          * @event edit
32428          * Fire when upload a image and editable set to true
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {Object} file
32431          */
32432         "edit" : true,
32433         /**
32434          * @event beforeselectfile
32435          * Fire before select file
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          */
32438         "beforeselectfile" : true,
32439         /**
32440          * @event process
32441          * Fire before process file
32442          * @param {Roo.bootstrap.DocumentManager} this
32443          * @param {Object} file
32444          */
32445         "process" : true,
32446         /**
32447          * @event previewrendered
32448          * Fire when preview rendered
32449          * @param {Roo.bootstrap.DocumentManager} this
32450          * @param {Object} file
32451          */
32452         "previewrendered" : true,
32453         /**
32454          */
32455         "previewResize" : true
32456         
32457     });
32458 };
32459
32460 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32461     
32462     boxes : 0,
32463     inputName : '',
32464     thumbSize : 300,
32465     multiple : true,
32466     files : false,
32467     method : 'POST',
32468     url : '',
32469     paramName : 'imageUpload',
32470     toolTipName : 'filename',
32471     fieldLabel : '',
32472     labelWidth : 4,
32473     labelAlign : 'left',
32474     editable : true,
32475     delegates : false,
32476     xhr : false, 
32477     
32478     labellg : 0,
32479     labelmd : 0,
32480     labelsm : 0,
32481     labelxs : 0,
32482     
32483     getAutoCreate : function()
32484     {   
32485         var managerWidget = {
32486             tag : 'div',
32487             cls : 'roo-document-manager',
32488             cn : [
32489                 {
32490                     tag : 'input',
32491                     cls : 'roo-document-manager-selector',
32492                     type : 'file'
32493                 },
32494                 {
32495                     tag : 'div',
32496                     cls : 'roo-document-manager-uploader',
32497                     cn : [
32498                         {
32499                             tag : 'div',
32500                             cls : 'roo-document-manager-upload-btn',
32501                             html : '<i class="fa fa-plus"></i>'
32502                         }
32503                     ]
32504                     
32505                 }
32506             ]
32507         };
32508         
32509         var content = [
32510             {
32511                 tag : 'div',
32512                 cls : 'column col-md-12',
32513                 cn : managerWidget
32514             }
32515         ];
32516         
32517         if(this.fieldLabel.length){
32518             
32519             content = [
32520                 {
32521                     tag : 'div',
32522                     cls : 'column col-md-12',
32523                     html : this.fieldLabel
32524                 },
32525                 {
32526                     tag : 'div',
32527                     cls : 'column col-md-12',
32528                     cn : managerWidget
32529                 }
32530             ];
32531
32532             if(this.labelAlign == 'left'){
32533                 content = [
32534                     {
32535                         tag : 'div',
32536                         cls : 'column',
32537                         html : this.fieldLabel
32538                     },
32539                     {
32540                         tag : 'div',
32541                         cls : 'column',
32542                         cn : managerWidget
32543                     }
32544                 ];
32545                 
32546                 if(this.labelWidth > 12){
32547                     content[0].style = "width: " + this.labelWidth + 'px';
32548                 }
32549
32550                 if(this.labelWidth < 13 && this.labelmd == 0){
32551                     this.labelmd = this.labelWidth;
32552                 }
32553
32554                 if(this.labellg > 0){
32555                     content[0].cls += ' col-lg-' + this.labellg;
32556                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32557                 }
32558
32559                 if(this.labelmd > 0){
32560                     content[0].cls += ' col-md-' + this.labelmd;
32561                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32562                 }
32563
32564                 if(this.labelsm > 0){
32565                     content[0].cls += ' col-sm-' + this.labelsm;
32566                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32567                 }
32568
32569                 if(this.labelxs > 0){
32570                     content[0].cls += ' col-xs-' + this.labelxs;
32571                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32572                 }
32573                 
32574             }
32575         }
32576         
32577         var cfg = {
32578             tag : 'div',
32579             cls : 'row clearfix',
32580             cn : content
32581         };
32582         
32583         return cfg;
32584         
32585     },
32586     
32587     initEvents : function()
32588     {
32589         this.managerEl = this.el.select('.roo-document-manager', true).first();
32590         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32591         
32592         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32593         this.selectorEl.hide();
32594         
32595         if(this.multiple){
32596             this.selectorEl.attr('multiple', 'multiple');
32597         }
32598         
32599         this.selectorEl.on('change', this.onFileSelected, this);
32600         
32601         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32602         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32603         
32604         this.uploader.on('click', this.onUploaderClick, this);
32605         
32606         this.renderProgressDialog();
32607         
32608         var _this = this;
32609         
32610         window.addEventListener("resize", function() { _this.refresh(); } );
32611         
32612         this.fireEvent('initial', this);
32613     },
32614     
32615     renderProgressDialog : function()
32616     {
32617         var _this = this;
32618         
32619         this.progressDialog = new Roo.bootstrap.Modal({
32620             cls : 'roo-document-manager-progress-dialog',
32621             allow_close : false,
32622             animate : false,
32623             title : '',
32624             buttons : [
32625                 {
32626                     name  :'cancel',
32627                     weight : 'danger',
32628                     html : 'Cancel'
32629                 }
32630             ], 
32631             listeners : { 
32632                 btnclick : function() {
32633                     _this.uploadCancel();
32634                     this.hide();
32635                 }
32636             }
32637         });
32638          
32639         this.progressDialog.render(Roo.get(document.body));
32640          
32641         this.progress = new Roo.bootstrap.Progress({
32642             cls : 'roo-document-manager-progress',
32643             active : true,
32644             striped : true
32645         });
32646         
32647         this.progress.render(this.progressDialog.getChildContainer());
32648         
32649         this.progressBar = new Roo.bootstrap.ProgressBar({
32650             cls : 'roo-document-manager-progress-bar',
32651             aria_valuenow : 0,
32652             aria_valuemin : 0,
32653             aria_valuemax : 12,
32654             panel : 'success'
32655         });
32656         
32657         this.progressBar.render(this.progress.getChildContainer());
32658     },
32659     
32660     onUploaderClick : function(e)
32661     {
32662         e.preventDefault();
32663      
32664         if(this.fireEvent('beforeselectfile', this) != false){
32665             this.selectorEl.dom.click();
32666         }
32667         
32668     },
32669     
32670     onFileSelected : function(e)
32671     {
32672         e.preventDefault();
32673         
32674         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32675             return;
32676         }
32677         
32678         Roo.each(this.selectorEl.dom.files, function(file){
32679             if(this.fireEvent('inspect', this, file) != false){
32680                 this.files.push(file);
32681             }
32682         }, this);
32683         
32684         this.queue();
32685         
32686     },
32687     
32688     queue : function()
32689     {
32690         this.selectorEl.dom.value = '';
32691         
32692         if(!this.files || !this.files.length){
32693             return;
32694         }
32695         
32696         if(this.boxes > 0 && this.files.length > this.boxes){
32697             this.files = this.files.slice(0, this.boxes);
32698         }
32699         
32700         this.uploader.show();
32701         
32702         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32703             this.uploader.hide();
32704         }
32705         
32706         var _this = this;
32707         
32708         var files = [];
32709         
32710         var docs = [];
32711         
32712         Roo.each(this.files, function(file){
32713             
32714             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32715                 var f = this.renderPreview(file);
32716                 files.push(f);
32717                 return;
32718             }
32719             
32720             if(file.type.indexOf('image') != -1){
32721                 this.delegates.push(
32722                     (function(){
32723                         _this.process(file);
32724                     }).createDelegate(this)
32725                 );
32726         
32727                 return;
32728             }
32729             
32730             docs.push(
32731                 (function(){
32732                     _this.process(file);
32733                 }).createDelegate(this)
32734             );
32735             
32736         }, this);
32737         
32738         this.files = files;
32739         
32740         this.delegates = this.delegates.concat(docs);
32741         
32742         if(!this.delegates.length){
32743             this.refresh();
32744             return;
32745         }
32746         
32747         this.progressBar.aria_valuemax = this.delegates.length;
32748         
32749         this.arrange();
32750         
32751         return;
32752     },
32753     
32754     arrange : function()
32755     {
32756         if(!this.delegates.length){
32757             this.progressDialog.hide();
32758             this.refresh();
32759             return;
32760         }
32761         
32762         var delegate = this.delegates.shift();
32763         
32764         this.progressDialog.show();
32765         
32766         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32767         
32768         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32769         
32770         delegate();
32771     },
32772     
32773     refresh : function()
32774     {
32775         this.uploader.show();
32776         
32777         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32778             this.uploader.hide();
32779         }
32780         
32781         Roo.isTouch ? this.closable(false) : this.closable(true);
32782         
32783         this.fireEvent('refresh', this);
32784     },
32785     
32786     onRemove : function(e, el, o)
32787     {
32788         e.preventDefault();
32789         
32790         this.fireEvent('remove', this, o);
32791         
32792     },
32793     
32794     remove : function(o)
32795     {
32796         var files = [];
32797         
32798         Roo.each(this.files, function(file){
32799             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32800                 files.push(file);
32801                 return;
32802             }
32803
32804             o.target.remove();
32805
32806         }, this);
32807         
32808         this.files = files;
32809         
32810         this.refresh();
32811     },
32812     
32813     clear : function()
32814     {
32815         Roo.each(this.files, function(file){
32816             if(!file.target){
32817                 return;
32818             }
32819             
32820             file.target.remove();
32821
32822         }, this);
32823         
32824         this.files = [];
32825         
32826         this.refresh();
32827     },
32828     
32829     onClick : function(e, el, o)
32830     {
32831         e.preventDefault();
32832         
32833         this.fireEvent('click', this, o);
32834         
32835     },
32836     
32837     closable : function(closable)
32838     {
32839         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32840             
32841             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32842             
32843             if(closable){
32844                 el.show();
32845                 return;
32846             }
32847             
32848             el.hide();
32849             
32850         }, this);
32851     },
32852     
32853     xhrOnLoad : function(xhr)
32854     {
32855         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32856             el.remove();
32857         }, this);
32858         
32859         if (xhr.readyState !== 4) {
32860             this.arrange();
32861             this.fireEvent('exception', this, xhr);
32862             return;
32863         }
32864
32865         var response = Roo.decode(xhr.responseText);
32866         
32867         if(!response.success){
32868             this.arrange();
32869             this.fireEvent('exception', this, xhr);
32870             return;
32871         }
32872         
32873         var file = this.renderPreview(response.data);
32874         
32875         this.files.push(file);
32876         
32877         this.arrange();
32878         
32879         this.fireEvent('afterupload', this, xhr);
32880         
32881     },
32882     
32883     xhrOnError : function(xhr)
32884     {
32885         Roo.log('xhr on error');
32886         
32887         var response = Roo.decode(xhr.responseText);
32888           
32889         Roo.log(response);
32890         
32891         this.arrange();
32892     },
32893     
32894     process : function(file)
32895     {
32896         if(this.fireEvent('process', this, file) !== false){
32897             if(this.editable && file.type.indexOf('image') != -1){
32898                 this.fireEvent('edit', this, file);
32899                 return;
32900             }
32901
32902             this.uploadStart(file, false);
32903
32904             return;
32905         }
32906         
32907     },
32908     
32909     uploadStart : function(file, crop)
32910     {
32911         this.xhr = new XMLHttpRequest();
32912         
32913         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32914             this.arrange();
32915             return;
32916         }
32917         
32918         file.xhr = this.xhr;
32919             
32920         this.managerEl.createChild({
32921             tag : 'div',
32922             cls : 'roo-document-manager-loading',
32923             cn : [
32924                 {
32925                     tag : 'div',
32926                     tooltip : file.name,
32927                     cls : 'roo-document-manager-thumb',
32928                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32929                 }
32930             ]
32931
32932         });
32933
32934         this.xhr.open(this.method, this.url, true);
32935         
32936         var headers = {
32937             "Accept": "application/json",
32938             "Cache-Control": "no-cache",
32939             "X-Requested-With": "XMLHttpRequest"
32940         };
32941         
32942         for (var headerName in headers) {
32943             var headerValue = headers[headerName];
32944             if (headerValue) {
32945                 this.xhr.setRequestHeader(headerName, headerValue);
32946             }
32947         }
32948         
32949         var _this = this;
32950         
32951         this.xhr.onload = function()
32952         {
32953             _this.xhrOnLoad(_this.xhr);
32954         }
32955         
32956         this.xhr.onerror = function()
32957         {
32958             _this.xhrOnError(_this.xhr);
32959         }
32960         
32961         var formData = new FormData();
32962
32963         formData.append('returnHTML', 'NO');
32964         
32965         if(crop){
32966             formData.append('crop', crop);
32967         }
32968         
32969         formData.append(this.paramName, file, file.name);
32970         
32971         var options = {
32972             file : file, 
32973             manually : false
32974         };
32975         
32976         if(this.fireEvent('prepare', this, formData, options) != false){
32977             
32978             if(options.manually){
32979                 return;
32980             }
32981             
32982             this.xhr.send(formData);
32983             return;
32984         };
32985         
32986         this.uploadCancel();
32987     },
32988     
32989     uploadCancel : function()
32990     {
32991         if (this.xhr) {
32992             this.xhr.abort();
32993         }
32994         
32995         this.delegates = [];
32996         
32997         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32998             el.remove();
32999         }, this);
33000         
33001         this.arrange();
33002     },
33003     
33004     renderPreview : function(file)
33005     {
33006         if(typeof(file.target) != 'undefined' && file.target){
33007             return file;
33008         }
33009         
33010         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33011         
33012         var previewEl = this.managerEl.createChild({
33013             tag : 'div',
33014             cls : 'roo-document-manager-preview',
33015             cn : [
33016                 {
33017                     tag : 'div',
33018                     tooltip : file[this.toolTipName],
33019                     cls : 'roo-document-manager-thumb',
33020                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33021                 },
33022                 {
33023                     tag : 'button',
33024                     cls : 'close',
33025                     html : '<i class="fa fa-times-circle"></i>'
33026                 }
33027             ]
33028         });
33029
33030         var close = previewEl.select('button.close', true).first();
33031
33032         close.on('click', this.onRemove, this, file);
33033
33034         file.target = previewEl;
33035
33036         var image = previewEl.select('img', true).first();
33037         
33038         var _this = this;
33039         
33040         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33041         
33042         image.on('click', this.onClick, this, file);
33043         
33044         this.fireEvent('previewrendered', this, file);
33045         
33046         return file;
33047         
33048     },
33049     
33050     onPreviewLoad : function(file, image)
33051     {
33052         if(typeof(file.target) == 'undefined' || !file.target){
33053             return;
33054         }
33055         
33056         var width = image.dom.naturalWidth || image.dom.width;
33057         var height = image.dom.naturalHeight || image.dom.height;
33058         
33059         if(!this.previewResize) {
33060             return;
33061         }
33062         
33063         if(width > height){
33064             file.target.addClass('wide');
33065             return;
33066         }
33067         
33068         file.target.addClass('tall');
33069         return;
33070         
33071     },
33072     
33073     uploadFromSource : function(file, crop)
33074     {
33075         this.xhr = new XMLHttpRequest();
33076         
33077         this.managerEl.createChild({
33078             tag : 'div',
33079             cls : 'roo-document-manager-loading',
33080             cn : [
33081                 {
33082                     tag : 'div',
33083                     tooltip : file.name,
33084                     cls : 'roo-document-manager-thumb',
33085                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33086                 }
33087             ]
33088
33089         });
33090
33091         this.xhr.open(this.method, this.url, true);
33092         
33093         var headers = {
33094             "Accept": "application/json",
33095             "Cache-Control": "no-cache",
33096             "X-Requested-With": "XMLHttpRequest"
33097         };
33098         
33099         for (var headerName in headers) {
33100             var headerValue = headers[headerName];
33101             if (headerValue) {
33102                 this.xhr.setRequestHeader(headerName, headerValue);
33103             }
33104         }
33105         
33106         var _this = this;
33107         
33108         this.xhr.onload = function()
33109         {
33110             _this.xhrOnLoad(_this.xhr);
33111         }
33112         
33113         this.xhr.onerror = function()
33114         {
33115             _this.xhrOnError(_this.xhr);
33116         }
33117         
33118         var formData = new FormData();
33119
33120         formData.append('returnHTML', 'NO');
33121         
33122         formData.append('crop', crop);
33123         
33124         if(typeof(file.filename) != 'undefined'){
33125             formData.append('filename', file.filename);
33126         }
33127         
33128         if(typeof(file.mimetype) != 'undefined'){
33129             formData.append('mimetype', file.mimetype);
33130         }
33131         
33132         Roo.log(formData);
33133         
33134         if(this.fireEvent('prepare', this, formData) != false){
33135             this.xhr.send(formData);
33136         };
33137     }
33138 });
33139
33140 /*
33141 * Licence: LGPL
33142 */
33143
33144 /**
33145  * @class Roo.bootstrap.DocumentViewer
33146  * @extends Roo.bootstrap.Component
33147  * Bootstrap DocumentViewer class
33148  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33149  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33150  * 
33151  * @constructor
33152  * Create a new DocumentViewer
33153  * @param {Object} config The config object
33154  */
33155
33156 Roo.bootstrap.DocumentViewer = function(config){
33157     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33158     
33159     this.addEvents({
33160         /**
33161          * @event initial
33162          * Fire after initEvent
33163          * @param {Roo.bootstrap.DocumentViewer} this
33164          */
33165         "initial" : true,
33166         /**
33167          * @event click
33168          * Fire after click
33169          * @param {Roo.bootstrap.DocumentViewer} this
33170          */
33171         "click" : true,
33172         /**
33173          * @event download
33174          * Fire after download button
33175          * @param {Roo.bootstrap.DocumentViewer} this
33176          */
33177         "download" : true,
33178         /**
33179          * @event trash
33180          * Fire after trash button
33181          * @param {Roo.bootstrap.DocumentViewer} this
33182          */
33183         "trash" : true
33184         
33185     });
33186 };
33187
33188 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33189     
33190     showDownload : true,
33191     
33192     showTrash : true,
33193     
33194     getAutoCreate : function()
33195     {
33196         var cfg = {
33197             tag : 'div',
33198             cls : 'roo-document-viewer',
33199             cn : [
33200                 {
33201                     tag : 'div',
33202                     cls : 'roo-document-viewer-body',
33203                     cn : [
33204                         {
33205                             tag : 'div',
33206                             cls : 'roo-document-viewer-thumb',
33207                             cn : [
33208                                 {
33209                                     tag : 'img',
33210                                     cls : 'roo-document-viewer-image'
33211                                 }
33212                             ]
33213                         }
33214                     ]
33215                 },
33216                 {
33217                     tag : 'div',
33218                     cls : 'roo-document-viewer-footer',
33219                     cn : {
33220                         tag : 'div',
33221                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33222                         cn : [
33223                             {
33224                                 tag : 'div',
33225                                 cls : 'btn-group roo-document-viewer-download',
33226                                 cn : [
33227                                     {
33228                                         tag : 'button',
33229                                         cls : 'btn btn-default',
33230                                         html : '<i class="fa fa-download"></i>'
33231                                     }
33232                                 ]
33233                             },
33234                             {
33235                                 tag : 'div',
33236                                 cls : 'btn-group roo-document-viewer-trash',
33237                                 cn : [
33238                                     {
33239                                         tag : 'button',
33240                                         cls : 'btn btn-default',
33241                                         html : '<i class="fa fa-trash"></i>'
33242                                     }
33243                                 ]
33244                             }
33245                         ]
33246                     }
33247                 }
33248             ]
33249         };
33250         
33251         return cfg;
33252     },
33253     
33254     initEvents : function()
33255     {
33256         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33257         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33258         
33259         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33260         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33261         
33262         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33263         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33264         
33265         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33266         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33267         
33268         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33269         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33270         
33271         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33272         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33273         
33274         this.bodyEl.on('click', this.onClick, this);
33275         this.downloadBtn.on('click', this.onDownload, this);
33276         this.trashBtn.on('click', this.onTrash, this);
33277         
33278         this.downloadBtn.hide();
33279         this.trashBtn.hide();
33280         
33281         if(this.showDownload){
33282             this.downloadBtn.show();
33283         }
33284         
33285         if(this.showTrash){
33286             this.trashBtn.show();
33287         }
33288         
33289         if(!this.showDownload && !this.showTrash) {
33290             this.footerEl.hide();
33291         }
33292         
33293     },
33294     
33295     initial : function()
33296     {
33297         this.fireEvent('initial', this);
33298         
33299     },
33300     
33301     onClick : function(e)
33302     {
33303         e.preventDefault();
33304         
33305         this.fireEvent('click', this);
33306     },
33307     
33308     onDownload : function(e)
33309     {
33310         e.preventDefault();
33311         
33312         this.fireEvent('download', this);
33313     },
33314     
33315     onTrash : function(e)
33316     {
33317         e.preventDefault();
33318         
33319         this.fireEvent('trash', this);
33320     }
33321     
33322 });
33323 /*
33324  * - LGPL
33325  *
33326  * nav progress bar
33327  * 
33328  */
33329
33330 /**
33331  * @class Roo.bootstrap.NavProgressBar
33332  * @extends Roo.bootstrap.Component
33333  * Bootstrap NavProgressBar class
33334  * 
33335  * @constructor
33336  * Create a new nav progress bar
33337  * @param {Object} config The config object
33338  */
33339
33340 Roo.bootstrap.NavProgressBar = function(config){
33341     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33342
33343     this.bullets = this.bullets || [];
33344    
33345 //    Roo.bootstrap.NavProgressBar.register(this);
33346      this.addEvents({
33347         /**
33348              * @event changed
33349              * Fires when the active item changes
33350              * @param {Roo.bootstrap.NavProgressBar} this
33351              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33352              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33353          */
33354         'changed': true
33355      });
33356     
33357 };
33358
33359 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33360     
33361     bullets : [],
33362     barItems : [],
33363     
33364     getAutoCreate : function()
33365     {
33366         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33367         
33368         cfg = {
33369             tag : 'div',
33370             cls : 'roo-navigation-bar-group',
33371             cn : [
33372                 {
33373                     tag : 'div',
33374                     cls : 'roo-navigation-top-bar'
33375                 },
33376                 {
33377                     tag : 'div',
33378                     cls : 'roo-navigation-bullets-bar',
33379                     cn : [
33380                         {
33381                             tag : 'ul',
33382                             cls : 'roo-navigation-bar'
33383                         }
33384                     ]
33385                 },
33386                 
33387                 {
33388                     tag : 'div',
33389                     cls : 'roo-navigation-bottom-bar'
33390                 }
33391             ]
33392             
33393         };
33394         
33395         return cfg;
33396         
33397     },
33398     
33399     initEvents: function() 
33400     {
33401         
33402     },
33403     
33404     onRender : function(ct, position) 
33405     {
33406         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33407         
33408         if(this.bullets.length){
33409             Roo.each(this.bullets, function(b){
33410                this.addItem(b);
33411             }, this);
33412         }
33413         
33414         this.format();
33415         
33416     },
33417     
33418     addItem : function(cfg)
33419     {
33420         var item = new Roo.bootstrap.NavProgressItem(cfg);
33421         
33422         item.parentId = this.id;
33423         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33424         
33425         if(cfg.html){
33426             var top = new Roo.bootstrap.Element({
33427                 tag : 'div',
33428                 cls : 'roo-navigation-bar-text'
33429             });
33430             
33431             var bottom = new Roo.bootstrap.Element({
33432                 tag : 'div',
33433                 cls : 'roo-navigation-bar-text'
33434             });
33435             
33436             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33437             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33438             
33439             var topText = new Roo.bootstrap.Element({
33440                 tag : 'span',
33441                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33442             });
33443             
33444             var bottomText = new Roo.bootstrap.Element({
33445                 tag : 'span',
33446                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33447             });
33448             
33449             topText.onRender(top.el, null);
33450             bottomText.onRender(bottom.el, null);
33451             
33452             item.topEl = top;
33453             item.bottomEl = bottom;
33454         }
33455         
33456         this.barItems.push(item);
33457         
33458         return item;
33459     },
33460     
33461     getActive : function()
33462     {
33463         var active = false;
33464         
33465         Roo.each(this.barItems, function(v){
33466             
33467             if (!v.isActive()) {
33468                 return;
33469             }
33470             
33471             active = v;
33472             return false;
33473             
33474         });
33475         
33476         return active;
33477     },
33478     
33479     setActiveItem : function(item)
33480     {
33481         var prev = false;
33482         
33483         Roo.each(this.barItems, function(v){
33484             if (v.rid == item.rid) {
33485                 return ;
33486             }
33487             
33488             if (v.isActive()) {
33489                 v.setActive(false);
33490                 prev = v;
33491             }
33492         });
33493
33494         item.setActive(true);
33495         
33496         this.fireEvent('changed', this, item, prev);
33497     },
33498     
33499     getBarItem: function(rid)
33500     {
33501         var ret = false;
33502         
33503         Roo.each(this.barItems, function(e) {
33504             if (e.rid != rid) {
33505                 return;
33506             }
33507             
33508             ret =  e;
33509             return false;
33510         });
33511         
33512         return ret;
33513     },
33514     
33515     indexOfItem : function(item)
33516     {
33517         var index = false;
33518         
33519         Roo.each(this.barItems, function(v, i){
33520             
33521             if (v.rid != item.rid) {
33522                 return;
33523             }
33524             
33525             index = i;
33526             return false
33527         });
33528         
33529         return index;
33530     },
33531     
33532     setActiveNext : function()
33533     {
33534         var i = this.indexOfItem(this.getActive());
33535         
33536         if (i > this.barItems.length) {
33537             return;
33538         }
33539         
33540         this.setActiveItem(this.barItems[i+1]);
33541     },
33542     
33543     setActivePrev : function()
33544     {
33545         var i = this.indexOfItem(this.getActive());
33546         
33547         if (i  < 1) {
33548             return;
33549         }
33550         
33551         this.setActiveItem(this.barItems[i-1]);
33552     },
33553     
33554     format : function()
33555     {
33556         if(!this.barItems.length){
33557             return;
33558         }
33559      
33560         var width = 100 / this.barItems.length;
33561         
33562         Roo.each(this.barItems, function(i){
33563             i.el.setStyle('width', width + '%');
33564             i.topEl.el.setStyle('width', width + '%');
33565             i.bottomEl.el.setStyle('width', width + '%');
33566         }, this);
33567         
33568     }
33569     
33570 });
33571 /*
33572  * - LGPL
33573  *
33574  * Nav Progress Item
33575  * 
33576  */
33577
33578 /**
33579  * @class Roo.bootstrap.NavProgressItem
33580  * @extends Roo.bootstrap.Component
33581  * Bootstrap NavProgressItem class
33582  * @cfg {String} rid the reference id
33583  * @cfg {Boolean} active (true|false) Is item active default false
33584  * @cfg {Boolean} disabled (true|false) Is item active default false
33585  * @cfg {String} html
33586  * @cfg {String} position (top|bottom) text position default bottom
33587  * @cfg {String} icon show icon instead of number
33588  * 
33589  * @constructor
33590  * Create a new NavProgressItem
33591  * @param {Object} config The config object
33592  */
33593 Roo.bootstrap.NavProgressItem = function(config){
33594     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33595     this.addEvents({
33596         // raw events
33597         /**
33598          * @event click
33599          * The raw click event for the entire grid.
33600          * @param {Roo.bootstrap.NavProgressItem} this
33601          * @param {Roo.EventObject} e
33602          */
33603         "click" : true
33604     });
33605    
33606 };
33607
33608 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33609     
33610     rid : '',
33611     active : false,
33612     disabled : false,
33613     html : '',
33614     position : 'bottom',
33615     icon : false,
33616     
33617     getAutoCreate : function()
33618     {
33619         var iconCls = 'roo-navigation-bar-item-icon';
33620         
33621         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33622         
33623         var cfg = {
33624             tag: 'li',
33625             cls: 'roo-navigation-bar-item',
33626             cn : [
33627                 {
33628                     tag : 'i',
33629                     cls : iconCls
33630                 }
33631             ]
33632         };
33633         
33634         if(this.active){
33635             cfg.cls += ' active';
33636         }
33637         if(this.disabled){
33638             cfg.cls += ' disabled';
33639         }
33640         
33641         return cfg;
33642     },
33643     
33644     disable : function()
33645     {
33646         this.setDisabled(true);
33647     },
33648     
33649     enable : function()
33650     {
33651         this.setDisabled(false);
33652     },
33653     
33654     initEvents: function() 
33655     {
33656         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33657         
33658         this.iconEl.on('click', this.onClick, this);
33659     },
33660     
33661     onClick : function(e)
33662     {
33663         e.preventDefault();
33664         
33665         if(this.disabled){
33666             return;
33667         }
33668         
33669         if(this.fireEvent('click', this, e) === false){
33670             return;
33671         };
33672         
33673         this.parent().setActiveItem(this);
33674     },
33675     
33676     isActive: function () 
33677     {
33678         return this.active;
33679     },
33680     
33681     setActive : function(state)
33682     {
33683         if(this.active == state){
33684             return;
33685         }
33686         
33687         this.active = state;
33688         
33689         if (state) {
33690             this.el.addClass('active');
33691             return;
33692         }
33693         
33694         this.el.removeClass('active');
33695         
33696         return;
33697     },
33698     
33699     setDisabled : function(state)
33700     {
33701         if(this.disabled == state){
33702             return;
33703         }
33704         
33705         this.disabled = state;
33706         
33707         if (state) {
33708             this.el.addClass('disabled');
33709             return;
33710         }
33711         
33712         this.el.removeClass('disabled');
33713     },
33714     
33715     tooltipEl : function()
33716     {
33717         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33718     }
33719 });
33720  
33721
33722  /*
33723  * - LGPL
33724  *
33725  * FieldLabel
33726  * 
33727  */
33728
33729 /**
33730  * @class Roo.bootstrap.FieldLabel
33731  * @extends Roo.bootstrap.Component
33732  * Bootstrap FieldLabel class
33733  * @cfg {String} html contents of the element
33734  * @cfg {String} tag tag of the element default label
33735  * @cfg {String} cls class of the element
33736  * @cfg {String} target label target 
33737  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33738  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33739  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33740  * @cfg {String} iconTooltip default "This field is required"
33741  * @cfg {String} indicatorpos (left|right) default left
33742  * 
33743  * @constructor
33744  * Create a new FieldLabel
33745  * @param {Object} config The config object
33746  */
33747
33748 Roo.bootstrap.FieldLabel = function(config){
33749     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33750     
33751     this.addEvents({
33752             /**
33753              * @event invalid
33754              * Fires after the field has been marked as invalid.
33755              * @param {Roo.form.FieldLabel} this
33756              * @param {String} msg The validation message
33757              */
33758             invalid : true,
33759             /**
33760              * @event valid
33761              * Fires after the field has been validated with no errors.
33762              * @param {Roo.form.FieldLabel} this
33763              */
33764             valid : true
33765         });
33766 };
33767
33768 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33769     
33770     tag: 'label',
33771     cls: '',
33772     html: '',
33773     target: '',
33774     allowBlank : true,
33775     invalidClass : 'has-warning',
33776     validClass : 'has-success',
33777     iconTooltip : 'This field is required',
33778     indicatorpos : 'left',
33779     
33780     getAutoCreate : function(){
33781         
33782         var cls = "";
33783         if (!this.allowBlank) {
33784             cls  = "visible";
33785         }
33786         
33787         var cfg = {
33788             tag : this.tag,
33789             cls : 'roo-bootstrap-field-label ' + this.cls,
33790             for : this.target,
33791             cn : [
33792                 {
33793                     tag : 'i',
33794                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33795                     tooltip : this.iconTooltip
33796                 },
33797                 {
33798                     tag : 'span',
33799                     html : this.html
33800                 }
33801             ] 
33802         };
33803         
33804         if(this.indicatorpos == 'right'){
33805             var cfg = {
33806                 tag : this.tag,
33807                 cls : 'roo-bootstrap-field-label ' + this.cls,
33808                 for : this.target,
33809                 cn : [
33810                     {
33811                         tag : 'span',
33812                         html : this.html
33813                     },
33814                     {
33815                         tag : 'i',
33816                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33817                         tooltip : this.iconTooltip
33818                     }
33819                 ] 
33820             };
33821         }
33822         
33823         return cfg;
33824     },
33825     
33826     initEvents: function() 
33827     {
33828         Roo.bootstrap.Element.superclass.initEvents.call(this);
33829         
33830         this.indicator = this.indicatorEl();
33831         
33832         if(this.indicator){
33833             this.indicator.removeClass('visible');
33834             this.indicator.addClass('invisible');
33835         }
33836         
33837         Roo.bootstrap.FieldLabel.register(this);
33838     },
33839     
33840     indicatorEl : function()
33841     {
33842         var indicator = this.el.select('i.roo-required-indicator',true).first();
33843         
33844         if(!indicator){
33845             return false;
33846         }
33847         
33848         return indicator;
33849         
33850     },
33851     
33852     /**
33853      * Mark this field as valid
33854      */
33855     markValid : function()
33856     {
33857         if(this.indicator){
33858             this.indicator.removeClass('visible');
33859             this.indicator.addClass('invisible');
33860         }
33861         if (Roo.bootstrap.version == 3) {
33862             this.el.removeClass(this.invalidClass);
33863             this.el.addClass(this.validClass);
33864         } else {
33865             this.el.removeClass('is-invalid');
33866             this.el.addClass('is-valid');
33867         }
33868         
33869         
33870         this.fireEvent('valid', this);
33871     },
33872     
33873     /**
33874      * Mark this field as invalid
33875      * @param {String} msg The validation message
33876      */
33877     markInvalid : function(msg)
33878     {
33879         if(this.indicator){
33880             this.indicator.removeClass('invisible');
33881             this.indicator.addClass('visible');
33882         }
33883           if (Roo.bootstrap.version == 3) {
33884             this.el.removeClass(this.validClass);
33885             this.el.addClass(this.invalidClass);
33886         } else {
33887             this.el.removeClass('is-valid');
33888             this.el.addClass('is-invalid');
33889         }
33890         
33891         
33892         this.fireEvent('invalid', this, msg);
33893     }
33894     
33895    
33896 });
33897
33898 Roo.apply(Roo.bootstrap.FieldLabel, {
33899     
33900     groups: {},
33901     
33902      /**
33903     * register a FieldLabel Group
33904     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33905     */
33906     register : function(label)
33907     {
33908         if(this.groups.hasOwnProperty(label.target)){
33909             return;
33910         }
33911      
33912         this.groups[label.target] = label;
33913         
33914     },
33915     /**
33916     * fetch a FieldLabel Group based on the target
33917     * @param {string} target
33918     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33919     */
33920     get: function(target) {
33921         if (typeof(this.groups[target]) == 'undefined') {
33922             return false;
33923         }
33924         
33925         return this.groups[target] ;
33926     }
33927 });
33928
33929  
33930
33931  /*
33932  * - LGPL
33933  *
33934  * page DateSplitField.
33935  * 
33936  */
33937
33938
33939 /**
33940  * @class Roo.bootstrap.DateSplitField
33941  * @extends Roo.bootstrap.Component
33942  * Bootstrap DateSplitField class
33943  * @cfg {string} fieldLabel - the label associated
33944  * @cfg {Number} labelWidth set the width of label (0-12)
33945  * @cfg {String} labelAlign (top|left)
33946  * @cfg {Boolean} dayAllowBlank (true|false) default false
33947  * @cfg {Boolean} monthAllowBlank (true|false) default false
33948  * @cfg {Boolean} yearAllowBlank (true|false) default false
33949  * @cfg {string} dayPlaceholder 
33950  * @cfg {string} monthPlaceholder
33951  * @cfg {string} yearPlaceholder
33952  * @cfg {string} dayFormat default 'd'
33953  * @cfg {string} monthFormat default 'm'
33954  * @cfg {string} yearFormat default 'Y'
33955  * @cfg {Number} labellg set the width of label (1-12)
33956  * @cfg {Number} labelmd set the width of label (1-12)
33957  * @cfg {Number} labelsm set the width of label (1-12)
33958  * @cfg {Number} labelxs set the width of label (1-12)
33959
33960  *     
33961  * @constructor
33962  * Create a new DateSplitField
33963  * @param {Object} config The config object
33964  */
33965
33966 Roo.bootstrap.DateSplitField = function(config){
33967     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33968     
33969     this.addEvents({
33970         // raw events
33971          /**
33972          * @event years
33973          * getting the data of years
33974          * @param {Roo.bootstrap.DateSplitField} this
33975          * @param {Object} years
33976          */
33977         "years" : true,
33978         /**
33979          * @event days
33980          * getting the data of days
33981          * @param {Roo.bootstrap.DateSplitField} this
33982          * @param {Object} days
33983          */
33984         "days" : true,
33985         /**
33986          * @event invalid
33987          * Fires after the field has been marked as invalid.
33988          * @param {Roo.form.Field} this
33989          * @param {String} msg The validation message
33990          */
33991         invalid : true,
33992        /**
33993          * @event valid
33994          * Fires after the field has been validated with no errors.
33995          * @param {Roo.form.Field} this
33996          */
33997         valid : true
33998     });
33999 };
34000
34001 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34002     
34003     fieldLabel : '',
34004     labelAlign : 'top',
34005     labelWidth : 3,
34006     dayAllowBlank : false,
34007     monthAllowBlank : false,
34008     yearAllowBlank : false,
34009     dayPlaceholder : '',
34010     monthPlaceholder : '',
34011     yearPlaceholder : '',
34012     dayFormat : 'd',
34013     monthFormat : 'm',
34014     yearFormat : 'Y',
34015     isFormField : true,
34016     labellg : 0,
34017     labelmd : 0,
34018     labelsm : 0,
34019     labelxs : 0,
34020     
34021     getAutoCreate : function()
34022     {
34023         var cfg = {
34024             tag : 'div',
34025             cls : 'row roo-date-split-field-group',
34026             cn : [
34027                 {
34028                     tag : 'input',
34029                     type : 'hidden',
34030                     cls : 'form-hidden-field roo-date-split-field-group-value',
34031                     name : this.name
34032                 }
34033             ]
34034         };
34035         
34036         var labelCls = 'col-md-12';
34037         var contentCls = 'col-md-4';
34038         
34039         if(this.fieldLabel){
34040             
34041             var label = {
34042                 tag : 'div',
34043                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34044                 cn : [
34045                     {
34046                         tag : 'label',
34047                         html : this.fieldLabel
34048                     }
34049                 ]
34050             };
34051             
34052             if(this.labelAlign == 'left'){
34053             
34054                 if(this.labelWidth > 12){
34055                     label.style = "width: " + this.labelWidth + 'px';
34056                 }
34057
34058                 if(this.labelWidth < 13 && this.labelmd == 0){
34059                     this.labelmd = this.labelWidth;
34060                 }
34061
34062                 if(this.labellg > 0){
34063                     labelCls = ' col-lg-' + this.labellg;
34064                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34065                 }
34066
34067                 if(this.labelmd > 0){
34068                     labelCls = ' col-md-' + this.labelmd;
34069                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34070                 }
34071
34072                 if(this.labelsm > 0){
34073                     labelCls = ' col-sm-' + this.labelsm;
34074                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34075                 }
34076
34077                 if(this.labelxs > 0){
34078                     labelCls = ' col-xs-' + this.labelxs;
34079                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34080                 }
34081             }
34082             
34083             label.cls += ' ' + labelCls;
34084             
34085             cfg.cn.push(label);
34086         }
34087         
34088         Roo.each(['day', 'month', 'year'], function(t){
34089             cfg.cn.push({
34090                 tag : 'div',
34091                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34092             });
34093         }, this);
34094         
34095         return cfg;
34096     },
34097     
34098     inputEl: function ()
34099     {
34100         return this.el.select('.roo-date-split-field-group-value', true).first();
34101     },
34102     
34103     onRender : function(ct, position) 
34104     {
34105         var _this = this;
34106         
34107         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34108         
34109         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34110         
34111         this.dayField = new Roo.bootstrap.ComboBox({
34112             allowBlank : this.dayAllowBlank,
34113             alwaysQuery : true,
34114             displayField : 'value',
34115             editable : false,
34116             fieldLabel : '',
34117             forceSelection : true,
34118             mode : 'local',
34119             placeholder : this.dayPlaceholder,
34120             selectOnFocus : true,
34121             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34122             triggerAction : 'all',
34123             typeAhead : true,
34124             valueField : 'value',
34125             store : new Roo.data.SimpleStore({
34126                 data : (function() {    
34127                     var days = [];
34128                     _this.fireEvent('days', _this, days);
34129                     return days;
34130                 })(),
34131                 fields : [ 'value' ]
34132             }),
34133             listeners : {
34134                 select : function (_self, record, index)
34135                 {
34136                     _this.setValue(_this.getValue());
34137                 }
34138             }
34139         });
34140
34141         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34142         
34143         this.monthField = new Roo.bootstrap.MonthField({
34144             after : '<i class=\"fa fa-calendar\"></i>',
34145             allowBlank : this.monthAllowBlank,
34146             placeholder : this.monthPlaceholder,
34147             readOnly : true,
34148             listeners : {
34149                 render : function (_self)
34150                 {
34151                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34152                         e.preventDefault();
34153                         _self.focus();
34154                     });
34155                 },
34156                 select : function (_self, oldvalue, newvalue)
34157                 {
34158                     _this.setValue(_this.getValue());
34159                 }
34160             }
34161         });
34162         
34163         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34164         
34165         this.yearField = new Roo.bootstrap.ComboBox({
34166             allowBlank : this.yearAllowBlank,
34167             alwaysQuery : true,
34168             displayField : 'value',
34169             editable : false,
34170             fieldLabel : '',
34171             forceSelection : true,
34172             mode : 'local',
34173             placeholder : this.yearPlaceholder,
34174             selectOnFocus : true,
34175             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34176             triggerAction : 'all',
34177             typeAhead : true,
34178             valueField : 'value',
34179             store : new Roo.data.SimpleStore({
34180                 data : (function() {
34181                     var years = [];
34182                     _this.fireEvent('years', _this, years);
34183                     return years;
34184                 })(),
34185                 fields : [ 'value' ]
34186             }),
34187             listeners : {
34188                 select : function (_self, record, index)
34189                 {
34190                     _this.setValue(_this.getValue());
34191                 }
34192             }
34193         });
34194
34195         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34196     },
34197     
34198     setValue : function(v, format)
34199     {
34200         this.inputEl.dom.value = v;
34201         
34202         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34203         
34204         var d = Date.parseDate(v, f);
34205         
34206         if(!d){
34207             this.validate();
34208             return;
34209         }
34210         
34211         this.setDay(d.format(this.dayFormat));
34212         this.setMonth(d.format(this.monthFormat));
34213         this.setYear(d.format(this.yearFormat));
34214         
34215         this.validate();
34216         
34217         return;
34218     },
34219     
34220     setDay : function(v)
34221     {
34222         this.dayField.setValue(v);
34223         this.inputEl.dom.value = this.getValue();
34224         this.validate();
34225         return;
34226     },
34227     
34228     setMonth : function(v)
34229     {
34230         this.monthField.setValue(v, true);
34231         this.inputEl.dom.value = this.getValue();
34232         this.validate();
34233         return;
34234     },
34235     
34236     setYear : function(v)
34237     {
34238         this.yearField.setValue(v);
34239         this.inputEl.dom.value = this.getValue();
34240         this.validate();
34241         return;
34242     },
34243     
34244     getDay : function()
34245     {
34246         return this.dayField.getValue();
34247     },
34248     
34249     getMonth : function()
34250     {
34251         return this.monthField.getValue();
34252     },
34253     
34254     getYear : function()
34255     {
34256         return this.yearField.getValue();
34257     },
34258     
34259     getValue : function()
34260     {
34261         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34262         
34263         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34264         
34265         return date;
34266     },
34267     
34268     reset : function()
34269     {
34270         this.setDay('');
34271         this.setMonth('');
34272         this.setYear('');
34273         this.inputEl.dom.value = '';
34274         this.validate();
34275         return;
34276     },
34277     
34278     validate : function()
34279     {
34280         var d = this.dayField.validate();
34281         var m = this.monthField.validate();
34282         var y = this.yearField.validate();
34283         
34284         var valid = true;
34285         
34286         if(
34287                 (!this.dayAllowBlank && !d) ||
34288                 (!this.monthAllowBlank && !m) ||
34289                 (!this.yearAllowBlank && !y)
34290         ){
34291             valid = false;
34292         }
34293         
34294         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34295             return valid;
34296         }
34297         
34298         if(valid){
34299             this.markValid();
34300             return valid;
34301         }
34302         
34303         this.markInvalid();
34304         
34305         return valid;
34306     },
34307     
34308     markValid : function()
34309     {
34310         
34311         var label = this.el.select('label', true).first();
34312         var icon = this.el.select('i.fa-star', true).first();
34313
34314         if(label && icon){
34315             icon.remove();
34316         }
34317         
34318         this.fireEvent('valid', this);
34319     },
34320     
34321      /**
34322      * Mark this field as invalid
34323      * @param {String} msg The validation message
34324      */
34325     markInvalid : function(msg)
34326     {
34327         
34328         var label = this.el.select('label', true).first();
34329         var icon = this.el.select('i.fa-star', true).first();
34330
34331         if(label && !icon){
34332             this.el.select('.roo-date-split-field-label', true).createChild({
34333                 tag : 'i',
34334                 cls : 'text-danger fa fa-lg fa-star',
34335                 tooltip : 'This field is required',
34336                 style : 'margin-right:5px;'
34337             }, label, true);
34338         }
34339         
34340         this.fireEvent('invalid', this, msg);
34341     },
34342     
34343     clearInvalid : function()
34344     {
34345         var label = this.el.select('label', true).first();
34346         var icon = this.el.select('i.fa-star', true).first();
34347
34348         if(label && icon){
34349             icon.remove();
34350         }
34351         
34352         this.fireEvent('valid', this);
34353     },
34354     
34355     getName: function()
34356     {
34357         return this.name;
34358     }
34359     
34360 });
34361
34362  /**
34363  *
34364  * This is based on 
34365  * http://masonry.desandro.com
34366  *
34367  * The idea is to render all the bricks based on vertical width...
34368  *
34369  * The original code extends 'outlayer' - we might need to use that....
34370  * 
34371  */
34372
34373
34374 /**
34375  * @class Roo.bootstrap.LayoutMasonry
34376  * @extends Roo.bootstrap.Component
34377  * Bootstrap Layout Masonry class
34378  * 
34379  * @constructor
34380  * Create a new Element
34381  * @param {Object} config The config object
34382  */
34383
34384 Roo.bootstrap.LayoutMasonry = function(config){
34385     
34386     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34387     
34388     this.bricks = [];
34389     
34390     Roo.bootstrap.LayoutMasonry.register(this);
34391     
34392     this.addEvents({
34393         // raw events
34394         /**
34395          * @event layout
34396          * Fire after layout the items
34397          * @param {Roo.bootstrap.LayoutMasonry} this
34398          * @param {Roo.EventObject} e
34399          */
34400         "layout" : true
34401     });
34402     
34403 };
34404
34405 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34406     
34407     /**
34408      * @cfg {Boolean} isLayoutInstant = no animation?
34409      */   
34410     isLayoutInstant : false, // needed?
34411    
34412     /**
34413      * @cfg {Number} boxWidth  width of the columns
34414      */   
34415     boxWidth : 450,
34416     
34417       /**
34418      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34419      */   
34420     boxHeight : 0,
34421     
34422     /**
34423      * @cfg {Number} padWidth padding below box..
34424      */   
34425     padWidth : 10, 
34426     
34427     /**
34428      * @cfg {Number} gutter gutter width..
34429      */   
34430     gutter : 10,
34431     
34432      /**
34433      * @cfg {Number} maxCols maximum number of columns
34434      */   
34435     
34436     maxCols: 0,
34437     
34438     /**
34439      * @cfg {Boolean} isAutoInitial defalut true
34440      */   
34441     isAutoInitial : true, 
34442     
34443     containerWidth: 0,
34444     
34445     /**
34446      * @cfg {Boolean} isHorizontal defalut false
34447      */   
34448     isHorizontal : false, 
34449
34450     currentSize : null,
34451     
34452     tag: 'div',
34453     
34454     cls: '',
34455     
34456     bricks: null, //CompositeElement
34457     
34458     cols : 1,
34459     
34460     _isLayoutInited : false,
34461     
34462 //    isAlternative : false, // only use for vertical layout...
34463     
34464     /**
34465      * @cfg {Number} alternativePadWidth padding below box..
34466      */   
34467     alternativePadWidth : 50,
34468     
34469     selectedBrick : [],
34470     
34471     getAutoCreate : function(){
34472         
34473         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34474         
34475         var cfg = {
34476             tag: this.tag,
34477             cls: 'blog-masonary-wrapper ' + this.cls,
34478             cn : {
34479                 cls : 'mas-boxes masonary'
34480             }
34481         };
34482         
34483         return cfg;
34484     },
34485     
34486     getChildContainer: function( )
34487     {
34488         if (this.boxesEl) {
34489             return this.boxesEl;
34490         }
34491         
34492         this.boxesEl = this.el.select('.mas-boxes').first();
34493         
34494         return this.boxesEl;
34495     },
34496     
34497     
34498     initEvents : function()
34499     {
34500         var _this = this;
34501         
34502         if(this.isAutoInitial){
34503             Roo.log('hook children rendered');
34504             this.on('childrenrendered', function() {
34505                 Roo.log('children rendered');
34506                 _this.initial();
34507             } ,this);
34508         }
34509     },
34510     
34511     initial : function()
34512     {
34513         this.selectedBrick = [];
34514         
34515         this.currentSize = this.el.getBox(true);
34516         
34517         Roo.EventManager.onWindowResize(this.resize, this); 
34518
34519         if(!this.isAutoInitial){
34520             this.layout();
34521             return;
34522         }
34523         
34524         this.layout();
34525         
34526         return;
34527         //this.layout.defer(500,this);
34528         
34529     },
34530     
34531     resize : function()
34532     {
34533         var cs = this.el.getBox(true);
34534         
34535         if (
34536                 this.currentSize.width == cs.width && 
34537                 this.currentSize.x == cs.x && 
34538                 this.currentSize.height == cs.height && 
34539                 this.currentSize.y == cs.y 
34540         ) {
34541             Roo.log("no change in with or X or Y");
34542             return;
34543         }
34544         
34545         this.currentSize = cs;
34546         
34547         this.layout();
34548         
34549     },
34550     
34551     layout : function()
34552     {   
34553         this._resetLayout();
34554         
34555         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34556         
34557         this.layoutItems( isInstant );
34558       
34559         this._isLayoutInited = true;
34560         
34561         this.fireEvent('layout', this);
34562         
34563     },
34564     
34565     _resetLayout : function()
34566     {
34567         if(this.isHorizontal){
34568             this.horizontalMeasureColumns();
34569             return;
34570         }
34571         
34572         this.verticalMeasureColumns();
34573         
34574     },
34575     
34576     verticalMeasureColumns : function()
34577     {
34578         this.getContainerWidth();
34579         
34580 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34581 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34582 //            return;
34583 //        }
34584         
34585         var boxWidth = this.boxWidth + this.padWidth;
34586         
34587         if(this.containerWidth < this.boxWidth){
34588             boxWidth = this.containerWidth
34589         }
34590         
34591         var containerWidth = this.containerWidth;
34592         
34593         var cols = Math.floor(containerWidth / boxWidth);
34594         
34595         this.cols = Math.max( cols, 1 );
34596         
34597         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34598         
34599         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34600         
34601         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34602         
34603         this.colWidth = boxWidth + avail - this.padWidth;
34604         
34605         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34606         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34607     },
34608     
34609     horizontalMeasureColumns : function()
34610     {
34611         this.getContainerWidth();
34612         
34613         var boxWidth = this.boxWidth;
34614         
34615         if(this.containerWidth < boxWidth){
34616             boxWidth = this.containerWidth;
34617         }
34618         
34619         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34620         
34621         this.el.setHeight(boxWidth);
34622         
34623     },
34624     
34625     getContainerWidth : function()
34626     {
34627         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34628     },
34629     
34630     layoutItems : function( isInstant )
34631     {
34632         Roo.log(this.bricks);
34633         
34634         var items = Roo.apply([], this.bricks);
34635         
34636         if(this.isHorizontal){
34637             this._horizontalLayoutItems( items , isInstant );
34638             return;
34639         }
34640         
34641 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34642 //            this._verticalAlternativeLayoutItems( items , isInstant );
34643 //            return;
34644 //        }
34645         
34646         this._verticalLayoutItems( items , isInstant );
34647         
34648     },
34649     
34650     _verticalLayoutItems : function ( items , isInstant)
34651     {
34652         if ( !items || !items.length ) {
34653             return;
34654         }
34655         
34656         var standard = [
34657             ['xs', 'xs', 'xs', 'tall'],
34658             ['xs', 'xs', 'tall'],
34659             ['xs', 'xs', 'sm'],
34660             ['xs', 'xs', 'xs'],
34661             ['xs', 'tall'],
34662             ['xs', 'sm'],
34663             ['xs', 'xs'],
34664             ['xs'],
34665             
34666             ['sm', 'xs', 'xs'],
34667             ['sm', 'xs'],
34668             ['sm'],
34669             
34670             ['tall', 'xs', 'xs', 'xs'],
34671             ['tall', 'xs', 'xs'],
34672             ['tall', 'xs'],
34673             ['tall']
34674             
34675         ];
34676         
34677         var queue = [];
34678         
34679         var boxes = [];
34680         
34681         var box = [];
34682         
34683         Roo.each(items, function(item, k){
34684             
34685             switch (item.size) {
34686                 // these layouts take up a full box,
34687                 case 'md' :
34688                 case 'md-left' :
34689                 case 'md-right' :
34690                 case 'wide' :
34691                     
34692                     if(box.length){
34693                         boxes.push(box);
34694                         box = [];
34695                     }
34696                     
34697                     boxes.push([item]);
34698                     
34699                     break;
34700                     
34701                 case 'xs' :
34702                 case 'sm' :
34703                 case 'tall' :
34704                     
34705                     box.push(item);
34706                     
34707                     break;
34708                 default :
34709                     break;
34710                     
34711             }
34712             
34713         }, this);
34714         
34715         if(box.length){
34716             boxes.push(box);
34717             box = [];
34718         }
34719         
34720         var filterPattern = function(box, length)
34721         {
34722             if(!box.length){
34723                 return;
34724             }
34725             
34726             var match = false;
34727             
34728             var pattern = box.slice(0, length);
34729             
34730             var format = [];
34731             
34732             Roo.each(pattern, function(i){
34733                 format.push(i.size);
34734             }, this);
34735             
34736             Roo.each(standard, function(s){
34737                 
34738                 if(String(s) != String(format)){
34739                     return;
34740                 }
34741                 
34742                 match = true;
34743                 return false;
34744                 
34745             }, this);
34746             
34747             if(!match && length == 1){
34748                 return;
34749             }
34750             
34751             if(!match){
34752                 filterPattern(box, length - 1);
34753                 return;
34754             }
34755                 
34756             queue.push(pattern);
34757
34758             box = box.slice(length, box.length);
34759
34760             filterPattern(box, 4);
34761
34762             return;
34763             
34764         }
34765         
34766         Roo.each(boxes, function(box, k){
34767             
34768             if(!box.length){
34769                 return;
34770             }
34771             
34772             if(box.length == 1){
34773                 queue.push(box);
34774                 return;
34775             }
34776             
34777             filterPattern(box, 4);
34778             
34779         }, this);
34780         
34781         this._processVerticalLayoutQueue( queue, isInstant );
34782         
34783     },
34784     
34785 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34786 //    {
34787 //        if ( !items || !items.length ) {
34788 //            return;
34789 //        }
34790 //
34791 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34792 //        
34793 //    },
34794     
34795     _horizontalLayoutItems : function ( items , isInstant)
34796     {
34797         if ( !items || !items.length || items.length < 3) {
34798             return;
34799         }
34800         
34801         items.reverse();
34802         
34803         var eItems = items.slice(0, 3);
34804         
34805         items = items.slice(3, items.length);
34806         
34807         var standard = [
34808             ['xs', 'xs', 'xs', 'wide'],
34809             ['xs', 'xs', 'wide'],
34810             ['xs', 'xs', 'sm'],
34811             ['xs', 'xs', 'xs'],
34812             ['xs', 'wide'],
34813             ['xs', 'sm'],
34814             ['xs', 'xs'],
34815             ['xs'],
34816             
34817             ['sm', 'xs', 'xs'],
34818             ['sm', 'xs'],
34819             ['sm'],
34820             
34821             ['wide', 'xs', 'xs', 'xs'],
34822             ['wide', 'xs', 'xs'],
34823             ['wide', 'xs'],
34824             ['wide'],
34825             
34826             ['wide-thin']
34827         ];
34828         
34829         var queue = [];
34830         
34831         var boxes = [];
34832         
34833         var box = [];
34834         
34835         Roo.each(items, function(item, k){
34836             
34837             switch (item.size) {
34838                 case 'md' :
34839                 case 'md-left' :
34840                 case 'md-right' :
34841                 case 'tall' :
34842                     
34843                     if(box.length){
34844                         boxes.push(box);
34845                         box = [];
34846                     }
34847                     
34848                     boxes.push([item]);
34849                     
34850                     break;
34851                     
34852                 case 'xs' :
34853                 case 'sm' :
34854                 case 'wide' :
34855                 case 'wide-thin' :
34856                     
34857                     box.push(item);
34858                     
34859                     break;
34860                 default :
34861                     break;
34862                     
34863             }
34864             
34865         }, this);
34866         
34867         if(box.length){
34868             boxes.push(box);
34869             box = [];
34870         }
34871         
34872         var filterPattern = function(box, length)
34873         {
34874             if(!box.length){
34875                 return;
34876             }
34877             
34878             var match = false;
34879             
34880             var pattern = box.slice(0, length);
34881             
34882             var format = [];
34883             
34884             Roo.each(pattern, function(i){
34885                 format.push(i.size);
34886             }, this);
34887             
34888             Roo.each(standard, function(s){
34889                 
34890                 if(String(s) != String(format)){
34891                     return;
34892                 }
34893                 
34894                 match = true;
34895                 return false;
34896                 
34897             }, this);
34898             
34899             if(!match && length == 1){
34900                 return;
34901             }
34902             
34903             if(!match){
34904                 filterPattern(box, length - 1);
34905                 return;
34906             }
34907                 
34908             queue.push(pattern);
34909
34910             box = box.slice(length, box.length);
34911
34912             filterPattern(box, 4);
34913
34914             return;
34915             
34916         }
34917         
34918         Roo.each(boxes, function(box, k){
34919             
34920             if(!box.length){
34921                 return;
34922             }
34923             
34924             if(box.length == 1){
34925                 queue.push(box);
34926                 return;
34927             }
34928             
34929             filterPattern(box, 4);
34930             
34931         }, this);
34932         
34933         
34934         var prune = [];
34935         
34936         var pos = this.el.getBox(true);
34937         
34938         var minX = pos.x;
34939         
34940         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34941         
34942         var hit_end = false;
34943         
34944         Roo.each(queue, function(box){
34945             
34946             if(hit_end){
34947                 
34948                 Roo.each(box, function(b){
34949                 
34950                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34951                     b.el.hide();
34952
34953                 }, this);
34954
34955                 return;
34956             }
34957             
34958             var mx = 0;
34959             
34960             Roo.each(box, function(b){
34961                 
34962                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34963                 b.el.show();
34964
34965                 mx = Math.max(mx, b.x);
34966                 
34967             }, this);
34968             
34969             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34970             
34971             if(maxX < minX){
34972                 
34973                 Roo.each(box, function(b){
34974                 
34975                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34976                     b.el.hide();
34977                     
34978                 }, this);
34979                 
34980                 hit_end = true;
34981                 
34982                 return;
34983             }
34984             
34985             prune.push(box);
34986             
34987         }, this);
34988         
34989         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34990     },
34991     
34992     /** Sets position of item in DOM
34993     * @param {Element} item
34994     * @param {Number} x - horizontal position
34995     * @param {Number} y - vertical position
34996     * @param {Boolean} isInstant - disables transitions
34997     */
34998     _processVerticalLayoutQueue : function( queue, isInstant )
34999     {
35000         var pos = this.el.getBox(true);
35001         var x = pos.x;
35002         var y = pos.y;
35003         var maxY = [];
35004         
35005         for (var i = 0; i < this.cols; i++){
35006             maxY[i] = pos.y;
35007         }
35008         
35009         Roo.each(queue, function(box, k){
35010             
35011             var col = k % this.cols;
35012             
35013             Roo.each(box, function(b,kk){
35014                 
35015                 b.el.position('absolute');
35016                 
35017                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35018                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35019                 
35020                 if(b.size == 'md-left' || b.size == 'md-right'){
35021                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35022                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35023                 }
35024                 
35025                 b.el.setWidth(width);
35026                 b.el.setHeight(height);
35027                 // iframe?
35028                 b.el.select('iframe',true).setSize(width,height);
35029                 
35030             }, this);
35031             
35032             for (var i = 0; i < this.cols; i++){
35033                 
35034                 if(maxY[i] < maxY[col]){
35035                     col = i;
35036                     continue;
35037                 }
35038                 
35039                 col = Math.min(col, i);
35040                 
35041             }
35042             
35043             x = pos.x + col * (this.colWidth + this.padWidth);
35044             
35045             y = maxY[col];
35046             
35047             var positions = [];
35048             
35049             switch (box.length){
35050                 case 1 :
35051                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35052                     break;
35053                 case 2 :
35054                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35055                     break;
35056                 case 3 :
35057                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35058                     break;
35059                 case 4 :
35060                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35061                     break;
35062                 default :
35063                     break;
35064             }
35065             
35066             Roo.each(box, function(b,kk){
35067                 
35068                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35069                 
35070                 var sz = b.el.getSize();
35071                 
35072                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35073                 
35074             }, this);
35075             
35076         }, this);
35077         
35078         var mY = 0;
35079         
35080         for (var i = 0; i < this.cols; i++){
35081             mY = Math.max(mY, maxY[i]);
35082         }
35083         
35084         this.el.setHeight(mY - pos.y);
35085         
35086     },
35087     
35088 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35089 //    {
35090 //        var pos = this.el.getBox(true);
35091 //        var x = pos.x;
35092 //        var y = pos.y;
35093 //        var maxX = pos.right;
35094 //        
35095 //        var maxHeight = 0;
35096 //        
35097 //        Roo.each(items, function(item, k){
35098 //            
35099 //            var c = k % 2;
35100 //            
35101 //            item.el.position('absolute');
35102 //                
35103 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35104 //
35105 //            item.el.setWidth(width);
35106 //
35107 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35108 //
35109 //            item.el.setHeight(height);
35110 //            
35111 //            if(c == 0){
35112 //                item.el.setXY([x, y], isInstant ? false : true);
35113 //            } else {
35114 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35115 //            }
35116 //            
35117 //            y = y + height + this.alternativePadWidth;
35118 //            
35119 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35120 //            
35121 //        }, this);
35122 //        
35123 //        this.el.setHeight(maxHeight);
35124 //        
35125 //    },
35126     
35127     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35128     {
35129         var pos = this.el.getBox(true);
35130         
35131         var minX = pos.x;
35132         var minY = pos.y;
35133         
35134         var maxX = pos.right;
35135         
35136         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35137         
35138         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35139         
35140         Roo.each(queue, function(box, k){
35141             
35142             Roo.each(box, function(b, kk){
35143                 
35144                 b.el.position('absolute');
35145                 
35146                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35147                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35148                 
35149                 if(b.size == 'md-left' || b.size == 'md-right'){
35150                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35151                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35152                 }
35153                 
35154                 b.el.setWidth(width);
35155                 b.el.setHeight(height);
35156                 
35157             }, this);
35158             
35159             if(!box.length){
35160                 return;
35161             }
35162             
35163             var positions = [];
35164             
35165             switch (box.length){
35166                 case 1 :
35167                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35168                     break;
35169                 case 2 :
35170                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35171                     break;
35172                 case 3 :
35173                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35174                     break;
35175                 case 4 :
35176                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35177                     break;
35178                 default :
35179                     break;
35180             }
35181             
35182             Roo.each(box, function(b,kk){
35183                 
35184                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35185                 
35186                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35187                 
35188             }, this);
35189             
35190         }, this);
35191         
35192     },
35193     
35194     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35195     {
35196         Roo.each(eItems, function(b,k){
35197             
35198             b.size = (k == 0) ? 'sm' : 'xs';
35199             b.x = (k == 0) ? 2 : 1;
35200             b.y = (k == 0) ? 2 : 1;
35201             
35202             b.el.position('absolute');
35203             
35204             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35205                 
35206             b.el.setWidth(width);
35207             
35208             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35209             
35210             b.el.setHeight(height);
35211             
35212         }, this);
35213
35214         var positions = [];
35215         
35216         positions.push({
35217             x : maxX - this.unitWidth * 2 - this.gutter,
35218             y : minY
35219         });
35220         
35221         positions.push({
35222             x : maxX - this.unitWidth,
35223             y : minY + (this.unitWidth + this.gutter) * 2
35224         });
35225         
35226         positions.push({
35227             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35228             y : minY
35229         });
35230         
35231         Roo.each(eItems, function(b,k){
35232             
35233             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35234
35235         }, this);
35236         
35237     },
35238     
35239     getVerticalOneBoxColPositions : function(x, y, box)
35240     {
35241         var pos = [];
35242         
35243         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35244         
35245         if(box[0].size == 'md-left'){
35246             rand = 0;
35247         }
35248         
35249         if(box[0].size == 'md-right'){
35250             rand = 1;
35251         }
35252         
35253         pos.push({
35254             x : x + (this.unitWidth + this.gutter) * rand,
35255             y : y
35256         });
35257         
35258         return pos;
35259     },
35260     
35261     getVerticalTwoBoxColPositions : function(x, y, box)
35262     {
35263         var pos = [];
35264         
35265         if(box[0].size == 'xs'){
35266             
35267             pos.push({
35268                 x : x,
35269                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35270             });
35271
35272             pos.push({
35273                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35274                 y : y
35275             });
35276             
35277             return pos;
35278             
35279         }
35280         
35281         pos.push({
35282             x : x,
35283             y : y
35284         });
35285
35286         pos.push({
35287             x : x + (this.unitWidth + this.gutter) * 2,
35288             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35289         });
35290         
35291         return pos;
35292         
35293     },
35294     
35295     getVerticalThreeBoxColPositions : function(x, y, box)
35296     {
35297         var pos = [];
35298         
35299         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35300             
35301             pos.push({
35302                 x : x,
35303                 y : y
35304             });
35305
35306             pos.push({
35307                 x : x + (this.unitWidth + this.gutter) * 1,
35308                 y : y
35309             });
35310             
35311             pos.push({
35312                 x : x + (this.unitWidth + this.gutter) * 2,
35313                 y : y
35314             });
35315             
35316             return pos;
35317             
35318         }
35319         
35320         if(box[0].size == 'xs' && box[1].size == 'xs'){
35321             
35322             pos.push({
35323                 x : x,
35324                 y : y
35325             });
35326
35327             pos.push({
35328                 x : x,
35329                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35330             });
35331             
35332             pos.push({
35333                 x : x + (this.unitWidth + this.gutter) * 1,
35334                 y : y
35335             });
35336             
35337             return pos;
35338             
35339         }
35340         
35341         pos.push({
35342             x : x,
35343             y : y
35344         });
35345
35346         pos.push({
35347             x : x + (this.unitWidth + this.gutter) * 2,
35348             y : y
35349         });
35350
35351         pos.push({
35352             x : x + (this.unitWidth + this.gutter) * 2,
35353             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35354         });
35355             
35356         return pos;
35357         
35358     },
35359     
35360     getVerticalFourBoxColPositions : function(x, y, box)
35361     {
35362         var pos = [];
35363         
35364         if(box[0].size == 'xs'){
35365             
35366             pos.push({
35367                 x : x,
35368                 y : y
35369             });
35370
35371             pos.push({
35372                 x : x,
35373                 y : y + (this.unitHeight + this.gutter) * 1
35374             });
35375             
35376             pos.push({
35377                 x : x,
35378                 y : y + (this.unitHeight + this.gutter) * 2
35379             });
35380             
35381             pos.push({
35382                 x : x + (this.unitWidth + this.gutter) * 1,
35383                 y : y
35384             });
35385             
35386             return pos;
35387             
35388         }
35389         
35390         pos.push({
35391             x : x,
35392             y : y
35393         });
35394
35395         pos.push({
35396             x : x + (this.unitWidth + this.gutter) * 2,
35397             y : y
35398         });
35399
35400         pos.push({
35401             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35402             y : y + (this.unitHeight + this.gutter) * 1
35403         });
35404
35405         pos.push({
35406             x : x + (this.unitWidth + this.gutter) * 2,
35407             y : y + (this.unitWidth + this.gutter) * 2
35408         });
35409
35410         return pos;
35411         
35412     },
35413     
35414     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35415     {
35416         var pos = [];
35417         
35418         if(box[0].size == 'md-left'){
35419             pos.push({
35420                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35421                 y : minY
35422             });
35423             
35424             return pos;
35425         }
35426         
35427         if(box[0].size == 'md-right'){
35428             pos.push({
35429                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35430                 y : minY + (this.unitWidth + this.gutter) * 1
35431             });
35432             
35433             return pos;
35434         }
35435         
35436         var rand = Math.floor(Math.random() * (4 - box[0].y));
35437         
35438         pos.push({
35439             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35440             y : minY + (this.unitWidth + this.gutter) * rand
35441         });
35442         
35443         return pos;
35444         
35445     },
35446     
35447     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35448     {
35449         var pos = [];
35450         
35451         if(box[0].size == 'xs'){
35452             
35453             pos.push({
35454                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35455                 y : minY
35456             });
35457
35458             pos.push({
35459                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35460                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35461             });
35462             
35463             return pos;
35464             
35465         }
35466         
35467         pos.push({
35468             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35469             y : minY
35470         });
35471
35472         pos.push({
35473             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35474             y : minY + (this.unitWidth + this.gutter) * 2
35475         });
35476         
35477         return pos;
35478         
35479     },
35480     
35481     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35482     {
35483         var pos = [];
35484         
35485         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35486             
35487             pos.push({
35488                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35489                 y : minY
35490             });
35491
35492             pos.push({
35493                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35494                 y : minY + (this.unitWidth + this.gutter) * 1
35495             });
35496             
35497             pos.push({
35498                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35499                 y : minY + (this.unitWidth + this.gutter) * 2
35500             });
35501             
35502             return pos;
35503             
35504         }
35505         
35506         if(box[0].size == 'xs' && box[1].size == 'xs'){
35507             
35508             pos.push({
35509                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35510                 y : minY
35511             });
35512
35513             pos.push({
35514                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35515                 y : minY
35516             });
35517             
35518             pos.push({
35519                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35520                 y : minY + (this.unitWidth + this.gutter) * 1
35521             });
35522             
35523             return pos;
35524             
35525         }
35526         
35527         pos.push({
35528             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35529             y : minY
35530         });
35531
35532         pos.push({
35533             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35534             y : minY + (this.unitWidth + this.gutter) * 2
35535         });
35536
35537         pos.push({
35538             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35539             y : minY + (this.unitWidth + this.gutter) * 2
35540         });
35541             
35542         return pos;
35543         
35544     },
35545     
35546     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35547     {
35548         var pos = [];
35549         
35550         if(box[0].size == 'xs'){
35551             
35552             pos.push({
35553                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35554                 y : minY
35555             });
35556
35557             pos.push({
35558                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35559                 y : minY
35560             });
35561             
35562             pos.push({
35563                 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),
35564                 y : minY
35565             });
35566             
35567             pos.push({
35568                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35569                 y : minY + (this.unitWidth + this.gutter) * 1
35570             });
35571             
35572             return pos;
35573             
35574         }
35575         
35576         pos.push({
35577             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35578             y : minY
35579         });
35580         
35581         pos.push({
35582             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35583             y : minY + (this.unitWidth + this.gutter) * 2
35584         });
35585         
35586         pos.push({
35587             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35588             y : minY + (this.unitWidth + this.gutter) * 2
35589         });
35590         
35591         pos.push({
35592             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),
35593             y : minY + (this.unitWidth + this.gutter) * 2
35594         });
35595
35596         return pos;
35597         
35598     },
35599     
35600     /**
35601     * remove a Masonry Brick
35602     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35603     */
35604     removeBrick : function(brick_id)
35605     {
35606         if (!brick_id) {
35607             return;
35608         }
35609         
35610         for (var i = 0; i<this.bricks.length; i++) {
35611             if (this.bricks[i].id == brick_id) {
35612                 this.bricks.splice(i,1);
35613                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35614                 this.initial();
35615             }
35616         }
35617     },
35618     
35619     /**
35620     * adds a Masonry Brick
35621     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35622     */
35623     addBrick : function(cfg)
35624     {
35625         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35626         //this.register(cn);
35627         cn.parentId = this.id;
35628         cn.render(this.el);
35629         return cn;
35630     },
35631     
35632     /**
35633     * register a Masonry Brick
35634     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35635     */
35636     
35637     register : function(brick)
35638     {
35639         this.bricks.push(brick);
35640         brick.masonryId = this.id;
35641     },
35642     
35643     /**
35644     * clear all the Masonry Brick
35645     */
35646     clearAll : function()
35647     {
35648         this.bricks = [];
35649         //this.getChildContainer().dom.innerHTML = "";
35650         this.el.dom.innerHTML = '';
35651     },
35652     
35653     getSelected : function()
35654     {
35655         if (!this.selectedBrick) {
35656             return false;
35657         }
35658         
35659         return this.selectedBrick;
35660     }
35661 });
35662
35663 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35664     
35665     groups: {},
35666      /**
35667     * register a Masonry Layout
35668     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35669     */
35670     
35671     register : function(layout)
35672     {
35673         this.groups[layout.id] = layout;
35674     },
35675     /**
35676     * fetch a  Masonry Layout based on the masonry layout ID
35677     * @param {string} the masonry layout to add
35678     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35679     */
35680     
35681     get: function(layout_id) {
35682         if (typeof(this.groups[layout_id]) == 'undefined') {
35683             return false;
35684         }
35685         return this.groups[layout_id] ;
35686     }
35687     
35688     
35689     
35690 });
35691
35692  
35693
35694  /**
35695  *
35696  * This is based on 
35697  * http://masonry.desandro.com
35698  *
35699  * The idea is to render all the bricks based on vertical width...
35700  *
35701  * The original code extends 'outlayer' - we might need to use that....
35702  * 
35703  */
35704
35705
35706 /**
35707  * @class Roo.bootstrap.LayoutMasonryAuto
35708  * @extends Roo.bootstrap.Component
35709  * Bootstrap Layout Masonry class
35710  * 
35711  * @constructor
35712  * Create a new Element
35713  * @param {Object} config The config object
35714  */
35715
35716 Roo.bootstrap.LayoutMasonryAuto = function(config){
35717     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35718 };
35719
35720 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35721     
35722       /**
35723      * @cfg {Boolean} isFitWidth  - resize the width..
35724      */   
35725     isFitWidth : false,  // options..
35726     /**
35727      * @cfg {Boolean} isOriginLeft = left align?
35728      */   
35729     isOriginLeft : true,
35730     /**
35731      * @cfg {Boolean} isOriginTop = top align?
35732      */   
35733     isOriginTop : false,
35734     /**
35735      * @cfg {Boolean} isLayoutInstant = no animation?
35736      */   
35737     isLayoutInstant : false, // needed?
35738     /**
35739      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35740      */   
35741     isResizingContainer : true,
35742     /**
35743      * @cfg {Number} columnWidth  width of the columns 
35744      */   
35745     
35746     columnWidth : 0,
35747     
35748     /**
35749      * @cfg {Number} maxCols maximum number of columns
35750      */   
35751     
35752     maxCols: 0,
35753     /**
35754      * @cfg {Number} padHeight padding below box..
35755      */   
35756     
35757     padHeight : 10, 
35758     
35759     /**
35760      * @cfg {Boolean} isAutoInitial defalut true
35761      */   
35762     
35763     isAutoInitial : true, 
35764     
35765     // private?
35766     gutter : 0,
35767     
35768     containerWidth: 0,
35769     initialColumnWidth : 0,
35770     currentSize : null,
35771     
35772     colYs : null, // array.
35773     maxY : 0,
35774     padWidth: 10,
35775     
35776     
35777     tag: 'div',
35778     cls: '',
35779     bricks: null, //CompositeElement
35780     cols : 0, // array?
35781     // element : null, // wrapped now this.el
35782     _isLayoutInited : null, 
35783     
35784     
35785     getAutoCreate : function(){
35786         
35787         var cfg = {
35788             tag: this.tag,
35789             cls: 'blog-masonary-wrapper ' + this.cls,
35790             cn : {
35791                 cls : 'mas-boxes masonary'
35792             }
35793         };
35794         
35795         return cfg;
35796     },
35797     
35798     getChildContainer: function( )
35799     {
35800         if (this.boxesEl) {
35801             return this.boxesEl;
35802         }
35803         
35804         this.boxesEl = this.el.select('.mas-boxes').first();
35805         
35806         return this.boxesEl;
35807     },
35808     
35809     
35810     initEvents : function()
35811     {
35812         var _this = this;
35813         
35814         if(this.isAutoInitial){
35815             Roo.log('hook children rendered');
35816             this.on('childrenrendered', function() {
35817                 Roo.log('children rendered');
35818                 _this.initial();
35819             } ,this);
35820         }
35821         
35822     },
35823     
35824     initial : function()
35825     {
35826         this.reloadItems();
35827
35828         this.currentSize = this.el.getBox(true);
35829
35830         /// was window resize... - let's see if this works..
35831         Roo.EventManager.onWindowResize(this.resize, this); 
35832
35833         if(!this.isAutoInitial){
35834             this.layout();
35835             return;
35836         }
35837         
35838         this.layout.defer(500,this);
35839     },
35840     
35841     reloadItems: function()
35842     {
35843         this.bricks = this.el.select('.masonry-brick', true);
35844         
35845         this.bricks.each(function(b) {
35846             //Roo.log(b.getSize());
35847             if (!b.attr('originalwidth')) {
35848                 b.attr('originalwidth',  b.getSize().width);
35849             }
35850             
35851         });
35852         
35853         Roo.log(this.bricks.elements.length);
35854     },
35855     
35856     resize : function()
35857     {
35858         Roo.log('resize');
35859         var cs = this.el.getBox(true);
35860         
35861         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35862             Roo.log("no change in with or X");
35863             return;
35864         }
35865         this.currentSize = cs;
35866         this.layout();
35867     },
35868     
35869     layout : function()
35870     {
35871          Roo.log('layout');
35872         this._resetLayout();
35873         //this._manageStamps();
35874       
35875         // don't animate first layout
35876         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35877         this.layoutItems( isInstant );
35878       
35879         // flag for initalized
35880         this._isLayoutInited = true;
35881     },
35882     
35883     layoutItems : function( isInstant )
35884     {
35885         //var items = this._getItemsForLayout( this.items );
35886         // original code supports filtering layout items.. we just ignore it..
35887         
35888         this._layoutItems( this.bricks , isInstant );
35889       
35890         this._postLayout();
35891     },
35892     _layoutItems : function ( items , isInstant)
35893     {
35894        //this.fireEvent( 'layout', this, items );
35895     
35896
35897         if ( !items || !items.elements.length ) {
35898           // no items, emit event with empty array
35899             return;
35900         }
35901
35902         var queue = [];
35903         items.each(function(item) {
35904             Roo.log("layout item");
35905             Roo.log(item);
35906             // get x/y object from method
35907             var position = this._getItemLayoutPosition( item );
35908             // enqueue
35909             position.item = item;
35910             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35911             queue.push( position );
35912         }, this);
35913       
35914         this._processLayoutQueue( queue );
35915     },
35916     /** Sets position of item in DOM
35917     * @param {Element} item
35918     * @param {Number} x - horizontal position
35919     * @param {Number} y - vertical position
35920     * @param {Boolean} isInstant - disables transitions
35921     */
35922     _processLayoutQueue : function( queue )
35923     {
35924         for ( var i=0, len = queue.length; i < len; i++ ) {
35925             var obj = queue[i];
35926             obj.item.position('absolute');
35927             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35928         }
35929     },
35930       
35931     
35932     /**
35933     * Any logic you want to do after each layout,
35934     * i.e. size the container
35935     */
35936     _postLayout : function()
35937     {
35938         this.resizeContainer();
35939     },
35940     
35941     resizeContainer : function()
35942     {
35943         if ( !this.isResizingContainer ) {
35944             return;
35945         }
35946         var size = this._getContainerSize();
35947         if ( size ) {
35948             this.el.setSize(size.width,size.height);
35949             this.boxesEl.setSize(size.width,size.height);
35950         }
35951     },
35952     
35953     
35954     
35955     _resetLayout : function()
35956     {
35957         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35958         this.colWidth = this.el.getWidth();
35959         //this.gutter = this.el.getWidth(); 
35960         
35961         this.measureColumns();
35962
35963         // reset column Y
35964         var i = this.cols;
35965         this.colYs = [];
35966         while (i--) {
35967             this.colYs.push( 0 );
35968         }
35969     
35970         this.maxY = 0;
35971     },
35972
35973     measureColumns : function()
35974     {
35975         this.getContainerWidth();
35976       // if columnWidth is 0, default to outerWidth of first item
35977         if ( !this.columnWidth ) {
35978             var firstItem = this.bricks.first();
35979             Roo.log(firstItem);
35980             this.columnWidth  = this.containerWidth;
35981             if (firstItem && firstItem.attr('originalwidth') ) {
35982                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35983             }
35984             // columnWidth fall back to item of first element
35985             Roo.log("set column width?");
35986                         this.initialColumnWidth = this.columnWidth  ;
35987
35988             // if first elem has no width, default to size of container
35989             
35990         }
35991         
35992         
35993         if (this.initialColumnWidth) {
35994             this.columnWidth = this.initialColumnWidth;
35995         }
35996         
35997         
35998             
35999         // column width is fixed at the top - however if container width get's smaller we should
36000         // reduce it...
36001         
36002         // this bit calcs how man columns..
36003             
36004         var columnWidth = this.columnWidth += this.gutter;
36005       
36006         // calculate columns
36007         var containerWidth = this.containerWidth + this.gutter;
36008         
36009         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36010         // fix rounding errors, typically with gutters
36011         var excess = columnWidth - containerWidth % columnWidth;
36012         
36013         
36014         // if overshoot is less than a pixel, round up, otherwise floor it
36015         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36016         cols = Math[ mathMethod ]( cols );
36017         this.cols = Math.max( cols, 1 );
36018         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36019         
36020          // padding positioning..
36021         var totalColWidth = this.cols * this.columnWidth;
36022         var padavail = this.containerWidth - totalColWidth;
36023         // so for 2 columns - we need 3 'pads'
36024         
36025         var padNeeded = (1+this.cols) * this.padWidth;
36026         
36027         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36028         
36029         this.columnWidth += padExtra
36030         //this.padWidth = Math.floor(padavail /  ( this.cols));
36031         
36032         // adjust colum width so that padding is fixed??
36033         
36034         // we have 3 columns ... total = width * 3
36035         // we have X left over... that should be used by 
36036         
36037         //if (this.expandC) {
36038             
36039         //}
36040         
36041         
36042         
36043     },
36044     
36045     getContainerWidth : function()
36046     {
36047        /* // container is parent if fit width
36048         var container = this.isFitWidth ? this.element.parentNode : this.element;
36049         // check that this.size and size are there
36050         // IE8 triggers resize on body size change, so they might not be
36051         
36052         var size = getSize( container );  //FIXME
36053         this.containerWidth = size && size.innerWidth; //FIXME
36054         */
36055          
36056         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36057         
36058     },
36059     
36060     _getItemLayoutPosition : function( item )  // what is item?
36061     {
36062         // we resize the item to our columnWidth..
36063       
36064         item.setWidth(this.columnWidth);
36065         item.autoBoxAdjust  = false;
36066         
36067         var sz = item.getSize();
36068  
36069         // how many columns does this brick span
36070         var remainder = this.containerWidth % this.columnWidth;
36071         
36072         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36073         // round if off by 1 pixel, otherwise use ceil
36074         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36075         colSpan = Math.min( colSpan, this.cols );
36076         
36077         // normally this should be '1' as we dont' currently allow multi width columns..
36078         
36079         var colGroup = this._getColGroup( colSpan );
36080         // get the minimum Y value from the columns
36081         var minimumY = Math.min.apply( Math, colGroup );
36082         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36083         
36084         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36085          
36086         // position the brick
36087         var position = {
36088             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36089             y: this.currentSize.y + minimumY + this.padHeight
36090         };
36091         
36092         Roo.log(position);
36093         // apply setHeight to necessary columns
36094         var setHeight = minimumY + sz.height + this.padHeight;
36095         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36096         
36097         var setSpan = this.cols + 1 - colGroup.length;
36098         for ( var i = 0; i < setSpan; i++ ) {
36099           this.colYs[ shortColIndex + i ] = setHeight ;
36100         }
36101       
36102         return position;
36103     },
36104     
36105     /**
36106      * @param {Number} colSpan - number of columns the element spans
36107      * @returns {Array} colGroup
36108      */
36109     _getColGroup : function( colSpan )
36110     {
36111         if ( colSpan < 2 ) {
36112           // if brick spans only one column, use all the column Ys
36113           return this.colYs;
36114         }
36115       
36116         var colGroup = [];
36117         // how many different places could this brick fit horizontally
36118         var groupCount = this.cols + 1 - colSpan;
36119         // for each group potential horizontal position
36120         for ( var i = 0; i < groupCount; i++ ) {
36121           // make an array of colY values for that one group
36122           var groupColYs = this.colYs.slice( i, i + colSpan );
36123           // and get the max value of the array
36124           colGroup[i] = Math.max.apply( Math, groupColYs );
36125         }
36126         return colGroup;
36127     },
36128     /*
36129     _manageStamp : function( stamp )
36130     {
36131         var stampSize =  stamp.getSize();
36132         var offset = stamp.getBox();
36133         // get the columns that this stamp affects
36134         var firstX = this.isOriginLeft ? offset.x : offset.right;
36135         var lastX = firstX + stampSize.width;
36136         var firstCol = Math.floor( firstX / this.columnWidth );
36137         firstCol = Math.max( 0, firstCol );
36138         
36139         var lastCol = Math.floor( lastX / this.columnWidth );
36140         // lastCol should not go over if multiple of columnWidth #425
36141         lastCol -= lastX % this.columnWidth ? 0 : 1;
36142         lastCol = Math.min( this.cols - 1, lastCol );
36143         
36144         // set colYs to bottom of the stamp
36145         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36146             stampSize.height;
36147             
36148         for ( var i = firstCol; i <= lastCol; i++ ) {
36149           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36150         }
36151     },
36152     */
36153     
36154     _getContainerSize : function()
36155     {
36156         this.maxY = Math.max.apply( Math, this.colYs );
36157         var size = {
36158             height: this.maxY
36159         };
36160       
36161         if ( this.isFitWidth ) {
36162             size.width = this._getContainerFitWidth();
36163         }
36164       
36165         return size;
36166     },
36167     
36168     _getContainerFitWidth : function()
36169     {
36170         var unusedCols = 0;
36171         // count unused columns
36172         var i = this.cols;
36173         while ( --i ) {
36174           if ( this.colYs[i] !== 0 ) {
36175             break;
36176           }
36177           unusedCols++;
36178         }
36179         // fit container to columns that have been used
36180         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36181     },
36182     
36183     needsResizeLayout : function()
36184     {
36185         var previousWidth = this.containerWidth;
36186         this.getContainerWidth();
36187         return previousWidth !== this.containerWidth;
36188     }
36189  
36190 });
36191
36192  
36193
36194  /*
36195  * - LGPL
36196  *
36197  * element
36198  * 
36199  */
36200
36201 /**
36202  * @class Roo.bootstrap.MasonryBrick
36203  * @extends Roo.bootstrap.Component
36204  * Bootstrap MasonryBrick class
36205  * 
36206  * @constructor
36207  * Create a new MasonryBrick
36208  * @param {Object} config The config object
36209  */
36210
36211 Roo.bootstrap.MasonryBrick = function(config){
36212     
36213     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36214     
36215     Roo.bootstrap.MasonryBrick.register(this);
36216     
36217     this.addEvents({
36218         // raw events
36219         /**
36220          * @event click
36221          * When a MasonryBrick is clcik
36222          * @param {Roo.bootstrap.MasonryBrick} this
36223          * @param {Roo.EventObject} e
36224          */
36225         "click" : true
36226     });
36227 };
36228
36229 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36230     
36231     /**
36232      * @cfg {String} title
36233      */   
36234     title : '',
36235     /**
36236      * @cfg {String} html
36237      */   
36238     html : '',
36239     /**
36240      * @cfg {String} bgimage
36241      */   
36242     bgimage : '',
36243     /**
36244      * @cfg {String} videourl
36245      */   
36246     videourl : '',
36247     /**
36248      * @cfg {String} cls
36249      */   
36250     cls : '',
36251     /**
36252      * @cfg {String} href
36253      */   
36254     href : '',
36255     /**
36256      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36257      */   
36258     size : 'xs',
36259     
36260     /**
36261      * @cfg {String} placetitle (center|bottom)
36262      */   
36263     placetitle : '',
36264     
36265     /**
36266      * @cfg {Boolean} isFitContainer defalut true
36267      */   
36268     isFitContainer : true, 
36269     
36270     /**
36271      * @cfg {Boolean} preventDefault defalut false
36272      */   
36273     preventDefault : false, 
36274     
36275     /**
36276      * @cfg {Boolean} inverse defalut false
36277      */   
36278     maskInverse : false, 
36279     
36280     getAutoCreate : function()
36281     {
36282         if(!this.isFitContainer){
36283             return this.getSplitAutoCreate();
36284         }
36285         
36286         var cls = 'masonry-brick masonry-brick-full';
36287         
36288         if(this.href.length){
36289             cls += ' masonry-brick-link';
36290         }
36291         
36292         if(this.bgimage.length){
36293             cls += ' masonry-brick-image';
36294         }
36295         
36296         if(this.maskInverse){
36297             cls += ' mask-inverse';
36298         }
36299         
36300         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36301             cls += ' enable-mask';
36302         }
36303         
36304         if(this.size){
36305             cls += ' masonry-' + this.size + '-brick';
36306         }
36307         
36308         if(this.placetitle.length){
36309             
36310             switch (this.placetitle) {
36311                 case 'center' :
36312                     cls += ' masonry-center-title';
36313                     break;
36314                 case 'bottom' :
36315                     cls += ' masonry-bottom-title';
36316                     break;
36317                 default:
36318                     break;
36319             }
36320             
36321         } else {
36322             if(!this.html.length && !this.bgimage.length){
36323                 cls += ' masonry-center-title';
36324             }
36325
36326             if(!this.html.length && this.bgimage.length){
36327                 cls += ' masonry-bottom-title';
36328             }
36329         }
36330         
36331         if(this.cls){
36332             cls += ' ' + this.cls;
36333         }
36334         
36335         var cfg = {
36336             tag: (this.href.length) ? 'a' : 'div',
36337             cls: cls,
36338             cn: [
36339                 {
36340                     tag: 'div',
36341                     cls: 'masonry-brick-mask'
36342                 },
36343                 {
36344                     tag: 'div',
36345                     cls: 'masonry-brick-paragraph',
36346                     cn: []
36347                 }
36348             ]
36349         };
36350         
36351         if(this.href.length){
36352             cfg.href = this.href;
36353         }
36354         
36355         var cn = cfg.cn[1].cn;
36356         
36357         if(this.title.length){
36358             cn.push({
36359                 tag: 'h4',
36360                 cls: 'masonry-brick-title',
36361                 html: this.title
36362             });
36363         }
36364         
36365         if(this.html.length){
36366             cn.push({
36367                 tag: 'p',
36368                 cls: 'masonry-brick-text',
36369                 html: this.html
36370             });
36371         }
36372         
36373         if (!this.title.length && !this.html.length) {
36374             cfg.cn[1].cls += ' hide';
36375         }
36376         
36377         if(this.bgimage.length){
36378             cfg.cn.push({
36379                 tag: 'img',
36380                 cls: 'masonry-brick-image-view',
36381                 src: this.bgimage
36382             });
36383         }
36384         
36385         if(this.videourl.length){
36386             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36387             // youtube support only?
36388             cfg.cn.push({
36389                 tag: 'iframe',
36390                 cls: 'masonry-brick-image-view',
36391                 src: vurl,
36392                 frameborder : 0,
36393                 allowfullscreen : true
36394             });
36395         }
36396         
36397         return cfg;
36398         
36399     },
36400     
36401     getSplitAutoCreate : function()
36402     {
36403         var cls = 'masonry-brick masonry-brick-split';
36404         
36405         if(this.href.length){
36406             cls += ' masonry-brick-link';
36407         }
36408         
36409         if(this.bgimage.length){
36410             cls += ' masonry-brick-image';
36411         }
36412         
36413         if(this.size){
36414             cls += ' masonry-' + this.size + '-brick';
36415         }
36416         
36417         switch (this.placetitle) {
36418             case 'center' :
36419                 cls += ' masonry-center-title';
36420                 break;
36421             case 'bottom' :
36422                 cls += ' masonry-bottom-title';
36423                 break;
36424             default:
36425                 if(!this.bgimage.length){
36426                     cls += ' masonry-center-title';
36427                 }
36428
36429                 if(this.bgimage.length){
36430                     cls += ' masonry-bottom-title';
36431                 }
36432                 break;
36433         }
36434         
36435         if(this.cls){
36436             cls += ' ' + this.cls;
36437         }
36438         
36439         var cfg = {
36440             tag: (this.href.length) ? 'a' : 'div',
36441             cls: cls,
36442             cn: [
36443                 {
36444                     tag: 'div',
36445                     cls: 'masonry-brick-split-head',
36446                     cn: [
36447                         {
36448                             tag: 'div',
36449                             cls: 'masonry-brick-paragraph',
36450                             cn: []
36451                         }
36452                     ]
36453                 },
36454                 {
36455                     tag: 'div',
36456                     cls: 'masonry-brick-split-body',
36457                     cn: []
36458                 }
36459             ]
36460         };
36461         
36462         if(this.href.length){
36463             cfg.href = this.href;
36464         }
36465         
36466         if(this.title.length){
36467             cfg.cn[0].cn[0].cn.push({
36468                 tag: 'h4',
36469                 cls: 'masonry-brick-title',
36470                 html: this.title
36471             });
36472         }
36473         
36474         if(this.html.length){
36475             cfg.cn[1].cn.push({
36476                 tag: 'p',
36477                 cls: 'masonry-brick-text',
36478                 html: this.html
36479             });
36480         }
36481
36482         if(this.bgimage.length){
36483             cfg.cn[0].cn.push({
36484                 tag: 'img',
36485                 cls: 'masonry-brick-image-view',
36486                 src: this.bgimage
36487             });
36488         }
36489         
36490         if(this.videourl.length){
36491             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36492             // youtube support only?
36493             cfg.cn[0].cn.cn.push({
36494                 tag: 'iframe',
36495                 cls: 'masonry-brick-image-view',
36496                 src: vurl,
36497                 frameborder : 0,
36498                 allowfullscreen : true
36499             });
36500         }
36501         
36502         return cfg;
36503     },
36504     
36505     initEvents: function() 
36506     {
36507         switch (this.size) {
36508             case 'xs' :
36509                 this.x = 1;
36510                 this.y = 1;
36511                 break;
36512             case 'sm' :
36513                 this.x = 2;
36514                 this.y = 2;
36515                 break;
36516             case 'md' :
36517             case 'md-left' :
36518             case 'md-right' :
36519                 this.x = 3;
36520                 this.y = 3;
36521                 break;
36522             case 'tall' :
36523                 this.x = 2;
36524                 this.y = 3;
36525                 break;
36526             case 'wide' :
36527                 this.x = 3;
36528                 this.y = 2;
36529                 break;
36530             case 'wide-thin' :
36531                 this.x = 3;
36532                 this.y = 1;
36533                 break;
36534                         
36535             default :
36536                 break;
36537         }
36538         
36539         if(Roo.isTouch){
36540             this.el.on('touchstart', this.onTouchStart, this);
36541             this.el.on('touchmove', this.onTouchMove, this);
36542             this.el.on('touchend', this.onTouchEnd, this);
36543             this.el.on('contextmenu', this.onContextMenu, this);
36544         } else {
36545             this.el.on('mouseenter'  ,this.enter, this);
36546             this.el.on('mouseleave', this.leave, this);
36547             this.el.on('click', this.onClick, this);
36548         }
36549         
36550         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36551             this.parent().bricks.push(this);   
36552         }
36553         
36554     },
36555     
36556     onClick: function(e, el)
36557     {
36558         var time = this.endTimer - this.startTimer;
36559         // Roo.log(e.preventDefault());
36560         if(Roo.isTouch){
36561             if(time > 1000){
36562                 e.preventDefault();
36563                 return;
36564             }
36565         }
36566         
36567         if(!this.preventDefault){
36568             return;
36569         }
36570         
36571         e.preventDefault();
36572         
36573         if (this.activeClass != '') {
36574             this.selectBrick();
36575         }
36576         
36577         this.fireEvent('click', this, e);
36578     },
36579     
36580     enter: function(e, el)
36581     {
36582         e.preventDefault();
36583         
36584         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36585             return;
36586         }
36587         
36588         if(this.bgimage.length && this.html.length){
36589             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36590         }
36591     },
36592     
36593     leave: function(e, el)
36594     {
36595         e.preventDefault();
36596         
36597         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36598             return;
36599         }
36600         
36601         if(this.bgimage.length && this.html.length){
36602             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36603         }
36604     },
36605     
36606     onTouchStart: function(e, el)
36607     {
36608 //        e.preventDefault();
36609         
36610         this.touchmoved = false;
36611         
36612         if(!this.isFitContainer){
36613             return;
36614         }
36615         
36616         if(!this.bgimage.length || !this.html.length){
36617             return;
36618         }
36619         
36620         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36621         
36622         this.timer = new Date().getTime();
36623         
36624     },
36625     
36626     onTouchMove: function(e, el)
36627     {
36628         this.touchmoved = true;
36629     },
36630     
36631     onContextMenu : function(e,el)
36632     {
36633         e.preventDefault();
36634         e.stopPropagation();
36635         return false;
36636     },
36637     
36638     onTouchEnd: function(e, el)
36639     {
36640 //        e.preventDefault();
36641         
36642         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36643         
36644             this.leave(e,el);
36645             
36646             return;
36647         }
36648         
36649         if(!this.bgimage.length || !this.html.length){
36650             
36651             if(this.href.length){
36652                 window.location.href = this.href;
36653             }
36654             
36655             return;
36656         }
36657         
36658         if(!this.isFitContainer){
36659             return;
36660         }
36661         
36662         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36663         
36664         window.location.href = this.href;
36665     },
36666     
36667     //selection on single brick only
36668     selectBrick : function() {
36669         
36670         if (!this.parentId) {
36671             return;
36672         }
36673         
36674         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36675         var index = m.selectedBrick.indexOf(this.id);
36676         
36677         if ( index > -1) {
36678             m.selectedBrick.splice(index,1);
36679             this.el.removeClass(this.activeClass);
36680             return;
36681         }
36682         
36683         for(var i = 0; i < m.selectedBrick.length; i++) {
36684             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36685             b.el.removeClass(b.activeClass);
36686         }
36687         
36688         m.selectedBrick = [];
36689         
36690         m.selectedBrick.push(this.id);
36691         this.el.addClass(this.activeClass);
36692         return;
36693     },
36694     
36695     isSelected : function(){
36696         return this.el.hasClass(this.activeClass);
36697         
36698     }
36699 });
36700
36701 Roo.apply(Roo.bootstrap.MasonryBrick, {
36702     
36703     //groups: {},
36704     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36705      /**
36706     * register a Masonry Brick
36707     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36708     */
36709     
36710     register : function(brick)
36711     {
36712         //this.groups[brick.id] = brick;
36713         this.groups.add(brick.id, brick);
36714     },
36715     /**
36716     * fetch a  masonry brick based on the masonry brick ID
36717     * @param {string} the masonry brick to add
36718     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36719     */
36720     
36721     get: function(brick_id) 
36722     {
36723         // if (typeof(this.groups[brick_id]) == 'undefined') {
36724         //     return false;
36725         // }
36726         // return this.groups[brick_id] ;
36727         
36728         if(this.groups.key(brick_id)) {
36729             return this.groups.key(brick_id);
36730         }
36731         
36732         return false;
36733     }
36734     
36735     
36736     
36737 });
36738
36739  /*
36740  * - LGPL
36741  *
36742  * element
36743  * 
36744  */
36745
36746 /**
36747  * @class Roo.bootstrap.Brick
36748  * @extends Roo.bootstrap.Component
36749  * Bootstrap Brick class
36750  * 
36751  * @constructor
36752  * Create a new Brick
36753  * @param {Object} config The config object
36754  */
36755
36756 Roo.bootstrap.Brick = function(config){
36757     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36758     
36759     this.addEvents({
36760         // raw events
36761         /**
36762          * @event click
36763          * When a Brick is click
36764          * @param {Roo.bootstrap.Brick} this
36765          * @param {Roo.EventObject} e
36766          */
36767         "click" : true
36768     });
36769 };
36770
36771 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36772     
36773     /**
36774      * @cfg {String} title
36775      */   
36776     title : '',
36777     /**
36778      * @cfg {String} html
36779      */   
36780     html : '',
36781     /**
36782      * @cfg {String} bgimage
36783      */   
36784     bgimage : '',
36785     /**
36786      * @cfg {String} cls
36787      */   
36788     cls : '',
36789     /**
36790      * @cfg {String} href
36791      */   
36792     href : '',
36793     /**
36794      * @cfg {String} video
36795      */   
36796     video : '',
36797     /**
36798      * @cfg {Boolean} square
36799      */   
36800     square : true,
36801     
36802     getAutoCreate : function()
36803     {
36804         var cls = 'roo-brick';
36805         
36806         if(this.href.length){
36807             cls += ' roo-brick-link';
36808         }
36809         
36810         if(this.bgimage.length){
36811             cls += ' roo-brick-image';
36812         }
36813         
36814         if(!this.html.length && !this.bgimage.length){
36815             cls += ' roo-brick-center-title';
36816         }
36817         
36818         if(!this.html.length && this.bgimage.length){
36819             cls += ' roo-brick-bottom-title';
36820         }
36821         
36822         if(this.cls){
36823             cls += ' ' + this.cls;
36824         }
36825         
36826         var cfg = {
36827             tag: (this.href.length) ? 'a' : 'div',
36828             cls: cls,
36829             cn: [
36830                 {
36831                     tag: 'div',
36832                     cls: 'roo-brick-paragraph',
36833                     cn: []
36834                 }
36835             ]
36836         };
36837         
36838         if(this.href.length){
36839             cfg.href = this.href;
36840         }
36841         
36842         var cn = cfg.cn[0].cn;
36843         
36844         if(this.title.length){
36845             cn.push({
36846                 tag: 'h4',
36847                 cls: 'roo-brick-title',
36848                 html: this.title
36849             });
36850         }
36851         
36852         if(this.html.length){
36853             cn.push({
36854                 tag: 'p',
36855                 cls: 'roo-brick-text',
36856                 html: this.html
36857             });
36858         } else {
36859             cn.cls += ' hide';
36860         }
36861         
36862         if(this.bgimage.length){
36863             cfg.cn.push({
36864                 tag: 'img',
36865                 cls: 'roo-brick-image-view',
36866                 src: this.bgimage
36867             });
36868         }
36869         
36870         return cfg;
36871     },
36872     
36873     initEvents: function() 
36874     {
36875         if(this.title.length || this.html.length){
36876             this.el.on('mouseenter'  ,this.enter, this);
36877             this.el.on('mouseleave', this.leave, this);
36878         }
36879         
36880         Roo.EventManager.onWindowResize(this.resize, this); 
36881         
36882         if(this.bgimage.length){
36883             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36884             this.imageEl.on('load', this.onImageLoad, this);
36885             return;
36886         }
36887         
36888         this.resize();
36889     },
36890     
36891     onImageLoad : function()
36892     {
36893         this.resize();
36894     },
36895     
36896     resize : function()
36897     {
36898         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36899         
36900         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36901         
36902         if(this.bgimage.length){
36903             var image = this.el.select('.roo-brick-image-view', true).first();
36904             
36905             image.setWidth(paragraph.getWidth());
36906             
36907             if(this.square){
36908                 image.setHeight(paragraph.getWidth());
36909             }
36910             
36911             this.el.setHeight(image.getHeight());
36912             paragraph.setHeight(image.getHeight());
36913             
36914         }
36915         
36916     },
36917     
36918     enter: function(e, el)
36919     {
36920         e.preventDefault();
36921         
36922         if(this.bgimage.length){
36923             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36924             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36925         }
36926     },
36927     
36928     leave: function(e, el)
36929     {
36930         e.preventDefault();
36931         
36932         if(this.bgimage.length){
36933             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36934             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36935         }
36936     }
36937     
36938 });
36939
36940  
36941
36942  /*
36943  * - LGPL
36944  *
36945  * Number field 
36946  */
36947
36948 /**
36949  * @class Roo.bootstrap.NumberField
36950  * @extends Roo.bootstrap.Input
36951  * Bootstrap NumberField class
36952  * 
36953  * 
36954  * 
36955  * 
36956  * @constructor
36957  * Create a new NumberField
36958  * @param {Object} config The config object
36959  */
36960
36961 Roo.bootstrap.NumberField = function(config){
36962     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36963 };
36964
36965 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36966     
36967     /**
36968      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36969      */
36970     allowDecimals : true,
36971     /**
36972      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36973      */
36974     decimalSeparator : ".",
36975     /**
36976      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36977      */
36978     decimalPrecision : 2,
36979     /**
36980      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36981      */
36982     allowNegative : true,
36983     
36984     /**
36985      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36986      */
36987     allowZero: true,
36988     /**
36989      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36990      */
36991     minValue : Number.NEGATIVE_INFINITY,
36992     /**
36993      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36994      */
36995     maxValue : Number.MAX_VALUE,
36996     /**
36997      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36998      */
36999     minText : "The minimum value for this field is {0}",
37000     /**
37001      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37002      */
37003     maxText : "The maximum value for this field is {0}",
37004     /**
37005      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37006      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37007      */
37008     nanText : "{0} is not a valid number",
37009     /**
37010      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37011      */
37012     thousandsDelimiter : false,
37013     /**
37014      * @cfg {String} valueAlign alignment of value
37015      */
37016     valueAlign : "left",
37017
37018     getAutoCreate : function()
37019     {
37020         var hiddenInput = {
37021             tag: 'input',
37022             type: 'hidden',
37023             id: Roo.id(),
37024             cls: 'hidden-number-input'
37025         };
37026         
37027         if (this.name) {
37028             hiddenInput.name = this.name;
37029         }
37030         
37031         this.name = '';
37032         
37033         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37034         
37035         this.name = hiddenInput.name;
37036         
37037         if(cfg.cn.length > 0) {
37038             cfg.cn.push(hiddenInput);
37039         }
37040         
37041         return cfg;
37042     },
37043
37044     // private
37045     initEvents : function()
37046     {   
37047         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37048         
37049         var allowed = "0123456789";
37050         
37051         if(this.allowDecimals){
37052             allowed += this.decimalSeparator;
37053         }
37054         
37055         if(this.allowNegative){
37056             allowed += "-";
37057         }
37058         
37059         if(this.thousandsDelimiter) {
37060             allowed += ",";
37061         }
37062         
37063         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37064         
37065         var keyPress = function(e){
37066             
37067             var k = e.getKey();
37068             
37069             var c = e.getCharCode();
37070             
37071             if(
37072                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37073                     allowed.indexOf(String.fromCharCode(c)) === -1
37074             ){
37075                 e.stopEvent();
37076                 return;
37077             }
37078             
37079             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37080                 return;
37081             }
37082             
37083             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37084                 e.stopEvent();
37085             }
37086         };
37087         
37088         this.el.on("keypress", keyPress, this);
37089     },
37090     
37091     validateValue : function(value)
37092     {
37093         
37094         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37095             return false;
37096         }
37097         
37098         var num = this.parseValue(value);
37099         
37100         if(isNaN(num)){
37101             this.markInvalid(String.format(this.nanText, value));
37102             return false;
37103         }
37104         
37105         if(num < this.minValue){
37106             this.markInvalid(String.format(this.minText, this.minValue));
37107             return false;
37108         }
37109         
37110         if(num > this.maxValue){
37111             this.markInvalid(String.format(this.maxText, this.maxValue));
37112             return false;
37113         }
37114         
37115         return true;
37116     },
37117
37118     getValue : function()
37119     {
37120         var v = this.hiddenEl().getValue();
37121         
37122         return this.fixPrecision(this.parseValue(v));
37123     },
37124
37125     parseValue : function(value)
37126     {
37127         if(this.thousandsDelimiter) {
37128             value += "";
37129             r = new RegExp(",", "g");
37130             value = value.replace(r, "");
37131         }
37132         
37133         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37134         return isNaN(value) ? '' : value;
37135     },
37136
37137     fixPrecision : function(value)
37138     {
37139         if(this.thousandsDelimiter) {
37140             value += "";
37141             r = new RegExp(",", "g");
37142             value = value.replace(r, "");
37143         }
37144         
37145         var nan = isNaN(value);
37146         
37147         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37148             return nan ? '' : value;
37149         }
37150         return parseFloat(value).toFixed(this.decimalPrecision);
37151     },
37152
37153     setValue : function(v)
37154     {
37155         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37156         
37157         this.value = v;
37158         
37159         if(this.rendered){
37160             
37161             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37162             
37163             this.inputEl().dom.value = (v == '') ? '' :
37164                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37165             
37166             if(!this.allowZero && v === '0') {
37167                 this.hiddenEl().dom.value = '';
37168                 this.inputEl().dom.value = '';
37169             }
37170             
37171             this.validate();
37172         }
37173     },
37174
37175     decimalPrecisionFcn : function(v)
37176     {
37177         return Math.floor(v);
37178     },
37179
37180     beforeBlur : function()
37181     {
37182         var v = this.parseValue(this.getRawValue());
37183         
37184         if(v || v === 0 || v === ''){
37185             this.setValue(v);
37186         }
37187     },
37188     
37189     hiddenEl : function()
37190     {
37191         return this.el.select('input.hidden-number-input',true).first();
37192     }
37193     
37194 });
37195
37196  
37197
37198 /*
37199 * Licence: LGPL
37200 */
37201
37202 /**
37203  * @class Roo.bootstrap.DocumentSlider
37204  * @extends Roo.bootstrap.Component
37205  * Bootstrap DocumentSlider class
37206  * 
37207  * @constructor
37208  * Create a new DocumentViewer
37209  * @param {Object} config The config object
37210  */
37211
37212 Roo.bootstrap.DocumentSlider = function(config){
37213     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37214     
37215     this.files = [];
37216     
37217     this.addEvents({
37218         /**
37219          * @event initial
37220          * Fire after initEvent
37221          * @param {Roo.bootstrap.DocumentSlider} this
37222          */
37223         "initial" : true,
37224         /**
37225          * @event update
37226          * Fire after update
37227          * @param {Roo.bootstrap.DocumentSlider} this
37228          */
37229         "update" : true,
37230         /**
37231          * @event click
37232          * Fire after click
37233          * @param {Roo.bootstrap.DocumentSlider} this
37234          */
37235         "click" : true
37236     });
37237 };
37238
37239 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37240     
37241     files : false,
37242     
37243     indicator : 0,
37244     
37245     getAutoCreate : function()
37246     {
37247         var cfg = {
37248             tag : 'div',
37249             cls : 'roo-document-slider',
37250             cn : [
37251                 {
37252                     tag : 'div',
37253                     cls : 'roo-document-slider-header',
37254                     cn : [
37255                         {
37256                             tag : 'div',
37257                             cls : 'roo-document-slider-header-title'
37258                         }
37259                     ]
37260                 },
37261                 {
37262                     tag : 'div',
37263                     cls : 'roo-document-slider-body',
37264                     cn : [
37265                         {
37266                             tag : 'div',
37267                             cls : 'roo-document-slider-prev',
37268                             cn : [
37269                                 {
37270                                     tag : 'i',
37271                                     cls : 'fa fa-chevron-left'
37272                                 }
37273                             ]
37274                         },
37275                         {
37276                             tag : 'div',
37277                             cls : 'roo-document-slider-thumb',
37278                             cn : [
37279                                 {
37280                                     tag : 'img',
37281                                     cls : 'roo-document-slider-image'
37282                                 }
37283                             ]
37284                         },
37285                         {
37286                             tag : 'div',
37287                             cls : 'roo-document-slider-next',
37288                             cn : [
37289                                 {
37290                                     tag : 'i',
37291                                     cls : 'fa fa-chevron-right'
37292                                 }
37293                             ]
37294                         }
37295                     ]
37296                 }
37297             ]
37298         };
37299         
37300         return cfg;
37301     },
37302     
37303     initEvents : function()
37304     {
37305         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37306         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37307         
37308         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37309         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37310         
37311         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37312         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37313         
37314         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37315         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37316         
37317         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37318         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37319         
37320         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37321         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37322         
37323         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37324         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37325         
37326         this.thumbEl.on('click', this.onClick, this);
37327         
37328         this.prevIndicator.on('click', this.prev, this);
37329         
37330         this.nextIndicator.on('click', this.next, this);
37331         
37332     },
37333     
37334     initial : function()
37335     {
37336         if(this.files.length){
37337             this.indicator = 1;
37338             this.update()
37339         }
37340         
37341         this.fireEvent('initial', this);
37342     },
37343     
37344     update : function()
37345     {
37346         this.imageEl.attr('src', this.files[this.indicator - 1]);
37347         
37348         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37349         
37350         this.prevIndicator.show();
37351         
37352         if(this.indicator == 1){
37353             this.prevIndicator.hide();
37354         }
37355         
37356         this.nextIndicator.show();
37357         
37358         if(this.indicator == this.files.length){
37359             this.nextIndicator.hide();
37360         }
37361         
37362         this.thumbEl.scrollTo('top');
37363         
37364         this.fireEvent('update', this);
37365     },
37366     
37367     onClick : function(e)
37368     {
37369         e.preventDefault();
37370         
37371         this.fireEvent('click', this);
37372     },
37373     
37374     prev : function(e)
37375     {
37376         e.preventDefault();
37377         
37378         this.indicator = Math.max(1, this.indicator - 1);
37379         
37380         this.update();
37381     },
37382     
37383     next : function(e)
37384     {
37385         e.preventDefault();
37386         
37387         this.indicator = Math.min(this.files.length, this.indicator + 1);
37388         
37389         this.update();
37390     }
37391 });
37392 /*
37393  * - LGPL
37394  *
37395  * RadioSet
37396  *
37397  *
37398  */
37399
37400 /**
37401  * @class Roo.bootstrap.RadioSet
37402  * @extends Roo.bootstrap.Input
37403  * Bootstrap RadioSet class
37404  * @cfg {String} indicatorpos (left|right) default left
37405  * @cfg {Boolean} inline (true|false) inline the element (default true)
37406  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37407  * @constructor
37408  * Create a new RadioSet
37409  * @param {Object} config The config object
37410  */
37411
37412 Roo.bootstrap.RadioSet = function(config){
37413     
37414     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37415     
37416     this.radioes = [];
37417     
37418     Roo.bootstrap.RadioSet.register(this);
37419     
37420     this.addEvents({
37421         /**
37422         * @event check
37423         * Fires when the element is checked or unchecked.
37424         * @param {Roo.bootstrap.RadioSet} this This radio
37425         * @param {Roo.bootstrap.Radio} item The checked item
37426         */
37427        check : true,
37428        /**
37429         * @event click
37430         * Fires when the element is click.
37431         * @param {Roo.bootstrap.RadioSet} this This radio set
37432         * @param {Roo.bootstrap.Radio} item The checked item
37433         * @param {Roo.EventObject} e The event object
37434         */
37435        click : true
37436     });
37437     
37438 };
37439
37440 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37441
37442     radioes : false,
37443     
37444     inline : true,
37445     
37446     weight : '',
37447     
37448     indicatorpos : 'left',
37449     
37450     getAutoCreate : function()
37451     {
37452         var label = {
37453             tag : 'label',
37454             cls : 'roo-radio-set-label',
37455             cn : [
37456                 {
37457                     tag : 'span',
37458                     html : this.fieldLabel
37459                 }
37460             ]
37461         };
37462         if (Roo.bootstrap.version == 3) {
37463             
37464             
37465             if(this.indicatorpos == 'left'){
37466                 label.cn.unshift({
37467                     tag : 'i',
37468                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37469                     tooltip : 'This field is required'
37470                 });
37471             } else {
37472                 label.cn.push({
37473                     tag : 'i',
37474                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37475                     tooltip : 'This field is required'
37476                 });
37477             }
37478         }
37479         var items = {
37480             tag : 'div',
37481             cls : 'roo-radio-set-items'
37482         };
37483         
37484         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37485         
37486         if (align === 'left' && this.fieldLabel.length) {
37487             
37488             items = {
37489                 cls : "roo-radio-set-right", 
37490                 cn: [
37491                     items
37492                 ]
37493             };
37494             
37495             if(this.labelWidth > 12){
37496                 label.style = "width: " + this.labelWidth + 'px';
37497             }
37498             
37499             if(this.labelWidth < 13 && this.labelmd == 0){
37500                 this.labelmd = this.labelWidth;
37501             }
37502             
37503             if(this.labellg > 0){
37504                 label.cls += ' col-lg-' + this.labellg;
37505                 items.cls += ' col-lg-' + (12 - this.labellg);
37506             }
37507             
37508             if(this.labelmd > 0){
37509                 label.cls += ' col-md-' + this.labelmd;
37510                 items.cls += ' col-md-' + (12 - this.labelmd);
37511             }
37512             
37513             if(this.labelsm > 0){
37514                 label.cls += ' col-sm-' + this.labelsm;
37515                 items.cls += ' col-sm-' + (12 - this.labelsm);
37516             }
37517             
37518             if(this.labelxs > 0){
37519                 label.cls += ' col-xs-' + this.labelxs;
37520                 items.cls += ' col-xs-' + (12 - this.labelxs);
37521             }
37522         }
37523         
37524         var cfg = {
37525             tag : 'div',
37526             cls : 'roo-radio-set',
37527             cn : [
37528                 {
37529                     tag : 'input',
37530                     cls : 'roo-radio-set-input',
37531                     type : 'hidden',
37532                     name : this.name,
37533                     value : this.value ? this.value :  ''
37534                 },
37535                 label,
37536                 items
37537             ]
37538         };
37539         
37540         if(this.weight.length){
37541             cfg.cls += ' roo-radio-' + this.weight;
37542         }
37543         
37544         if(this.inline) {
37545             cfg.cls += ' roo-radio-set-inline';
37546         }
37547         
37548         var settings=this;
37549         ['xs','sm','md','lg'].map(function(size){
37550             if (settings[size]) {
37551                 cfg.cls += ' col-' + size + '-' + settings[size];
37552             }
37553         });
37554         
37555         return cfg;
37556         
37557     },
37558
37559     initEvents : function()
37560     {
37561         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37562         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37563         
37564         if(!this.fieldLabel.length){
37565             this.labelEl.hide();
37566         }
37567         
37568         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37569         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37570         
37571         this.indicator = this.indicatorEl();
37572         
37573         if(this.indicator){
37574             this.indicator.addClass('invisible');
37575         }
37576         
37577         this.originalValue = this.getValue();
37578         
37579     },
37580     
37581     inputEl: function ()
37582     {
37583         return this.el.select('.roo-radio-set-input', true).first();
37584     },
37585     
37586     getChildContainer : function()
37587     {
37588         return this.itemsEl;
37589     },
37590     
37591     register : function(item)
37592     {
37593         this.radioes.push(item);
37594         
37595     },
37596     
37597     validate : function()
37598     {   
37599         if(this.getVisibilityEl().hasClass('hidden')){
37600             return true;
37601         }
37602         
37603         var valid = false;
37604         
37605         Roo.each(this.radioes, function(i){
37606             if(!i.checked){
37607                 return;
37608             }
37609             
37610             valid = true;
37611             return false;
37612         });
37613         
37614         if(this.allowBlank) {
37615             return true;
37616         }
37617         
37618         if(this.disabled || valid){
37619             this.markValid();
37620             return true;
37621         }
37622         
37623         this.markInvalid();
37624         return false;
37625         
37626     },
37627     
37628     markValid : function()
37629     {
37630         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37631             this.indicatorEl().removeClass('visible');
37632             this.indicatorEl().addClass('invisible');
37633         }
37634         
37635         
37636         if (Roo.bootstrap.version == 3) {
37637             this.el.removeClass([this.invalidClass, this.validClass]);
37638             this.el.addClass(this.validClass);
37639         } else {
37640             this.el.removeClass(['is-invalid','is-valid']);
37641             this.el.addClass(['is-valid']);
37642         }
37643         this.fireEvent('valid', this);
37644     },
37645     
37646     markInvalid : function(msg)
37647     {
37648         if(this.allowBlank || this.disabled){
37649             return;
37650         }
37651         
37652         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37653             this.indicatorEl().removeClass('invisible');
37654             this.indicatorEl().addClass('visible');
37655         }
37656         if (Roo.bootstrap.version == 3) {
37657             this.el.removeClass([this.invalidClass, this.validClass]);
37658             this.el.addClass(this.invalidClass);
37659         } else {
37660             this.el.removeClass(['is-invalid','is-valid']);
37661             this.el.addClass(['is-invalid']);
37662         }
37663         
37664         this.fireEvent('invalid', this, msg);
37665         
37666     },
37667     
37668     setValue : function(v, suppressEvent)
37669     {   
37670         if(this.value === v){
37671             return;
37672         }
37673         
37674         this.value = v;
37675         
37676         if(this.rendered){
37677             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37678         }
37679         
37680         Roo.each(this.radioes, function(i){
37681             i.checked = false;
37682             i.el.removeClass('checked');
37683         });
37684         
37685         Roo.each(this.radioes, function(i){
37686             
37687             if(i.value === v || i.value.toString() === v.toString()){
37688                 i.checked = true;
37689                 i.el.addClass('checked');
37690                 
37691                 if(suppressEvent !== true){
37692                     this.fireEvent('check', this, i);
37693                 }
37694                 
37695                 return false;
37696             }
37697             
37698         }, this);
37699         
37700         this.validate();
37701     },
37702     
37703     clearInvalid : function(){
37704         
37705         if(!this.el || this.preventMark){
37706             return;
37707         }
37708         
37709         this.el.removeClass([this.invalidClass]);
37710         
37711         this.fireEvent('valid', this);
37712     }
37713     
37714 });
37715
37716 Roo.apply(Roo.bootstrap.RadioSet, {
37717     
37718     groups: {},
37719     
37720     register : function(set)
37721     {
37722         this.groups[set.name] = set;
37723     },
37724     
37725     get: function(name) 
37726     {
37727         if (typeof(this.groups[name]) == 'undefined') {
37728             return false;
37729         }
37730         
37731         return this.groups[name] ;
37732     }
37733     
37734 });
37735 /*
37736  * Based on:
37737  * Ext JS Library 1.1.1
37738  * Copyright(c) 2006-2007, Ext JS, LLC.
37739  *
37740  * Originally Released Under LGPL - original licence link has changed is not relivant.
37741  *
37742  * Fork - LGPL
37743  * <script type="text/javascript">
37744  */
37745
37746
37747 /**
37748  * @class Roo.bootstrap.SplitBar
37749  * @extends Roo.util.Observable
37750  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37751  * <br><br>
37752  * Usage:
37753  * <pre><code>
37754 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37755                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37756 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37757 split.minSize = 100;
37758 split.maxSize = 600;
37759 split.animate = true;
37760 split.on('moved', splitterMoved);
37761 </code></pre>
37762  * @constructor
37763  * Create a new SplitBar
37764  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37765  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37766  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37767  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37768                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37769                         position of the SplitBar).
37770  */
37771 Roo.bootstrap.SplitBar = function(cfg){
37772     
37773     /** @private */
37774     
37775     //{
37776     //  dragElement : elm
37777     //  resizingElement: el,
37778         // optional..
37779     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37780     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37781         // existingProxy ???
37782     //}
37783     
37784     this.el = Roo.get(cfg.dragElement, true);
37785     this.el.dom.unselectable = "on";
37786     /** @private */
37787     this.resizingEl = Roo.get(cfg.resizingElement, true);
37788
37789     /**
37790      * @private
37791      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37792      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37793      * @type Number
37794      */
37795     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37796     
37797     /**
37798      * The minimum size of the resizing element. (Defaults to 0)
37799      * @type Number
37800      */
37801     this.minSize = 0;
37802     
37803     /**
37804      * The maximum size of the resizing element. (Defaults to 2000)
37805      * @type Number
37806      */
37807     this.maxSize = 2000;
37808     
37809     /**
37810      * Whether to animate the transition to the new size
37811      * @type Boolean
37812      */
37813     this.animate = false;
37814     
37815     /**
37816      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37817      * @type Boolean
37818      */
37819     this.useShim = false;
37820     
37821     /** @private */
37822     this.shim = null;
37823     
37824     if(!cfg.existingProxy){
37825         /** @private */
37826         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37827     }else{
37828         this.proxy = Roo.get(cfg.existingProxy).dom;
37829     }
37830     /** @private */
37831     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37832     
37833     /** @private */
37834     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37835     
37836     /** @private */
37837     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37838     
37839     /** @private */
37840     this.dragSpecs = {};
37841     
37842     /**
37843      * @private The adapter to use to positon and resize elements
37844      */
37845     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37846     this.adapter.init(this);
37847     
37848     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37849         /** @private */
37850         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37851         this.el.addClass("roo-splitbar-h");
37852     }else{
37853         /** @private */
37854         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37855         this.el.addClass("roo-splitbar-v");
37856     }
37857     
37858     this.addEvents({
37859         /**
37860          * @event resize
37861          * Fires when the splitter is moved (alias for {@link #event-moved})
37862          * @param {Roo.bootstrap.SplitBar} this
37863          * @param {Number} newSize the new width or height
37864          */
37865         "resize" : true,
37866         /**
37867          * @event moved
37868          * Fires when the splitter is moved
37869          * @param {Roo.bootstrap.SplitBar} this
37870          * @param {Number} newSize the new width or height
37871          */
37872         "moved" : true,
37873         /**
37874          * @event beforeresize
37875          * Fires before the splitter is dragged
37876          * @param {Roo.bootstrap.SplitBar} this
37877          */
37878         "beforeresize" : true,
37879
37880         "beforeapply" : true
37881     });
37882
37883     Roo.util.Observable.call(this);
37884 };
37885
37886 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37887     onStartProxyDrag : function(x, y){
37888         this.fireEvent("beforeresize", this);
37889         if(!this.overlay){
37890             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37891             o.unselectable();
37892             o.enableDisplayMode("block");
37893             // all splitbars share the same overlay
37894             Roo.bootstrap.SplitBar.prototype.overlay = o;
37895         }
37896         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37897         this.overlay.show();
37898         Roo.get(this.proxy).setDisplayed("block");
37899         var size = this.adapter.getElementSize(this);
37900         this.activeMinSize = this.getMinimumSize();;
37901         this.activeMaxSize = this.getMaximumSize();;
37902         var c1 = size - this.activeMinSize;
37903         var c2 = Math.max(this.activeMaxSize - size, 0);
37904         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37905             this.dd.resetConstraints();
37906             this.dd.setXConstraint(
37907                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37908                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37909             );
37910             this.dd.setYConstraint(0, 0);
37911         }else{
37912             this.dd.resetConstraints();
37913             this.dd.setXConstraint(0, 0);
37914             this.dd.setYConstraint(
37915                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37916                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37917             );
37918          }
37919         this.dragSpecs.startSize = size;
37920         this.dragSpecs.startPoint = [x, y];
37921         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37922     },
37923     
37924     /** 
37925      * @private Called after the drag operation by the DDProxy
37926      */
37927     onEndProxyDrag : function(e){
37928         Roo.get(this.proxy).setDisplayed(false);
37929         var endPoint = Roo.lib.Event.getXY(e);
37930         if(this.overlay){
37931             this.overlay.hide();
37932         }
37933         var newSize;
37934         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37935             newSize = this.dragSpecs.startSize + 
37936                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37937                     endPoint[0] - this.dragSpecs.startPoint[0] :
37938                     this.dragSpecs.startPoint[0] - endPoint[0]
37939                 );
37940         }else{
37941             newSize = this.dragSpecs.startSize + 
37942                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37943                     endPoint[1] - this.dragSpecs.startPoint[1] :
37944                     this.dragSpecs.startPoint[1] - endPoint[1]
37945                 );
37946         }
37947         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37948         if(newSize != this.dragSpecs.startSize){
37949             if(this.fireEvent('beforeapply', this, newSize) !== false){
37950                 this.adapter.setElementSize(this, newSize);
37951                 this.fireEvent("moved", this, newSize);
37952                 this.fireEvent("resize", this, newSize);
37953             }
37954         }
37955     },
37956     
37957     /**
37958      * Get the adapter this SplitBar uses
37959      * @return The adapter object
37960      */
37961     getAdapter : function(){
37962         return this.adapter;
37963     },
37964     
37965     /**
37966      * Set the adapter this SplitBar uses
37967      * @param {Object} adapter A SplitBar adapter object
37968      */
37969     setAdapter : function(adapter){
37970         this.adapter = adapter;
37971         this.adapter.init(this);
37972     },
37973     
37974     /**
37975      * Gets the minimum size for the resizing element
37976      * @return {Number} The minimum size
37977      */
37978     getMinimumSize : function(){
37979         return this.minSize;
37980     },
37981     
37982     /**
37983      * Sets the minimum size for the resizing element
37984      * @param {Number} minSize The minimum size
37985      */
37986     setMinimumSize : function(minSize){
37987         this.minSize = minSize;
37988     },
37989     
37990     /**
37991      * Gets the maximum size for the resizing element
37992      * @return {Number} The maximum size
37993      */
37994     getMaximumSize : function(){
37995         return this.maxSize;
37996     },
37997     
37998     /**
37999      * Sets the maximum size for the resizing element
38000      * @param {Number} maxSize The maximum size
38001      */
38002     setMaximumSize : function(maxSize){
38003         this.maxSize = maxSize;
38004     },
38005     
38006     /**
38007      * Sets the initialize size for the resizing element
38008      * @param {Number} size The initial size
38009      */
38010     setCurrentSize : function(size){
38011         var oldAnimate = this.animate;
38012         this.animate = false;
38013         this.adapter.setElementSize(this, size);
38014         this.animate = oldAnimate;
38015     },
38016     
38017     /**
38018      * Destroy this splitbar. 
38019      * @param {Boolean} removeEl True to remove the element
38020      */
38021     destroy : function(removeEl){
38022         if(this.shim){
38023             this.shim.remove();
38024         }
38025         this.dd.unreg();
38026         this.proxy.parentNode.removeChild(this.proxy);
38027         if(removeEl){
38028             this.el.remove();
38029         }
38030     }
38031 });
38032
38033 /**
38034  * @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.
38035  */
38036 Roo.bootstrap.SplitBar.createProxy = function(dir){
38037     var proxy = new Roo.Element(document.createElement("div"));
38038     proxy.unselectable();
38039     var cls = 'roo-splitbar-proxy';
38040     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38041     document.body.appendChild(proxy.dom);
38042     return proxy.dom;
38043 };
38044
38045 /** 
38046  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38047  * Default Adapter. It assumes the splitter and resizing element are not positioned
38048  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38049  */
38050 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38051 };
38052
38053 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38054     // do nothing for now
38055     init : function(s){
38056     
38057     },
38058     /**
38059      * Called before drag operations to get the current size of the resizing element. 
38060      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38061      */
38062      getElementSize : function(s){
38063         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38064             return s.resizingEl.getWidth();
38065         }else{
38066             return s.resizingEl.getHeight();
38067         }
38068     },
38069     
38070     /**
38071      * Called after drag operations to set the size of the resizing element.
38072      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38073      * @param {Number} newSize The new size to set
38074      * @param {Function} onComplete A function to be invoked when resizing is complete
38075      */
38076     setElementSize : function(s, newSize, onComplete){
38077         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38078             if(!s.animate){
38079                 s.resizingEl.setWidth(newSize);
38080                 if(onComplete){
38081                     onComplete(s, newSize);
38082                 }
38083             }else{
38084                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38085             }
38086         }else{
38087             
38088             if(!s.animate){
38089                 s.resizingEl.setHeight(newSize);
38090                 if(onComplete){
38091                     onComplete(s, newSize);
38092                 }
38093             }else{
38094                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38095             }
38096         }
38097     }
38098 };
38099
38100 /** 
38101  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38102  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38103  * Adapter that  moves the splitter element to align with the resized sizing element. 
38104  * Used with an absolute positioned SplitBar.
38105  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38106  * document.body, make sure you assign an id to the body element.
38107  */
38108 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38109     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38110     this.container = Roo.get(container);
38111 };
38112
38113 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38114     init : function(s){
38115         this.basic.init(s);
38116     },
38117     
38118     getElementSize : function(s){
38119         return this.basic.getElementSize(s);
38120     },
38121     
38122     setElementSize : function(s, newSize, onComplete){
38123         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38124     },
38125     
38126     moveSplitter : function(s){
38127         var yes = Roo.bootstrap.SplitBar;
38128         switch(s.placement){
38129             case yes.LEFT:
38130                 s.el.setX(s.resizingEl.getRight());
38131                 break;
38132             case yes.RIGHT:
38133                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38134                 break;
38135             case yes.TOP:
38136                 s.el.setY(s.resizingEl.getBottom());
38137                 break;
38138             case yes.BOTTOM:
38139                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38140                 break;
38141         }
38142     }
38143 };
38144
38145 /**
38146  * Orientation constant - Create a vertical SplitBar
38147  * @static
38148  * @type Number
38149  */
38150 Roo.bootstrap.SplitBar.VERTICAL = 1;
38151
38152 /**
38153  * Orientation constant - Create a horizontal SplitBar
38154  * @static
38155  * @type Number
38156  */
38157 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38158
38159 /**
38160  * Placement constant - The resizing element is to the left of the splitter element
38161  * @static
38162  * @type Number
38163  */
38164 Roo.bootstrap.SplitBar.LEFT = 1;
38165
38166 /**
38167  * Placement constant - The resizing element is to the right of the splitter element
38168  * @static
38169  * @type Number
38170  */
38171 Roo.bootstrap.SplitBar.RIGHT = 2;
38172
38173 /**
38174  * Placement constant - The resizing element is positioned above the splitter element
38175  * @static
38176  * @type Number
38177  */
38178 Roo.bootstrap.SplitBar.TOP = 3;
38179
38180 /**
38181  * Placement constant - The resizing element is positioned under splitter element
38182  * @static
38183  * @type Number
38184  */
38185 Roo.bootstrap.SplitBar.BOTTOM = 4;
38186 Roo.namespace("Roo.bootstrap.layout");/*
38187  * Based on:
38188  * Ext JS Library 1.1.1
38189  * Copyright(c) 2006-2007, Ext JS, LLC.
38190  *
38191  * Originally Released Under LGPL - original licence link has changed is not relivant.
38192  *
38193  * Fork - LGPL
38194  * <script type="text/javascript">
38195  */
38196
38197 /**
38198  * @class Roo.bootstrap.layout.Manager
38199  * @extends Roo.bootstrap.Component
38200  * Base class for layout managers.
38201  */
38202 Roo.bootstrap.layout.Manager = function(config)
38203 {
38204     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38205
38206
38207
38208
38209
38210     /** false to disable window resize monitoring @type Boolean */
38211     this.monitorWindowResize = true;
38212     this.regions = {};
38213     this.addEvents({
38214         /**
38215          * @event layout
38216          * Fires when a layout is performed.
38217          * @param {Roo.LayoutManager} this
38218          */
38219         "layout" : true,
38220         /**
38221          * @event regionresized
38222          * Fires when the user resizes a region.
38223          * @param {Roo.LayoutRegion} region The resized region
38224          * @param {Number} newSize The new size (width for east/west, height for north/south)
38225          */
38226         "regionresized" : true,
38227         /**
38228          * @event regioncollapsed
38229          * Fires when a region is collapsed.
38230          * @param {Roo.LayoutRegion} region The collapsed region
38231          */
38232         "regioncollapsed" : true,
38233         /**
38234          * @event regionexpanded
38235          * Fires when a region is expanded.
38236          * @param {Roo.LayoutRegion} region The expanded region
38237          */
38238         "regionexpanded" : true
38239     });
38240     this.updating = false;
38241
38242     if (config.el) {
38243         this.el = Roo.get(config.el);
38244         this.initEvents();
38245     }
38246
38247 };
38248
38249 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38250
38251
38252     regions : null,
38253
38254     monitorWindowResize : true,
38255
38256
38257     updating : false,
38258
38259
38260     onRender : function(ct, position)
38261     {
38262         if(!this.el){
38263             this.el = Roo.get(ct);
38264             this.initEvents();
38265         }
38266         //this.fireEvent('render',this);
38267     },
38268
38269
38270     initEvents: function()
38271     {
38272
38273
38274         // ie scrollbar fix
38275         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38276             document.body.scroll = "no";
38277         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38278             this.el.position('relative');
38279         }
38280         this.id = this.el.id;
38281         this.el.addClass("roo-layout-container");
38282         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38283         if(this.el.dom != document.body ) {
38284             this.el.on('resize', this.layout,this);
38285             this.el.on('show', this.layout,this);
38286         }
38287
38288     },
38289
38290     /**
38291      * Returns true if this layout is currently being updated
38292      * @return {Boolean}
38293      */
38294     isUpdating : function(){
38295         return this.updating;
38296     },
38297
38298     /**
38299      * Suspend the LayoutManager from doing auto-layouts while
38300      * making multiple add or remove calls
38301      */
38302     beginUpdate : function(){
38303         this.updating = true;
38304     },
38305
38306     /**
38307      * Restore auto-layouts and optionally disable the manager from performing a layout
38308      * @param {Boolean} noLayout true to disable a layout update
38309      */
38310     endUpdate : function(noLayout){
38311         this.updating = false;
38312         if(!noLayout){
38313             this.layout();
38314         }
38315     },
38316
38317     layout: function(){
38318         // abstract...
38319     },
38320
38321     onRegionResized : function(region, newSize){
38322         this.fireEvent("regionresized", region, newSize);
38323         this.layout();
38324     },
38325
38326     onRegionCollapsed : function(region){
38327         this.fireEvent("regioncollapsed", region);
38328     },
38329
38330     onRegionExpanded : function(region){
38331         this.fireEvent("regionexpanded", region);
38332     },
38333
38334     /**
38335      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38336      * performs box-model adjustments.
38337      * @return {Object} The size as an object {width: (the width), height: (the height)}
38338      */
38339     getViewSize : function()
38340     {
38341         var size;
38342         if(this.el.dom != document.body){
38343             size = this.el.getSize();
38344         }else{
38345             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38346         }
38347         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38348         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38349         return size;
38350     },
38351
38352     /**
38353      * Returns the Element this layout is bound to.
38354      * @return {Roo.Element}
38355      */
38356     getEl : function(){
38357         return this.el;
38358     },
38359
38360     /**
38361      * Returns the specified region.
38362      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38363      * @return {Roo.LayoutRegion}
38364      */
38365     getRegion : function(target){
38366         return this.regions[target.toLowerCase()];
38367     },
38368
38369     onWindowResize : function(){
38370         if(this.monitorWindowResize){
38371             this.layout();
38372         }
38373     }
38374 });
38375 /*
38376  * Based on:
38377  * Ext JS Library 1.1.1
38378  * Copyright(c) 2006-2007, Ext JS, LLC.
38379  *
38380  * Originally Released Under LGPL - original licence link has changed is not relivant.
38381  *
38382  * Fork - LGPL
38383  * <script type="text/javascript">
38384  */
38385 /**
38386  * @class Roo.bootstrap.layout.Border
38387  * @extends Roo.bootstrap.layout.Manager
38388  * @builder-top
38389  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38390  * please see: examples/bootstrap/nested.html<br><br>
38391  
38392 <b>The container the layout is rendered into can be either the body element or any other element.
38393 If it is not the body element, the container needs to either be an absolute positioned element,
38394 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38395 the container size if it is not the body element.</b>
38396
38397 * @constructor
38398 * Create a new Border
38399 * @param {Object} config Configuration options
38400  */
38401 Roo.bootstrap.layout.Border = function(config){
38402     config = config || {};
38403     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38404     
38405     
38406     
38407     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38408         if(config[region]){
38409             config[region].region = region;
38410             this.addRegion(config[region]);
38411         }
38412     },this);
38413     
38414 };
38415
38416 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38417
38418 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38419     
38420     parent : false, // this might point to a 'nest' or a ???
38421     
38422     /**
38423      * Creates and adds a new region if it doesn't already exist.
38424      * @param {String} target The target region key (north, south, east, west or center).
38425      * @param {Object} config The regions config object
38426      * @return {BorderLayoutRegion} The new region
38427      */
38428     addRegion : function(config)
38429     {
38430         if(!this.regions[config.region]){
38431             var r = this.factory(config);
38432             this.bindRegion(r);
38433         }
38434         return this.regions[config.region];
38435     },
38436
38437     // private (kinda)
38438     bindRegion : function(r){
38439         this.regions[r.config.region] = r;
38440         
38441         r.on("visibilitychange",    this.layout, this);
38442         r.on("paneladded",          this.layout, this);
38443         r.on("panelremoved",        this.layout, this);
38444         r.on("invalidated",         this.layout, this);
38445         r.on("resized",             this.onRegionResized, this);
38446         r.on("collapsed",           this.onRegionCollapsed, this);
38447         r.on("expanded",            this.onRegionExpanded, this);
38448     },
38449
38450     /**
38451      * Performs a layout update.
38452      */
38453     layout : function()
38454     {
38455         if(this.updating) {
38456             return;
38457         }
38458         
38459         // render all the rebions if they have not been done alreayd?
38460         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38461             if(this.regions[region] && !this.regions[region].bodyEl){
38462                 this.regions[region].onRender(this.el)
38463             }
38464         },this);
38465         
38466         var size = this.getViewSize();
38467         var w = size.width;
38468         var h = size.height;
38469         var centerW = w;
38470         var centerH = h;
38471         var centerY = 0;
38472         var centerX = 0;
38473         //var x = 0, y = 0;
38474
38475         var rs = this.regions;
38476         var north = rs["north"];
38477         var south = rs["south"]; 
38478         var west = rs["west"];
38479         var east = rs["east"];
38480         var center = rs["center"];
38481         //if(this.hideOnLayout){ // not supported anymore
38482             //c.el.setStyle("display", "none");
38483         //}
38484         if(north && north.isVisible()){
38485             var b = north.getBox();
38486             var m = north.getMargins();
38487             b.width = w - (m.left+m.right);
38488             b.x = m.left;
38489             b.y = m.top;
38490             centerY = b.height + b.y + m.bottom;
38491             centerH -= centerY;
38492             north.updateBox(this.safeBox(b));
38493         }
38494         if(south && south.isVisible()){
38495             var b = south.getBox();
38496             var m = south.getMargins();
38497             b.width = w - (m.left+m.right);
38498             b.x = m.left;
38499             var totalHeight = (b.height + m.top + m.bottom);
38500             b.y = h - totalHeight + m.top;
38501             centerH -= totalHeight;
38502             south.updateBox(this.safeBox(b));
38503         }
38504         if(west && west.isVisible()){
38505             var b = west.getBox();
38506             var m = west.getMargins();
38507             b.height = centerH - (m.top+m.bottom);
38508             b.x = m.left;
38509             b.y = centerY + m.top;
38510             var totalWidth = (b.width + m.left + m.right);
38511             centerX += totalWidth;
38512             centerW -= totalWidth;
38513             west.updateBox(this.safeBox(b));
38514         }
38515         if(east && east.isVisible()){
38516             var b = east.getBox();
38517             var m = east.getMargins();
38518             b.height = centerH - (m.top+m.bottom);
38519             var totalWidth = (b.width + m.left + m.right);
38520             b.x = w - totalWidth + m.left;
38521             b.y = centerY + m.top;
38522             centerW -= totalWidth;
38523             east.updateBox(this.safeBox(b));
38524         }
38525         if(center){
38526             var m = center.getMargins();
38527             var centerBox = {
38528                 x: centerX + m.left,
38529                 y: centerY + m.top,
38530                 width: centerW - (m.left+m.right),
38531                 height: centerH - (m.top+m.bottom)
38532             };
38533             //if(this.hideOnLayout){
38534                 //center.el.setStyle("display", "block");
38535             //}
38536             center.updateBox(this.safeBox(centerBox));
38537         }
38538         this.el.repaint();
38539         this.fireEvent("layout", this);
38540     },
38541
38542     // private
38543     safeBox : function(box){
38544         box.width = Math.max(0, box.width);
38545         box.height = Math.max(0, box.height);
38546         return box;
38547     },
38548
38549     /**
38550      * Adds a ContentPanel (or subclass) to this layout.
38551      * @param {String} target The target region key (north, south, east, west or center).
38552      * @param {Roo.ContentPanel} panel The panel to add
38553      * @return {Roo.ContentPanel} The added panel
38554      */
38555     add : function(target, panel){
38556          
38557         target = target.toLowerCase();
38558         return this.regions[target].add(panel);
38559     },
38560
38561     /**
38562      * Remove a ContentPanel (or subclass) to this layout.
38563      * @param {String} target The target region key (north, south, east, west or center).
38564      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38565      * @return {Roo.ContentPanel} The removed panel
38566      */
38567     remove : function(target, panel){
38568         target = target.toLowerCase();
38569         return this.regions[target].remove(panel);
38570     },
38571
38572     /**
38573      * Searches all regions for a panel with the specified id
38574      * @param {String} panelId
38575      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38576      */
38577     findPanel : function(panelId){
38578         var rs = this.regions;
38579         for(var target in rs){
38580             if(typeof rs[target] != "function"){
38581                 var p = rs[target].getPanel(panelId);
38582                 if(p){
38583                     return p;
38584                 }
38585             }
38586         }
38587         return null;
38588     },
38589
38590     /**
38591      * Searches all regions for a panel with the specified id and activates (shows) it.
38592      * @param {String/ContentPanel} panelId The panels id or the panel itself
38593      * @return {Roo.ContentPanel} The shown panel or null
38594      */
38595     showPanel : function(panelId) {
38596       var rs = this.regions;
38597       for(var target in rs){
38598          var r = rs[target];
38599          if(typeof r != "function"){
38600             if(r.hasPanel(panelId)){
38601                return r.showPanel(panelId);
38602             }
38603          }
38604       }
38605       return null;
38606    },
38607
38608    /**
38609      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38610      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38611      */
38612    /*
38613     restoreState : function(provider){
38614         if(!provider){
38615             provider = Roo.state.Manager;
38616         }
38617         var sm = new Roo.LayoutStateManager();
38618         sm.init(this, provider);
38619     },
38620 */
38621  
38622  
38623     /**
38624      * Adds a xtype elements to the layout.
38625      * <pre><code>
38626
38627 layout.addxtype({
38628        xtype : 'ContentPanel',
38629        region: 'west',
38630        items: [ .... ]
38631    }
38632 );
38633
38634 layout.addxtype({
38635         xtype : 'NestedLayoutPanel',
38636         region: 'west',
38637         layout: {
38638            center: { },
38639            west: { }   
38640         },
38641         items : [ ... list of content panels or nested layout panels.. ]
38642    }
38643 );
38644 </code></pre>
38645      * @param {Object} cfg Xtype definition of item to add.
38646      */
38647     addxtype : function(cfg)
38648     {
38649         // basically accepts a pannel...
38650         // can accept a layout region..!?!?
38651         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38652         
38653         
38654         // theory?  children can only be panels??
38655         
38656         //if (!cfg.xtype.match(/Panel$/)) {
38657         //    return false;
38658         //}
38659         var ret = false;
38660         
38661         if (typeof(cfg.region) == 'undefined') {
38662             Roo.log("Failed to add Panel, region was not set");
38663             Roo.log(cfg);
38664             return false;
38665         }
38666         var region = cfg.region;
38667         delete cfg.region;
38668         
38669           
38670         var xitems = [];
38671         if (cfg.items) {
38672             xitems = cfg.items;
38673             delete cfg.items;
38674         }
38675         var nb = false;
38676         
38677         if ( region == 'center') {
38678             Roo.log("Center: " + cfg.title);
38679         }
38680         
38681         
38682         switch(cfg.xtype) 
38683         {
38684             case 'Content':  // ContentPanel (el, cfg)
38685             case 'Scroll':  // ContentPanel (el, cfg)
38686             case 'View': 
38687                 cfg.autoCreate = cfg.autoCreate || true;
38688                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38689                 //} else {
38690                 //    var el = this.el.createChild();
38691                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38692                 //}
38693                 
38694                 this.add(region, ret);
38695                 break;
38696             
38697             /*
38698             case 'TreePanel': // our new panel!
38699                 cfg.el = this.el.createChild();
38700                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38701                 this.add(region, ret);
38702                 break;
38703             */
38704             
38705             case 'Nest': 
38706                 // create a new Layout (which is  a Border Layout...
38707                 
38708                 var clayout = cfg.layout;
38709                 clayout.el  = this.el.createChild();
38710                 clayout.items   = clayout.items  || [];
38711                 
38712                 delete cfg.layout;
38713                 
38714                 // replace this exitems with the clayout ones..
38715                 xitems = clayout.items;
38716                  
38717                 // force background off if it's in center...
38718                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38719                     cfg.background = false;
38720                 }
38721                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38722                 
38723                 
38724                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38725                 //console.log('adding nested layout panel '  + cfg.toSource());
38726                 this.add(region, ret);
38727                 nb = {}; /// find first...
38728                 break;
38729             
38730             case 'Grid':
38731                 
38732                 // needs grid and region
38733                 
38734                 //var el = this.getRegion(region).el.createChild();
38735                 /*
38736                  *var el = this.el.createChild();
38737                 // create the grid first...
38738                 cfg.grid.container = el;
38739                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38740                 */
38741                 
38742                 if (region == 'center' && this.active ) {
38743                     cfg.background = false;
38744                 }
38745                 
38746                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38747                 
38748                 this.add(region, ret);
38749                 /*
38750                 if (cfg.background) {
38751                     // render grid on panel activation (if panel background)
38752                     ret.on('activate', function(gp) {
38753                         if (!gp.grid.rendered) {
38754                     //        gp.grid.render(el);
38755                         }
38756                     });
38757                 } else {
38758                   //  cfg.grid.render(el);
38759                 }
38760                 */
38761                 break;
38762            
38763            
38764             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38765                 // it was the old xcomponent building that caused this before.
38766                 // espeically if border is the top element in the tree.
38767                 ret = this;
38768                 break; 
38769                 
38770                     
38771                 
38772                 
38773                 
38774             default:
38775                 /*
38776                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38777                     
38778                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38779                     this.add(region, ret);
38780                 } else {
38781                 */
38782                     Roo.log(cfg);
38783                     throw "Can not add '" + cfg.xtype + "' to Border";
38784                     return null;
38785              
38786                                 
38787              
38788         }
38789         this.beginUpdate();
38790         // add children..
38791         var region = '';
38792         var abn = {};
38793         Roo.each(xitems, function(i)  {
38794             region = nb && i.region ? i.region : false;
38795             
38796             var add = ret.addxtype(i);
38797            
38798             if (region) {
38799                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38800                 if (!i.background) {
38801                     abn[region] = nb[region] ;
38802                 }
38803             }
38804             
38805         });
38806         this.endUpdate();
38807
38808         // make the last non-background panel active..
38809         //if (nb) { Roo.log(abn); }
38810         if (nb) {
38811             
38812             for(var r in abn) {
38813                 region = this.getRegion(r);
38814                 if (region) {
38815                     // tried using nb[r], but it does not work..
38816                      
38817                     region.showPanel(abn[r]);
38818                    
38819                 }
38820             }
38821         }
38822         return ret;
38823         
38824     },
38825     
38826     
38827 // private
38828     factory : function(cfg)
38829     {
38830         
38831         var validRegions = Roo.bootstrap.layout.Border.regions;
38832
38833         var target = cfg.region;
38834         cfg.mgr = this;
38835         
38836         var r = Roo.bootstrap.layout;
38837         Roo.log(target);
38838         switch(target){
38839             case "north":
38840                 return new r.North(cfg);
38841             case "south":
38842                 return new r.South(cfg);
38843             case "east":
38844                 return new r.East(cfg);
38845             case "west":
38846                 return new r.West(cfg);
38847             case "center":
38848                 return new r.Center(cfg);
38849         }
38850         throw 'Layout region "'+target+'" not supported.';
38851     }
38852     
38853     
38854 });
38855  /*
38856  * Based on:
38857  * Ext JS Library 1.1.1
38858  * Copyright(c) 2006-2007, Ext JS, LLC.
38859  *
38860  * Originally Released Under LGPL - original licence link has changed is not relivant.
38861  *
38862  * Fork - LGPL
38863  * <script type="text/javascript">
38864  */
38865  
38866 /**
38867  * @class Roo.bootstrap.layout.Basic
38868  * @extends Roo.util.Observable
38869  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38870  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38871  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38872  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38873  * @cfg {string}   region  the region that it inhabits..
38874  * @cfg {bool}   skipConfig skip config?
38875  * 
38876
38877  */
38878 Roo.bootstrap.layout.Basic = function(config){
38879     
38880     this.mgr = config.mgr;
38881     
38882     this.position = config.region;
38883     
38884     var skipConfig = config.skipConfig;
38885     
38886     this.events = {
38887         /**
38888          * @scope Roo.BasicLayoutRegion
38889          */
38890         
38891         /**
38892          * @event beforeremove
38893          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38894          * @param {Roo.LayoutRegion} this
38895          * @param {Roo.ContentPanel} panel The panel
38896          * @param {Object} e The cancel event object
38897          */
38898         "beforeremove" : true,
38899         /**
38900          * @event invalidated
38901          * Fires when the layout for this region is changed.
38902          * @param {Roo.LayoutRegion} this
38903          */
38904         "invalidated" : true,
38905         /**
38906          * @event visibilitychange
38907          * Fires when this region is shown or hidden 
38908          * @param {Roo.LayoutRegion} this
38909          * @param {Boolean} visibility true or false
38910          */
38911         "visibilitychange" : true,
38912         /**
38913          * @event paneladded
38914          * Fires when a panel is added. 
38915          * @param {Roo.LayoutRegion} this
38916          * @param {Roo.ContentPanel} panel The panel
38917          */
38918         "paneladded" : true,
38919         /**
38920          * @event panelremoved
38921          * Fires when a panel is removed. 
38922          * @param {Roo.LayoutRegion} this
38923          * @param {Roo.ContentPanel} panel The panel
38924          */
38925         "panelremoved" : true,
38926         /**
38927          * @event beforecollapse
38928          * Fires when this region before collapse.
38929          * @param {Roo.LayoutRegion} this
38930          */
38931         "beforecollapse" : true,
38932         /**
38933          * @event collapsed
38934          * Fires when this region is collapsed.
38935          * @param {Roo.LayoutRegion} this
38936          */
38937         "collapsed" : true,
38938         /**
38939          * @event expanded
38940          * Fires when this region is expanded.
38941          * @param {Roo.LayoutRegion} this
38942          */
38943         "expanded" : true,
38944         /**
38945          * @event slideshow
38946          * Fires when this region is slid into view.
38947          * @param {Roo.LayoutRegion} this
38948          */
38949         "slideshow" : true,
38950         /**
38951          * @event slidehide
38952          * Fires when this region slides out of view. 
38953          * @param {Roo.LayoutRegion} this
38954          */
38955         "slidehide" : true,
38956         /**
38957          * @event panelactivated
38958          * Fires when a panel is activated. 
38959          * @param {Roo.LayoutRegion} this
38960          * @param {Roo.ContentPanel} panel The activated panel
38961          */
38962         "panelactivated" : true,
38963         /**
38964          * @event resized
38965          * Fires when the user resizes this region. 
38966          * @param {Roo.LayoutRegion} this
38967          * @param {Number} newSize The new size (width for east/west, height for north/south)
38968          */
38969         "resized" : true
38970     };
38971     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38972     this.panels = new Roo.util.MixedCollection();
38973     this.panels.getKey = this.getPanelId.createDelegate(this);
38974     this.box = null;
38975     this.activePanel = null;
38976     // ensure listeners are added...
38977     
38978     if (config.listeners || config.events) {
38979         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38980             listeners : config.listeners || {},
38981             events : config.events || {}
38982         });
38983     }
38984     
38985     if(skipConfig !== true){
38986         this.applyConfig(config);
38987     }
38988 };
38989
38990 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38991 {
38992     getPanelId : function(p){
38993         return p.getId();
38994     },
38995     
38996     applyConfig : function(config){
38997         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38998         this.config = config;
38999         
39000     },
39001     
39002     /**
39003      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39004      * the width, for horizontal (north, south) the height.
39005      * @param {Number} newSize The new width or height
39006      */
39007     resizeTo : function(newSize){
39008         var el = this.el ? this.el :
39009                  (this.activePanel ? this.activePanel.getEl() : null);
39010         if(el){
39011             switch(this.position){
39012                 case "east":
39013                 case "west":
39014                     el.setWidth(newSize);
39015                     this.fireEvent("resized", this, newSize);
39016                 break;
39017                 case "north":
39018                 case "south":
39019                     el.setHeight(newSize);
39020                     this.fireEvent("resized", this, newSize);
39021                 break;                
39022             }
39023         }
39024     },
39025     
39026     getBox : function(){
39027         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39028     },
39029     
39030     getMargins : function(){
39031         return this.margins;
39032     },
39033     
39034     updateBox : function(box){
39035         this.box = box;
39036         var el = this.activePanel.getEl();
39037         el.dom.style.left = box.x + "px";
39038         el.dom.style.top = box.y + "px";
39039         this.activePanel.setSize(box.width, box.height);
39040     },
39041     
39042     /**
39043      * Returns the container element for this region.
39044      * @return {Roo.Element}
39045      */
39046     getEl : function(){
39047         return this.activePanel;
39048     },
39049     
39050     /**
39051      * Returns true if this region is currently visible.
39052      * @return {Boolean}
39053      */
39054     isVisible : function(){
39055         return this.activePanel ? true : false;
39056     },
39057     
39058     setActivePanel : function(panel){
39059         panel = this.getPanel(panel);
39060         if(this.activePanel && this.activePanel != panel){
39061             this.activePanel.setActiveState(false);
39062             this.activePanel.getEl().setLeftTop(-10000,-10000);
39063         }
39064         this.activePanel = panel;
39065         panel.setActiveState(true);
39066         if(this.box){
39067             panel.setSize(this.box.width, this.box.height);
39068         }
39069         this.fireEvent("panelactivated", this, panel);
39070         this.fireEvent("invalidated");
39071     },
39072     
39073     /**
39074      * Show the specified panel.
39075      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39076      * @return {Roo.ContentPanel} The shown panel or null
39077      */
39078     showPanel : function(panel){
39079         panel = this.getPanel(panel);
39080         if(panel){
39081             this.setActivePanel(panel);
39082         }
39083         return panel;
39084     },
39085     
39086     /**
39087      * Get the active panel for this region.
39088      * @return {Roo.ContentPanel} The active panel or null
39089      */
39090     getActivePanel : function(){
39091         return this.activePanel;
39092     },
39093     
39094     /**
39095      * Add the passed ContentPanel(s)
39096      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39097      * @return {Roo.ContentPanel} The panel added (if only one was added)
39098      */
39099     add : function(panel){
39100         if(arguments.length > 1){
39101             for(var i = 0, len = arguments.length; i < len; i++) {
39102                 this.add(arguments[i]);
39103             }
39104             return null;
39105         }
39106         if(this.hasPanel(panel)){
39107             this.showPanel(panel);
39108             return panel;
39109         }
39110         var el = panel.getEl();
39111         if(el.dom.parentNode != this.mgr.el.dom){
39112             this.mgr.el.dom.appendChild(el.dom);
39113         }
39114         if(panel.setRegion){
39115             panel.setRegion(this);
39116         }
39117         this.panels.add(panel);
39118         el.setStyle("position", "absolute");
39119         if(!panel.background){
39120             this.setActivePanel(panel);
39121             if(this.config.initialSize && this.panels.getCount()==1){
39122                 this.resizeTo(this.config.initialSize);
39123             }
39124         }
39125         this.fireEvent("paneladded", this, panel);
39126         return panel;
39127     },
39128     
39129     /**
39130      * Returns true if the panel is in this region.
39131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39132      * @return {Boolean}
39133      */
39134     hasPanel : function(panel){
39135         if(typeof panel == "object"){ // must be panel obj
39136             panel = panel.getId();
39137         }
39138         return this.getPanel(panel) ? true : false;
39139     },
39140     
39141     /**
39142      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39143      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39144      * @param {Boolean} preservePanel Overrides the config preservePanel option
39145      * @return {Roo.ContentPanel} The panel that was removed
39146      */
39147     remove : function(panel, preservePanel){
39148         panel = this.getPanel(panel);
39149         if(!panel){
39150             return null;
39151         }
39152         var e = {};
39153         this.fireEvent("beforeremove", this, panel, e);
39154         if(e.cancel === true){
39155             return null;
39156         }
39157         var panelId = panel.getId();
39158         this.panels.removeKey(panelId);
39159         return panel;
39160     },
39161     
39162     /**
39163      * Returns the panel specified or null if it's not in this region.
39164      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39165      * @return {Roo.ContentPanel}
39166      */
39167     getPanel : function(id){
39168         if(typeof id == "object"){ // must be panel obj
39169             return id;
39170         }
39171         return this.panels.get(id);
39172     },
39173     
39174     /**
39175      * Returns this regions position (north/south/east/west/center).
39176      * @return {String} 
39177      */
39178     getPosition: function(){
39179         return this.position;    
39180     }
39181 });/*
39182  * Based on:
39183  * Ext JS Library 1.1.1
39184  * Copyright(c) 2006-2007, Ext JS, LLC.
39185  *
39186  * Originally Released Under LGPL - original licence link has changed is not relivant.
39187  *
39188  * Fork - LGPL
39189  * <script type="text/javascript">
39190  */
39191  
39192 /**
39193  * @class Roo.bootstrap.layout.Region
39194  * @extends Roo.bootstrap.layout.Basic
39195  * This class represents a region in a layout manager.
39196  
39197  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39198  * @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})
39199  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39200  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39201  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39202  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39203  * @cfg {String}    title           The title for the region (overrides panel titles)
39204  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39205  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39206  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39207  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39208  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39209  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39210  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39211  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39212  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39213  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39214
39215  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39216  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39217  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39218  * @cfg {Number}    width           For East/West panels
39219  * @cfg {Number}    height          For North/South panels
39220  * @cfg {Boolean}   split           To show the splitter
39221  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39222  * 
39223  * @cfg {string}   cls             Extra CSS classes to add to region
39224  * 
39225  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39226  * @cfg {string}   region  the region that it inhabits..
39227  *
39228
39229  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39230  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39231
39232  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39233  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39234  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39235  */
39236 Roo.bootstrap.layout.Region = function(config)
39237 {
39238     this.applyConfig(config);
39239
39240     var mgr = config.mgr;
39241     var pos = config.region;
39242     config.skipConfig = true;
39243     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39244     
39245     if (mgr.el) {
39246         this.onRender(mgr.el);   
39247     }
39248      
39249     this.visible = true;
39250     this.collapsed = false;
39251     this.unrendered_panels = [];
39252 };
39253
39254 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39255
39256     position: '', // set by wrapper (eg. north/south etc..)
39257     unrendered_panels : null,  // unrendered panels.
39258     
39259     tabPosition : false,
39260     
39261     mgr: false, // points to 'Border'
39262     
39263     
39264     createBody : function(){
39265         /** This region's body element 
39266         * @type Roo.Element */
39267         this.bodyEl = this.el.createChild({
39268                 tag: "div",
39269                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39270         });
39271     },
39272
39273     onRender: function(ctr, pos)
39274     {
39275         var dh = Roo.DomHelper;
39276         /** This region's container element 
39277         * @type Roo.Element */
39278         this.el = dh.append(ctr.dom, {
39279                 tag: "div",
39280                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39281             }, true);
39282         /** This region's title element 
39283         * @type Roo.Element */
39284     
39285         this.titleEl = dh.append(this.el.dom,  {
39286                 tag: "div",
39287                 unselectable: "on",
39288                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39289                 children:[
39290                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39291                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39292                 ]
39293             }, true);
39294         
39295         this.titleEl.enableDisplayMode();
39296         /** This region's title text element 
39297         * @type HTMLElement */
39298         this.titleTextEl = this.titleEl.dom.firstChild;
39299         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39300         /*
39301         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39302         this.closeBtn.enableDisplayMode();
39303         this.closeBtn.on("click", this.closeClicked, this);
39304         this.closeBtn.hide();
39305     */
39306         this.createBody(this.config);
39307         if(this.config.hideWhenEmpty){
39308             this.hide();
39309             this.on("paneladded", this.validateVisibility, this);
39310             this.on("panelremoved", this.validateVisibility, this);
39311         }
39312         if(this.autoScroll){
39313             this.bodyEl.setStyle("overflow", "auto");
39314         }else{
39315             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39316         }
39317         //if(c.titlebar !== false){
39318             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39319                 this.titleEl.hide();
39320             }else{
39321                 this.titleEl.show();
39322                 if(this.config.title){
39323                     this.titleTextEl.innerHTML = this.config.title;
39324                 }
39325             }
39326         //}
39327         if(this.config.collapsed){
39328             this.collapse(true);
39329         }
39330         if(this.config.hidden){
39331             this.hide();
39332         }
39333         
39334         if (this.unrendered_panels && this.unrendered_panels.length) {
39335             for (var i =0;i< this.unrendered_panels.length; i++) {
39336                 this.add(this.unrendered_panels[i]);
39337             }
39338             this.unrendered_panels = null;
39339             
39340         }
39341         
39342     },
39343     
39344     applyConfig : function(c)
39345     {
39346         /*
39347          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39348             var dh = Roo.DomHelper;
39349             if(c.titlebar !== false){
39350                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39351                 this.collapseBtn.on("click", this.collapse, this);
39352                 this.collapseBtn.enableDisplayMode();
39353                 /*
39354                 if(c.showPin === true || this.showPin){
39355                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39356                     this.stickBtn.enableDisplayMode();
39357                     this.stickBtn.on("click", this.expand, this);
39358                     this.stickBtn.hide();
39359                 }
39360                 
39361             }
39362             */
39363             /** This region's collapsed element
39364             * @type Roo.Element */
39365             /*
39366              *
39367             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39368                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39369             ]}, true);
39370             
39371             if(c.floatable !== false){
39372                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39373                this.collapsedEl.on("click", this.collapseClick, this);
39374             }
39375
39376             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39377                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39378                    id: "message", unselectable: "on", style:{"float":"left"}});
39379                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39380              }
39381             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39382             this.expandBtn.on("click", this.expand, this);
39383             
39384         }
39385         
39386         if(this.collapseBtn){
39387             this.collapseBtn.setVisible(c.collapsible == true);
39388         }
39389         
39390         this.cmargins = c.cmargins || this.cmargins ||
39391                          (this.position == "west" || this.position == "east" ?
39392                              {top: 0, left: 2, right:2, bottom: 0} :
39393                              {top: 2, left: 0, right:0, bottom: 2});
39394         */
39395         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39396         
39397         
39398         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39399         
39400         this.autoScroll = c.autoScroll || false;
39401         
39402         
39403        
39404         
39405         this.duration = c.duration || .30;
39406         this.slideDuration = c.slideDuration || .45;
39407         this.config = c;
39408        
39409     },
39410     /**
39411      * Returns true if this region is currently visible.
39412      * @return {Boolean}
39413      */
39414     isVisible : function(){
39415         return this.visible;
39416     },
39417
39418     /**
39419      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39420      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39421      */
39422     //setCollapsedTitle : function(title){
39423     //    title = title || "&#160;";
39424      //   if(this.collapsedTitleTextEl){
39425       //      this.collapsedTitleTextEl.innerHTML = title;
39426        // }
39427     //},
39428
39429     getBox : function(){
39430         var b;
39431       //  if(!this.collapsed){
39432             b = this.el.getBox(false, true);
39433        // }else{
39434           //  b = this.collapsedEl.getBox(false, true);
39435         //}
39436         return b;
39437     },
39438
39439     getMargins : function(){
39440         return this.margins;
39441         //return this.collapsed ? this.cmargins : this.margins;
39442     },
39443 /*
39444     highlight : function(){
39445         this.el.addClass("x-layout-panel-dragover");
39446     },
39447
39448     unhighlight : function(){
39449         this.el.removeClass("x-layout-panel-dragover");
39450     },
39451 */
39452     updateBox : function(box)
39453     {
39454         if (!this.bodyEl) {
39455             return; // not rendered yet..
39456         }
39457         
39458         this.box = box;
39459         if(!this.collapsed){
39460             this.el.dom.style.left = box.x + "px";
39461             this.el.dom.style.top = box.y + "px";
39462             this.updateBody(box.width, box.height);
39463         }else{
39464             this.collapsedEl.dom.style.left = box.x + "px";
39465             this.collapsedEl.dom.style.top = box.y + "px";
39466             this.collapsedEl.setSize(box.width, box.height);
39467         }
39468         if(this.tabs){
39469             this.tabs.autoSizeTabs();
39470         }
39471     },
39472
39473     updateBody : function(w, h)
39474     {
39475         if(w !== null){
39476             this.el.setWidth(w);
39477             w -= this.el.getBorderWidth("rl");
39478             if(this.config.adjustments){
39479                 w += this.config.adjustments[0];
39480             }
39481         }
39482         if(h !== null && h > 0){
39483             this.el.setHeight(h);
39484             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39485             h -= this.el.getBorderWidth("tb");
39486             if(this.config.adjustments){
39487                 h += this.config.adjustments[1];
39488             }
39489             this.bodyEl.setHeight(h);
39490             if(this.tabs){
39491                 h = this.tabs.syncHeight(h);
39492             }
39493         }
39494         if(this.panelSize){
39495             w = w !== null ? w : this.panelSize.width;
39496             h = h !== null ? h : this.panelSize.height;
39497         }
39498         if(this.activePanel){
39499             var el = this.activePanel.getEl();
39500             w = w !== null ? w : el.getWidth();
39501             h = h !== null ? h : el.getHeight();
39502             this.panelSize = {width: w, height: h};
39503             this.activePanel.setSize(w, h);
39504         }
39505         if(Roo.isIE && this.tabs){
39506             this.tabs.el.repaint();
39507         }
39508     },
39509
39510     /**
39511      * Returns the container element for this region.
39512      * @return {Roo.Element}
39513      */
39514     getEl : function(){
39515         return this.el;
39516     },
39517
39518     /**
39519      * Hides this region.
39520      */
39521     hide : function(){
39522         //if(!this.collapsed){
39523             this.el.dom.style.left = "-2000px";
39524             this.el.hide();
39525         //}else{
39526          //   this.collapsedEl.dom.style.left = "-2000px";
39527          //   this.collapsedEl.hide();
39528        // }
39529         this.visible = false;
39530         this.fireEvent("visibilitychange", this, false);
39531     },
39532
39533     /**
39534      * Shows this region if it was previously hidden.
39535      */
39536     show : function(){
39537         //if(!this.collapsed){
39538             this.el.show();
39539         //}else{
39540         //    this.collapsedEl.show();
39541        // }
39542         this.visible = true;
39543         this.fireEvent("visibilitychange", this, true);
39544     },
39545 /*
39546     closeClicked : function(){
39547         if(this.activePanel){
39548             this.remove(this.activePanel);
39549         }
39550     },
39551
39552     collapseClick : function(e){
39553         if(this.isSlid){
39554            e.stopPropagation();
39555            this.slideIn();
39556         }else{
39557            e.stopPropagation();
39558            this.slideOut();
39559         }
39560     },
39561 */
39562     /**
39563      * Collapses this region.
39564      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39565      */
39566     /*
39567     collapse : function(skipAnim, skipCheck = false){
39568         if(this.collapsed) {
39569             return;
39570         }
39571         
39572         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39573             
39574             this.collapsed = true;
39575             if(this.split){
39576                 this.split.el.hide();
39577             }
39578             if(this.config.animate && skipAnim !== true){
39579                 this.fireEvent("invalidated", this);
39580                 this.animateCollapse();
39581             }else{
39582                 this.el.setLocation(-20000,-20000);
39583                 this.el.hide();
39584                 this.collapsedEl.show();
39585                 this.fireEvent("collapsed", this);
39586                 this.fireEvent("invalidated", this);
39587             }
39588         }
39589         
39590     },
39591 */
39592     animateCollapse : function(){
39593         // overridden
39594     },
39595
39596     /**
39597      * Expands this region if it was previously collapsed.
39598      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39599      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39600      */
39601     /*
39602     expand : function(e, skipAnim){
39603         if(e) {
39604             e.stopPropagation();
39605         }
39606         if(!this.collapsed || this.el.hasActiveFx()) {
39607             return;
39608         }
39609         if(this.isSlid){
39610             this.afterSlideIn();
39611             skipAnim = true;
39612         }
39613         this.collapsed = false;
39614         if(this.config.animate && skipAnim !== true){
39615             this.animateExpand();
39616         }else{
39617             this.el.show();
39618             if(this.split){
39619                 this.split.el.show();
39620             }
39621             this.collapsedEl.setLocation(-2000,-2000);
39622             this.collapsedEl.hide();
39623             this.fireEvent("invalidated", this);
39624             this.fireEvent("expanded", this);
39625         }
39626     },
39627 */
39628     animateExpand : function(){
39629         // overridden
39630     },
39631
39632     initTabs : function()
39633     {
39634         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39635         
39636         var ts = new Roo.bootstrap.panel.Tabs({
39637             el: this.bodyEl.dom,
39638             region : this,
39639             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39640             disableTooltips: this.config.disableTabTips,
39641             toolbar : this.config.toolbar
39642         });
39643         
39644         if(this.config.hideTabs){
39645             ts.stripWrap.setDisplayed(false);
39646         }
39647         this.tabs = ts;
39648         ts.resizeTabs = this.config.resizeTabs === true;
39649         ts.minTabWidth = this.config.minTabWidth || 40;
39650         ts.maxTabWidth = this.config.maxTabWidth || 250;
39651         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39652         ts.monitorResize = false;
39653         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39654         ts.bodyEl.addClass('roo-layout-tabs-body');
39655         this.panels.each(this.initPanelAsTab, this);
39656     },
39657
39658     initPanelAsTab : function(panel){
39659         var ti = this.tabs.addTab(
39660             panel.getEl().id,
39661             panel.getTitle(),
39662             null,
39663             this.config.closeOnTab && panel.isClosable(),
39664             panel.tpl
39665         );
39666         if(panel.tabTip !== undefined){
39667             ti.setTooltip(panel.tabTip);
39668         }
39669         ti.on("activate", function(){
39670               this.setActivePanel(panel);
39671         }, this);
39672         
39673         if(this.config.closeOnTab){
39674             ti.on("beforeclose", function(t, e){
39675                 e.cancel = true;
39676                 this.remove(panel);
39677             }, this);
39678         }
39679         
39680         panel.tabItem = ti;
39681         
39682         return ti;
39683     },
39684
39685     updatePanelTitle : function(panel, title)
39686     {
39687         if(this.activePanel == panel){
39688             this.updateTitle(title);
39689         }
39690         if(this.tabs){
39691             var ti = this.tabs.getTab(panel.getEl().id);
39692             ti.setText(title);
39693             if(panel.tabTip !== undefined){
39694                 ti.setTooltip(panel.tabTip);
39695             }
39696         }
39697     },
39698
39699     updateTitle : function(title){
39700         if(this.titleTextEl && !this.config.title){
39701             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39702         }
39703     },
39704
39705     setActivePanel : function(panel)
39706     {
39707         panel = this.getPanel(panel);
39708         if(this.activePanel && this.activePanel != panel){
39709             if(this.activePanel.setActiveState(false) === false){
39710                 return;
39711             }
39712         }
39713         this.activePanel = panel;
39714         panel.setActiveState(true);
39715         if(this.panelSize){
39716             panel.setSize(this.panelSize.width, this.panelSize.height);
39717         }
39718         if(this.closeBtn){
39719             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39720         }
39721         this.updateTitle(panel.getTitle());
39722         if(this.tabs){
39723             this.fireEvent("invalidated", this);
39724         }
39725         this.fireEvent("panelactivated", this, panel);
39726     },
39727
39728     /**
39729      * Shows the specified panel.
39730      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39731      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39732      */
39733     showPanel : function(panel)
39734     {
39735         panel = this.getPanel(panel);
39736         if(panel){
39737             if(this.tabs){
39738                 var tab = this.tabs.getTab(panel.getEl().id);
39739                 if(tab.isHidden()){
39740                     this.tabs.unhideTab(tab.id);
39741                 }
39742                 tab.activate();
39743             }else{
39744                 this.setActivePanel(panel);
39745             }
39746         }
39747         return panel;
39748     },
39749
39750     /**
39751      * Get the active panel for this region.
39752      * @return {Roo.ContentPanel} The active panel or null
39753      */
39754     getActivePanel : function(){
39755         return this.activePanel;
39756     },
39757
39758     validateVisibility : function(){
39759         if(this.panels.getCount() < 1){
39760             this.updateTitle("&#160;");
39761             this.closeBtn.hide();
39762             this.hide();
39763         }else{
39764             if(!this.isVisible()){
39765                 this.show();
39766             }
39767         }
39768     },
39769
39770     /**
39771      * Adds the passed ContentPanel(s) to this region.
39772      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39773      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39774      */
39775     add : function(panel)
39776     {
39777         if(arguments.length > 1){
39778             for(var i = 0, len = arguments.length; i < len; i++) {
39779                 this.add(arguments[i]);
39780             }
39781             return null;
39782         }
39783         
39784         // if we have not been rendered yet, then we can not really do much of this..
39785         if (!this.bodyEl) {
39786             this.unrendered_panels.push(panel);
39787             return panel;
39788         }
39789         
39790         
39791         
39792         
39793         if(this.hasPanel(panel)){
39794             this.showPanel(panel);
39795             return panel;
39796         }
39797         panel.setRegion(this);
39798         this.panels.add(panel);
39799        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39800             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39801             // and hide them... ???
39802             this.bodyEl.dom.appendChild(panel.getEl().dom);
39803             if(panel.background !== true){
39804                 this.setActivePanel(panel);
39805             }
39806             this.fireEvent("paneladded", this, panel);
39807             return panel;
39808         }
39809         */
39810         if(!this.tabs){
39811             this.initTabs();
39812         }else{
39813             this.initPanelAsTab(panel);
39814         }
39815         
39816         
39817         if(panel.background !== true){
39818             this.tabs.activate(panel.getEl().id);
39819         }
39820         this.fireEvent("paneladded", this, panel);
39821         return panel;
39822     },
39823
39824     /**
39825      * Hides the tab for the specified panel.
39826      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39827      */
39828     hidePanel : function(panel){
39829         if(this.tabs && (panel = this.getPanel(panel))){
39830             this.tabs.hideTab(panel.getEl().id);
39831         }
39832     },
39833
39834     /**
39835      * Unhides the tab for a previously hidden panel.
39836      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39837      */
39838     unhidePanel : function(panel){
39839         if(this.tabs && (panel = this.getPanel(panel))){
39840             this.tabs.unhideTab(panel.getEl().id);
39841         }
39842     },
39843
39844     clearPanels : function(){
39845         while(this.panels.getCount() > 0){
39846              this.remove(this.panels.first());
39847         }
39848     },
39849
39850     /**
39851      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39852      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39853      * @param {Boolean} preservePanel Overrides the config preservePanel option
39854      * @return {Roo.ContentPanel} The panel that was removed
39855      */
39856     remove : function(panel, preservePanel)
39857     {
39858         panel = this.getPanel(panel);
39859         if(!panel){
39860             return null;
39861         }
39862         var e = {};
39863         this.fireEvent("beforeremove", this, panel, e);
39864         if(e.cancel === true){
39865             return null;
39866         }
39867         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39868         var panelId = panel.getId();
39869         this.panels.removeKey(panelId);
39870         if(preservePanel){
39871             document.body.appendChild(panel.getEl().dom);
39872         }
39873         if(this.tabs){
39874             this.tabs.removeTab(panel.getEl().id);
39875         }else if (!preservePanel){
39876             this.bodyEl.dom.removeChild(panel.getEl().dom);
39877         }
39878         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39879             var p = this.panels.first();
39880             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39881             tempEl.appendChild(p.getEl().dom);
39882             this.bodyEl.update("");
39883             this.bodyEl.dom.appendChild(p.getEl().dom);
39884             tempEl = null;
39885             this.updateTitle(p.getTitle());
39886             this.tabs = null;
39887             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39888             this.setActivePanel(p);
39889         }
39890         panel.setRegion(null);
39891         if(this.activePanel == panel){
39892             this.activePanel = null;
39893         }
39894         if(this.config.autoDestroy !== false && preservePanel !== true){
39895             try{panel.destroy();}catch(e){}
39896         }
39897         this.fireEvent("panelremoved", this, panel);
39898         return panel;
39899     },
39900
39901     /**
39902      * Returns the TabPanel component used by this region
39903      * @return {Roo.TabPanel}
39904      */
39905     getTabs : function(){
39906         return this.tabs;
39907     },
39908
39909     createTool : function(parentEl, className){
39910         var btn = Roo.DomHelper.append(parentEl, {
39911             tag: "div",
39912             cls: "x-layout-tools-button",
39913             children: [ {
39914                 tag: "div",
39915                 cls: "roo-layout-tools-button-inner " + className,
39916                 html: "&#160;"
39917             }]
39918         }, true);
39919         btn.addClassOnOver("roo-layout-tools-button-over");
39920         return btn;
39921     }
39922 });/*
39923  * Based on:
39924  * Ext JS Library 1.1.1
39925  * Copyright(c) 2006-2007, Ext JS, LLC.
39926  *
39927  * Originally Released Under LGPL - original licence link has changed is not relivant.
39928  *
39929  * Fork - LGPL
39930  * <script type="text/javascript">
39931  */
39932  
39933
39934
39935 /**
39936  * @class Roo.SplitLayoutRegion
39937  * @extends Roo.LayoutRegion
39938  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39939  */
39940 Roo.bootstrap.layout.Split = function(config){
39941     this.cursor = config.cursor;
39942     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39943 };
39944
39945 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39946 {
39947     splitTip : "Drag to resize.",
39948     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39949     useSplitTips : false,
39950
39951     applyConfig : function(config){
39952         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39953     },
39954     
39955     onRender : function(ctr,pos) {
39956         
39957         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39958         if(!this.config.split){
39959             return;
39960         }
39961         if(!this.split){
39962             
39963             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39964                             tag: "div",
39965                             id: this.el.id + "-split",
39966                             cls: "roo-layout-split roo-layout-split-"+this.position,
39967                             html: "&#160;"
39968             });
39969             /** The SplitBar for this region 
39970             * @type Roo.SplitBar */
39971             // does not exist yet...
39972             Roo.log([this.position, this.orientation]);
39973             
39974             this.split = new Roo.bootstrap.SplitBar({
39975                 dragElement : splitEl,
39976                 resizingElement: this.el,
39977                 orientation : this.orientation
39978             });
39979             
39980             this.split.on("moved", this.onSplitMove, this);
39981             this.split.useShim = this.config.useShim === true;
39982             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39983             if(this.useSplitTips){
39984                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39985             }
39986             //if(config.collapsible){
39987             //    this.split.el.on("dblclick", this.collapse,  this);
39988             //}
39989         }
39990         if(typeof this.config.minSize != "undefined"){
39991             this.split.minSize = this.config.minSize;
39992         }
39993         if(typeof this.config.maxSize != "undefined"){
39994             this.split.maxSize = this.config.maxSize;
39995         }
39996         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39997             this.hideSplitter();
39998         }
39999         
40000     },
40001
40002     getHMaxSize : function(){
40003          var cmax = this.config.maxSize || 10000;
40004          var center = this.mgr.getRegion("center");
40005          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40006     },
40007
40008     getVMaxSize : function(){
40009          var cmax = this.config.maxSize || 10000;
40010          var center = this.mgr.getRegion("center");
40011          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40012     },
40013
40014     onSplitMove : function(split, newSize){
40015         this.fireEvent("resized", this, newSize);
40016     },
40017     
40018     /** 
40019      * Returns the {@link Roo.SplitBar} for this region.
40020      * @return {Roo.SplitBar}
40021      */
40022     getSplitBar : function(){
40023         return this.split;
40024     },
40025     
40026     hide : function(){
40027         this.hideSplitter();
40028         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40029     },
40030
40031     hideSplitter : function(){
40032         if(this.split){
40033             this.split.el.setLocation(-2000,-2000);
40034             this.split.el.hide();
40035         }
40036     },
40037
40038     show : function(){
40039         if(this.split){
40040             this.split.el.show();
40041         }
40042         Roo.bootstrap.layout.Split.superclass.show.call(this);
40043     },
40044     
40045     beforeSlide: function(){
40046         if(Roo.isGecko){// firefox overflow auto bug workaround
40047             this.bodyEl.clip();
40048             if(this.tabs) {
40049                 this.tabs.bodyEl.clip();
40050             }
40051             if(this.activePanel){
40052                 this.activePanel.getEl().clip();
40053                 
40054                 if(this.activePanel.beforeSlide){
40055                     this.activePanel.beforeSlide();
40056                 }
40057             }
40058         }
40059     },
40060     
40061     afterSlide : function(){
40062         if(Roo.isGecko){// firefox overflow auto bug workaround
40063             this.bodyEl.unclip();
40064             if(this.tabs) {
40065                 this.tabs.bodyEl.unclip();
40066             }
40067             if(this.activePanel){
40068                 this.activePanel.getEl().unclip();
40069                 if(this.activePanel.afterSlide){
40070                     this.activePanel.afterSlide();
40071                 }
40072             }
40073         }
40074     },
40075
40076     initAutoHide : function(){
40077         if(this.autoHide !== false){
40078             if(!this.autoHideHd){
40079                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40080                 this.autoHideHd = {
40081                     "mouseout": function(e){
40082                         if(!e.within(this.el, true)){
40083                             st.delay(500);
40084                         }
40085                     },
40086                     "mouseover" : function(e){
40087                         st.cancel();
40088                     },
40089                     scope : this
40090                 };
40091             }
40092             this.el.on(this.autoHideHd);
40093         }
40094     },
40095
40096     clearAutoHide : function(){
40097         if(this.autoHide !== false){
40098             this.el.un("mouseout", this.autoHideHd.mouseout);
40099             this.el.un("mouseover", this.autoHideHd.mouseover);
40100         }
40101     },
40102
40103     clearMonitor : function(){
40104         Roo.get(document).un("click", this.slideInIf, this);
40105     },
40106
40107     // these names are backwards but not changed for compat
40108     slideOut : function(){
40109         if(this.isSlid || this.el.hasActiveFx()){
40110             return;
40111         }
40112         this.isSlid = true;
40113         if(this.collapseBtn){
40114             this.collapseBtn.hide();
40115         }
40116         this.closeBtnState = this.closeBtn.getStyle('display');
40117         this.closeBtn.hide();
40118         if(this.stickBtn){
40119             this.stickBtn.show();
40120         }
40121         this.el.show();
40122         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40123         this.beforeSlide();
40124         this.el.setStyle("z-index", 10001);
40125         this.el.slideIn(this.getSlideAnchor(), {
40126             callback: function(){
40127                 this.afterSlide();
40128                 this.initAutoHide();
40129                 Roo.get(document).on("click", this.slideInIf, this);
40130                 this.fireEvent("slideshow", this);
40131             },
40132             scope: this,
40133             block: true
40134         });
40135     },
40136
40137     afterSlideIn : function(){
40138         this.clearAutoHide();
40139         this.isSlid = false;
40140         this.clearMonitor();
40141         this.el.setStyle("z-index", "");
40142         if(this.collapseBtn){
40143             this.collapseBtn.show();
40144         }
40145         this.closeBtn.setStyle('display', this.closeBtnState);
40146         if(this.stickBtn){
40147             this.stickBtn.hide();
40148         }
40149         this.fireEvent("slidehide", this);
40150     },
40151
40152     slideIn : function(cb){
40153         if(!this.isSlid || this.el.hasActiveFx()){
40154             Roo.callback(cb);
40155             return;
40156         }
40157         this.isSlid = false;
40158         this.beforeSlide();
40159         this.el.slideOut(this.getSlideAnchor(), {
40160             callback: function(){
40161                 this.el.setLeftTop(-10000, -10000);
40162                 this.afterSlide();
40163                 this.afterSlideIn();
40164                 Roo.callback(cb);
40165             },
40166             scope: this,
40167             block: true
40168         });
40169     },
40170     
40171     slideInIf : function(e){
40172         if(!e.within(this.el)){
40173             this.slideIn();
40174         }
40175     },
40176
40177     animateCollapse : function(){
40178         this.beforeSlide();
40179         this.el.setStyle("z-index", 20000);
40180         var anchor = this.getSlideAnchor();
40181         this.el.slideOut(anchor, {
40182             callback : function(){
40183                 this.el.setStyle("z-index", "");
40184                 this.collapsedEl.slideIn(anchor, {duration:.3});
40185                 this.afterSlide();
40186                 this.el.setLocation(-10000,-10000);
40187                 this.el.hide();
40188                 this.fireEvent("collapsed", this);
40189             },
40190             scope: this,
40191             block: true
40192         });
40193     },
40194
40195     animateExpand : function(){
40196         this.beforeSlide();
40197         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40198         this.el.setStyle("z-index", 20000);
40199         this.collapsedEl.hide({
40200             duration:.1
40201         });
40202         this.el.slideIn(this.getSlideAnchor(), {
40203             callback : function(){
40204                 this.el.setStyle("z-index", "");
40205                 this.afterSlide();
40206                 if(this.split){
40207                     this.split.el.show();
40208                 }
40209                 this.fireEvent("invalidated", this);
40210                 this.fireEvent("expanded", this);
40211             },
40212             scope: this,
40213             block: true
40214         });
40215     },
40216
40217     anchors : {
40218         "west" : "left",
40219         "east" : "right",
40220         "north" : "top",
40221         "south" : "bottom"
40222     },
40223
40224     sanchors : {
40225         "west" : "l",
40226         "east" : "r",
40227         "north" : "t",
40228         "south" : "b"
40229     },
40230
40231     canchors : {
40232         "west" : "tl-tr",
40233         "east" : "tr-tl",
40234         "north" : "tl-bl",
40235         "south" : "bl-tl"
40236     },
40237
40238     getAnchor : function(){
40239         return this.anchors[this.position];
40240     },
40241
40242     getCollapseAnchor : function(){
40243         return this.canchors[this.position];
40244     },
40245
40246     getSlideAnchor : function(){
40247         return this.sanchors[this.position];
40248     },
40249
40250     getAlignAdj : function(){
40251         var cm = this.cmargins;
40252         switch(this.position){
40253             case "west":
40254                 return [0, 0];
40255             break;
40256             case "east":
40257                 return [0, 0];
40258             break;
40259             case "north":
40260                 return [0, 0];
40261             break;
40262             case "south":
40263                 return [0, 0];
40264             break;
40265         }
40266     },
40267
40268     getExpandAdj : function(){
40269         var c = this.collapsedEl, cm = this.cmargins;
40270         switch(this.position){
40271             case "west":
40272                 return [-(cm.right+c.getWidth()+cm.left), 0];
40273             break;
40274             case "east":
40275                 return [cm.right+c.getWidth()+cm.left, 0];
40276             break;
40277             case "north":
40278                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40279             break;
40280             case "south":
40281                 return [0, cm.top+cm.bottom+c.getHeight()];
40282             break;
40283         }
40284     }
40285 });/*
40286  * Based on:
40287  * Ext JS Library 1.1.1
40288  * Copyright(c) 2006-2007, Ext JS, LLC.
40289  *
40290  * Originally Released Under LGPL - original licence link has changed is not relivant.
40291  *
40292  * Fork - LGPL
40293  * <script type="text/javascript">
40294  */
40295 /*
40296  * These classes are private internal classes
40297  */
40298 Roo.bootstrap.layout.Center = function(config){
40299     config.region = "center";
40300     Roo.bootstrap.layout.Region.call(this, config);
40301     this.visible = true;
40302     this.minWidth = config.minWidth || 20;
40303     this.minHeight = config.minHeight || 20;
40304 };
40305
40306 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40307     hide : function(){
40308         // center panel can't be hidden
40309     },
40310     
40311     show : function(){
40312         // center panel can't be hidden
40313     },
40314     
40315     getMinWidth: function(){
40316         return this.minWidth;
40317     },
40318     
40319     getMinHeight: function(){
40320         return this.minHeight;
40321     }
40322 });
40323
40324
40325
40326
40327  
40328
40329
40330
40331
40332
40333
40334 Roo.bootstrap.layout.North = function(config)
40335 {
40336     config.region = 'north';
40337     config.cursor = 'n-resize';
40338     
40339     Roo.bootstrap.layout.Split.call(this, config);
40340     
40341     
40342     if(this.split){
40343         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40344         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40345         this.split.el.addClass("roo-layout-split-v");
40346     }
40347     //var size = config.initialSize || config.height;
40348     //if(this.el && typeof size != "undefined"){
40349     //    this.el.setHeight(size);
40350     //}
40351 };
40352 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40353 {
40354     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40355      
40356      
40357     onRender : function(ctr, pos)
40358     {
40359         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40360         var size = this.config.initialSize || this.config.height;
40361         if(this.el && typeof size != "undefined"){
40362             this.el.setHeight(size);
40363         }
40364     
40365     },
40366     
40367     getBox : function(){
40368         if(this.collapsed){
40369             return this.collapsedEl.getBox();
40370         }
40371         var box = this.el.getBox();
40372         if(this.split){
40373             box.height += this.split.el.getHeight();
40374         }
40375         return box;
40376     },
40377     
40378     updateBox : function(box){
40379         if(this.split && !this.collapsed){
40380             box.height -= this.split.el.getHeight();
40381             this.split.el.setLeft(box.x);
40382             this.split.el.setTop(box.y+box.height);
40383             this.split.el.setWidth(box.width);
40384         }
40385         if(this.collapsed){
40386             this.updateBody(box.width, null);
40387         }
40388         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40389     }
40390 });
40391
40392
40393
40394
40395
40396 Roo.bootstrap.layout.South = function(config){
40397     config.region = 'south';
40398     config.cursor = 's-resize';
40399     Roo.bootstrap.layout.Split.call(this, config);
40400     if(this.split){
40401         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40402         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40403         this.split.el.addClass("roo-layout-split-v");
40404     }
40405     
40406 };
40407
40408 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40409     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40410     
40411     onRender : function(ctr, pos)
40412     {
40413         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40414         var size = this.config.initialSize || this.config.height;
40415         if(this.el && typeof size != "undefined"){
40416             this.el.setHeight(size);
40417         }
40418     
40419     },
40420     
40421     getBox : function(){
40422         if(this.collapsed){
40423             return this.collapsedEl.getBox();
40424         }
40425         var box = this.el.getBox();
40426         if(this.split){
40427             var sh = this.split.el.getHeight();
40428             box.height += sh;
40429             box.y -= sh;
40430         }
40431         return box;
40432     },
40433     
40434     updateBox : function(box){
40435         if(this.split && !this.collapsed){
40436             var sh = this.split.el.getHeight();
40437             box.height -= sh;
40438             box.y += sh;
40439             this.split.el.setLeft(box.x);
40440             this.split.el.setTop(box.y-sh);
40441             this.split.el.setWidth(box.width);
40442         }
40443         if(this.collapsed){
40444             this.updateBody(box.width, null);
40445         }
40446         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40447     }
40448 });
40449
40450 Roo.bootstrap.layout.East = function(config){
40451     config.region = "east";
40452     config.cursor = "e-resize";
40453     Roo.bootstrap.layout.Split.call(this, config);
40454     if(this.split){
40455         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40456         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40457         this.split.el.addClass("roo-layout-split-h");
40458     }
40459     
40460 };
40461 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40462     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40463     
40464     onRender : function(ctr, pos)
40465     {
40466         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40467         var size = this.config.initialSize || this.config.width;
40468         if(this.el && typeof size != "undefined"){
40469             this.el.setWidth(size);
40470         }
40471     
40472     },
40473     
40474     getBox : function(){
40475         if(this.collapsed){
40476             return this.collapsedEl.getBox();
40477         }
40478         var box = this.el.getBox();
40479         if(this.split){
40480             var sw = this.split.el.getWidth();
40481             box.width += sw;
40482             box.x -= sw;
40483         }
40484         return box;
40485     },
40486
40487     updateBox : function(box){
40488         if(this.split && !this.collapsed){
40489             var sw = this.split.el.getWidth();
40490             box.width -= sw;
40491             this.split.el.setLeft(box.x);
40492             this.split.el.setTop(box.y);
40493             this.split.el.setHeight(box.height);
40494             box.x += sw;
40495         }
40496         if(this.collapsed){
40497             this.updateBody(null, box.height);
40498         }
40499         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40500     }
40501 });
40502
40503 Roo.bootstrap.layout.West = function(config){
40504     config.region = "west";
40505     config.cursor = "w-resize";
40506     
40507     Roo.bootstrap.layout.Split.call(this, config);
40508     if(this.split){
40509         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40510         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40511         this.split.el.addClass("roo-layout-split-h");
40512     }
40513     
40514 };
40515 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40516     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40517     
40518     onRender: function(ctr, pos)
40519     {
40520         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40521         var size = this.config.initialSize || this.config.width;
40522         if(typeof size != "undefined"){
40523             this.el.setWidth(size);
40524         }
40525     },
40526     
40527     getBox : function(){
40528         if(this.collapsed){
40529             return this.collapsedEl.getBox();
40530         }
40531         var box = this.el.getBox();
40532         if (box.width == 0) {
40533             box.width = this.config.width; // kludge?
40534         }
40535         if(this.split){
40536             box.width += this.split.el.getWidth();
40537         }
40538         return box;
40539     },
40540     
40541     updateBox : function(box){
40542         if(this.split && !this.collapsed){
40543             var sw = this.split.el.getWidth();
40544             box.width -= sw;
40545             this.split.el.setLeft(box.x+box.width);
40546             this.split.el.setTop(box.y);
40547             this.split.el.setHeight(box.height);
40548         }
40549         if(this.collapsed){
40550             this.updateBody(null, box.height);
40551         }
40552         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40553     }
40554 });Roo.namespace("Roo.bootstrap.panel");/*
40555  * Based on:
40556  * Ext JS Library 1.1.1
40557  * Copyright(c) 2006-2007, Ext JS, LLC.
40558  *
40559  * Originally Released Under LGPL - original licence link has changed is not relivant.
40560  *
40561  * Fork - LGPL
40562  * <script type="text/javascript">
40563  */
40564 /**
40565  * @class Roo.ContentPanel
40566  * @extends Roo.util.Observable
40567  * @builder-top
40568  * A basic ContentPanel element.
40569  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40570  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40571  * @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
40572  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40573  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40574  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40575  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40576  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40577  * @cfg {String} title          The title for this panel
40578  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40579  * @cfg {String} url            Calls {@link #setUrl} with this value
40580  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40581  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40582  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40583  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40584  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40585  * @cfg {Boolean} badges render the badges
40586  * @cfg {String} cls  extra classes to use  
40587  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40588
40589  * @constructor
40590  * Create a new ContentPanel.
40591  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40592  * @param {String/Object} config A string to set only the title or a config object
40593  * @param {String} content (optional) Set the HTML content for this panel
40594  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40595  */
40596 Roo.bootstrap.panel.Content = function( config){
40597     
40598     this.tpl = config.tpl || false;
40599     
40600     var el = config.el;
40601     var content = config.content;
40602
40603     if(config.autoCreate){ // xtype is available if this is called from factory
40604         el = Roo.id();
40605     }
40606     this.el = Roo.get(el);
40607     if(!this.el && config && config.autoCreate){
40608         if(typeof config.autoCreate == "object"){
40609             if(!config.autoCreate.id){
40610                 config.autoCreate.id = config.id||el;
40611             }
40612             this.el = Roo.DomHelper.append(document.body,
40613                         config.autoCreate, true);
40614         }else{
40615             var elcfg =  {
40616                 tag: "div",
40617                 cls: (config.cls || '') +
40618                     (config.background ? ' bg-' + config.background : '') +
40619                     " roo-layout-inactive-content",
40620                 id: config.id||el
40621             };
40622             if (config.iframe) {
40623                 elcfg.cn = [
40624                     {
40625                         tag : 'iframe',
40626                         style : 'border: 0px',
40627                         src : 'about:blank'
40628                     }
40629                 ];
40630             }
40631               
40632             if (config.html) {
40633                 elcfg.html = config.html;
40634                 
40635             }
40636                         
40637             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40638             if (config.iframe) {
40639                 this.iframeEl = this.el.select('iframe',true).first();
40640             }
40641             
40642         }
40643     } 
40644     this.closable = false;
40645     this.loaded = false;
40646     this.active = false;
40647    
40648       
40649     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40650         
40651         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40652         
40653         this.wrapEl = this.el; //this.el.wrap();
40654         var ti = [];
40655         if (config.toolbar.items) {
40656             ti = config.toolbar.items ;
40657             delete config.toolbar.items ;
40658         }
40659         
40660         var nitems = [];
40661         this.toolbar.render(this.wrapEl, 'before');
40662         for(var i =0;i < ti.length;i++) {
40663           //  Roo.log(['add child', items[i]]);
40664             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40665         }
40666         this.toolbar.items = nitems;
40667         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40668         delete config.toolbar;
40669         
40670     }
40671     /*
40672     // xtype created footer. - not sure if will work as we normally have to render first..
40673     if (this.footer && !this.footer.el && this.footer.xtype) {
40674         if (!this.wrapEl) {
40675             this.wrapEl = this.el.wrap();
40676         }
40677     
40678         this.footer.container = this.wrapEl.createChild();
40679          
40680         this.footer = Roo.factory(this.footer, Roo);
40681         
40682     }
40683     */
40684     
40685      if(typeof config == "string"){
40686         this.title = config;
40687     }else{
40688         Roo.apply(this, config);
40689     }
40690     
40691     if(this.resizeEl){
40692         this.resizeEl = Roo.get(this.resizeEl, true);
40693     }else{
40694         this.resizeEl = this.el;
40695     }
40696     // handle view.xtype
40697     
40698  
40699     
40700     
40701     this.addEvents({
40702         /**
40703          * @event activate
40704          * Fires when this panel is activated. 
40705          * @param {Roo.ContentPanel} this
40706          */
40707         "activate" : true,
40708         /**
40709          * @event deactivate
40710          * Fires when this panel is activated. 
40711          * @param {Roo.ContentPanel} this
40712          */
40713         "deactivate" : true,
40714
40715         /**
40716          * @event resize
40717          * Fires when this panel is resized if fitToFrame is true.
40718          * @param {Roo.ContentPanel} this
40719          * @param {Number} width The width after any component adjustments
40720          * @param {Number} height The height after any component adjustments
40721          */
40722         "resize" : true,
40723         
40724          /**
40725          * @event render
40726          * Fires when this tab is created
40727          * @param {Roo.ContentPanel} this
40728          */
40729         "render" : true,
40730         
40731           /**
40732          * @event scroll
40733          * Fires when this content is scrolled
40734          * @param {Roo.ContentPanel} this
40735          * @param {Event} scrollEvent
40736          */
40737         "scroll" : true
40738         
40739         
40740         
40741     });
40742     
40743
40744     
40745     
40746     if(this.autoScroll && !this.iframe){
40747         this.resizeEl.setStyle("overflow", "auto");
40748         this.resizeEl.on('scroll', this.onScroll, this);
40749     } else {
40750         // fix randome scrolling
40751         //this.el.on('scroll', function() {
40752         //    Roo.log('fix random scolling');
40753         //    this.scrollTo('top',0); 
40754         //});
40755     }
40756     content = content || this.content;
40757     if(content){
40758         this.setContent(content);
40759     }
40760     if(config && config.url){
40761         this.setUrl(this.url, this.params, this.loadOnce);
40762     }
40763     
40764     
40765     
40766     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40767     
40768     if (this.view && typeof(this.view.xtype) != 'undefined') {
40769         this.view.el = this.el.appendChild(document.createElement("div"));
40770         this.view = Roo.factory(this.view); 
40771         this.view.render  &&  this.view.render(false, '');  
40772     }
40773     
40774     
40775     this.fireEvent('render', this);
40776 };
40777
40778 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40779     
40780     cls : '',
40781     background : '',
40782     
40783     tabTip : '',
40784     
40785     iframe : false,
40786     iframeEl : false,
40787     
40788     /* Resize Element - use this to work out scroll etc. */
40789     resizeEl : false,
40790     
40791     setRegion : function(region){
40792         this.region = region;
40793         this.setActiveClass(region && !this.background);
40794     },
40795     
40796     
40797     setActiveClass: function(state)
40798     {
40799         if(state){
40800            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40801            this.el.setStyle('position','relative');
40802         }else{
40803            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40804            this.el.setStyle('position', 'absolute');
40805         } 
40806     },
40807     
40808     /**
40809      * Returns the toolbar for this Panel if one was configured. 
40810      * @return {Roo.Toolbar} 
40811      */
40812     getToolbar : function(){
40813         return this.toolbar;
40814     },
40815     
40816     setActiveState : function(active)
40817     {
40818         this.active = active;
40819         this.setActiveClass(active);
40820         if(!active){
40821             if(this.fireEvent("deactivate", this) === false){
40822                 return false;
40823             }
40824             return true;
40825         }
40826         this.fireEvent("activate", this);
40827         return true;
40828     },
40829     /**
40830      * Updates this panel's element (not for iframe)
40831      * @param {String} content The new content
40832      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40833     */
40834     setContent : function(content, loadScripts){
40835         if (this.iframe) {
40836             return;
40837         }
40838         
40839         this.el.update(content, loadScripts);
40840     },
40841
40842     ignoreResize : function(w, h){
40843         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40844             return true;
40845         }else{
40846             this.lastSize = {width: w, height: h};
40847             return false;
40848         }
40849     },
40850     /**
40851      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40852      * @return {Roo.UpdateManager} The UpdateManager
40853      */
40854     getUpdateManager : function(){
40855         if (this.iframe) {
40856             return false;
40857         }
40858         return this.el.getUpdateManager();
40859     },
40860      /**
40861      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40862      * Does not work with IFRAME contents
40863      * @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:
40864 <pre><code>
40865 panel.load({
40866     url: "your-url.php",
40867     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40868     callback: yourFunction,
40869     scope: yourObject, //(optional scope)
40870     discardUrl: false,
40871     nocache: false,
40872     text: "Loading...",
40873     timeout: 30,
40874     scripts: false
40875 });
40876 </code></pre>
40877      
40878      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40879      * 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.
40880      * @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}
40881      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40882      * @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.
40883      * @return {Roo.ContentPanel} this
40884      */
40885     load : function(){
40886         
40887         if (this.iframe) {
40888             return this;
40889         }
40890         
40891         var um = this.el.getUpdateManager();
40892         um.update.apply(um, arguments);
40893         return this;
40894     },
40895
40896
40897     /**
40898      * 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.
40899      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40900      * @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)
40901      * @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)
40902      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40903      */
40904     setUrl : function(url, params, loadOnce){
40905         if (this.iframe) {
40906             this.iframeEl.dom.src = url;
40907             return false;
40908         }
40909         
40910         if(this.refreshDelegate){
40911             this.removeListener("activate", this.refreshDelegate);
40912         }
40913         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40914         this.on("activate", this.refreshDelegate);
40915         return this.el.getUpdateManager();
40916     },
40917     
40918     _handleRefresh : function(url, params, loadOnce){
40919         if(!loadOnce || !this.loaded){
40920             var updater = this.el.getUpdateManager();
40921             updater.update(url, params, this._setLoaded.createDelegate(this));
40922         }
40923     },
40924     
40925     _setLoaded : function(){
40926         this.loaded = true;
40927     }, 
40928     
40929     /**
40930      * Returns this panel's id
40931      * @return {String} 
40932      */
40933     getId : function(){
40934         return this.el.id;
40935     },
40936     
40937     /** 
40938      * Returns this panel's element - used by regiosn to add.
40939      * @return {Roo.Element} 
40940      */
40941     getEl : function(){
40942         return this.wrapEl || this.el;
40943     },
40944     
40945    
40946     
40947     adjustForComponents : function(width, height)
40948     {
40949         //Roo.log('adjustForComponents ');
40950         if(this.resizeEl != this.el){
40951             width -= this.el.getFrameWidth('lr');
40952             height -= this.el.getFrameWidth('tb');
40953         }
40954         if(this.toolbar){
40955             var te = this.toolbar.getEl();
40956             te.setWidth(width);
40957             height -= te.getHeight();
40958         }
40959         if(this.footer){
40960             var te = this.footer.getEl();
40961             te.setWidth(width);
40962             height -= te.getHeight();
40963         }
40964         
40965         
40966         if(this.adjustments){
40967             width += this.adjustments[0];
40968             height += this.adjustments[1];
40969         }
40970         return {"width": width, "height": height};
40971     },
40972     
40973     setSize : function(width, height){
40974         if(this.fitToFrame && !this.ignoreResize(width, height)){
40975             if(this.fitContainer && this.resizeEl != this.el){
40976                 this.el.setSize(width, height);
40977             }
40978             var size = this.adjustForComponents(width, height);
40979             if (this.iframe) {
40980                 this.iframeEl.setSize(width,height);
40981             }
40982             
40983             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40984             this.fireEvent('resize', this, size.width, size.height);
40985             
40986             
40987         }
40988     },
40989     
40990     /**
40991      * Returns this panel's title
40992      * @return {String} 
40993      */
40994     getTitle : function(){
40995         
40996         if (typeof(this.title) != 'object') {
40997             return this.title;
40998         }
40999         
41000         var t = '';
41001         for (var k in this.title) {
41002             if (!this.title.hasOwnProperty(k)) {
41003                 continue;
41004             }
41005             
41006             if (k.indexOf('-') >= 0) {
41007                 var s = k.split('-');
41008                 for (var i = 0; i<s.length; i++) {
41009                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41010                 }
41011             } else {
41012                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41013             }
41014         }
41015         return t;
41016     },
41017     
41018     /**
41019      * Set this panel's title
41020      * @param {String} title
41021      */
41022     setTitle : function(title){
41023         this.title = title;
41024         if(this.region){
41025             this.region.updatePanelTitle(this, title);
41026         }
41027     },
41028     
41029     /**
41030      * Returns true is this panel was configured to be closable
41031      * @return {Boolean} 
41032      */
41033     isClosable : function(){
41034         return this.closable;
41035     },
41036     
41037     beforeSlide : function(){
41038         this.el.clip();
41039         this.resizeEl.clip();
41040     },
41041     
41042     afterSlide : function(){
41043         this.el.unclip();
41044         this.resizeEl.unclip();
41045     },
41046     
41047     /**
41048      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41049      *   Will fail silently if the {@link #setUrl} method has not been called.
41050      *   This does not activate the panel, just updates its content.
41051      */
41052     refresh : function(){
41053         if(this.refreshDelegate){
41054            this.loaded = false;
41055            this.refreshDelegate();
41056         }
41057     },
41058     
41059     /**
41060      * Destroys this panel
41061      */
41062     destroy : function(){
41063         this.el.removeAllListeners();
41064         var tempEl = document.createElement("span");
41065         tempEl.appendChild(this.el.dom);
41066         tempEl.innerHTML = "";
41067         this.el.remove();
41068         this.el = null;
41069     },
41070     
41071     /**
41072      * form - if the content panel contains a form - this is a reference to it.
41073      * @type {Roo.form.Form}
41074      */
41075     form : false,
41076     /**
41077      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41078      *    This contains a reference to it.
41079      * @type {Roo.View}
41080      */
41081     view : false,
41082     
41083       /**
41084      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41085      * <pre><code>
41086
41087 layout.addxtype({
41088        xtype : 'Form',
41089        items: [ .... ]
41090    }
41091 );
41092
41093 </code></pre>
41094      * @param {Object} cfg Xtype definition of item to add.
41095      */
41096     
41097     
41098     getChildContainer: function () {
41099         return this.getEl();
41100     },
41101     
41102     
41103     onScroll : function(e)
41104     {
41105         this.fireEvent('scroll', this, e);
41106     }
41107     
41108     
41109     /*
41110         var  ret = new Roo.factory(cfg);
41111         return ret;
41112         
41113         
41114         // add form..
41115         if (cfg.xtype.match(/^Form$/)) {
41116             
41117             var el;
41118             //if (this.footer) {
41119             //    el = this.footer.container.insertSibling(false, 'before');
41120             //} else {
41121                 el = this.el.createChild();
41122             //}
41123
41124             this.form = new  Roo.form.Form(cfg);
41125             
41126             
41127             if ( this.form.allItems.length) {
41128                 this.form.render(el.dom);
41129             }
41130             return this.form;
41131         }
41132         // should only have one of theses..
41133         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41134             // views.. should not be just added - used named prop 'view''
41135             
41136             cfg.el = this.el.appendChild(document.createElement("div"));
41137             // factory?
41138             
41139             var ret = new Roo.factory(cfg);
41140              
41141              ret.render && ret.render(false, ''); // render blank..
41142             this.view = ret;
41143             return ret;
41144         }
41145         return false;
41146     }
41147     \*/
41148 });
41149  
41150 /**
41151  * @class Roo.bootstrap.panel.Grid
41152  * @extends Roo.bootstrap.panel.Content
41153  * @constructor
41154  * Create a new GridPanel.
41155  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41156  * @param {Object} config A the config object
41157   
41158  */
41159
41160
41161
41162 Roo.bootstrap.panel.Grid = function(config)
41163 {
41164     
41165       
41166     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41167         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41168
41169     config.el = this.wrapper;
41170     //this.el = this.wrapper;
41171     
41172       if (config.container) {
41173         // ctor'ed from a Border/panel.grid
41174         
41175         
41176         this.wrapper.setStyle("overflow", "hidden");
41177         this.wrapper.addClass('roo-grid-container');
41178
41179     }
41180     
41181     
41182     if(config.toolbar){
41183         var tool_el = this.wrapper.createChild();    
41184         this.toolbar = Roo.factory(config.toolbar);
41185         var ti = [];
41186         if (config.toolbar.items) {
41187             ti = config.toolbar.items ;
41188             delete config.toolbar.items ;
41189         }
41190         
41191         var nitems = [];
41192         this.toolbar.render(tool_el);
41193         for(var i =0;i < ti.length;i++) {
41194           //  Roo.log(['add child', items[i]]);
41195             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41196         }
41197         this.toolbar.items = nitems;
41198         
41199         delete config.toolbar;
41200     }
41201     
41202     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41203     config.grid.scrollBody = true;;
41204     config.grid.monitorWindowResize = false; // turn off autosizing
41205     config.grid.autoHeight = false;
41206     config.grid.autoWidth = false;
41207     
41208     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41209     
41210     if (config.background) {
41211         // render grid on panel activation (if panel background)
41212         this.on('activate', function(gp) {
41213             if (!gp.grid.rendered) {
41214                 gp.grid.render(this.wrapper);
41215                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41216             }
41217         });
41218             
41219     } else {
41220         this.grid.render(this.wrapper);
41221         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41222
41223     }
41224     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41225     // ??? needed ??? config.el = this.wrapper;
41226     
41227     
41228     
41229   
41230     // xtype created footer. - not sure if will work as we normally have to render first..
41231     if (this.footer && !this.footer.el && this.footer.xtype) {
41232         
41233         var ctr = this.grid.getView().getFooterPanel(true);
41234         this.footer.dataSource = this.grid.dataSource;
41235         this.footer = Roo.factory(this.footer, Roo);
41236         this.footer.render(ctr);
41237         
41238     }
41239     
41240     
41241     
41242     
41243      
41244 };
41245
41246 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41247     getId : function(){
41248         return this.grid.id;
41249     },
41250     
41251     /**
41252      * Returns the grid for this panel
41253      * @return {Roo.bootstrap.Table} 
41254      */
41255     getGrid : function(){
41256         return this.grid;    
41257     },
41258     
41259     setSize : function(width, height){
41260         if(!this.ignoreResize(width, height)){
41261             var grid = this.grid;
41262             var size = this.adjustForComponents(width, height);
41263             // tfoot is not a footer?
41264           
41265             
41266             var gridel = grid.getGridEl();
41267             gridel.setSize(size.width, size.height);
41268             
41269             var tbd = grid.getGridEl().select('tbody', true).first();
41270             var thd = grid.getGridEl().select('thead',true).first();
41271             var tbf= grid.getGridEl().select('tfoot', true).first();
41272
41273             if (tbf) {
41274                 size.height -= tbf.getHeight();
41275             }
41276             if (thd) {
41277                 size.height -= thd.getHeight();
41278             }
41279             
41280             tbd.setSize(size.width, size.height );
41281             // this is for the account management tab -seems to work there.
41282             var thd = grid.getGridEl().select('thead',true).first();
41283             //if (tbd) {
41284             //    tbd.setSize(size.width, size.height - thd.getHeight());
41285             //}
41286              
41287             grid.autoSize();
41288         }
41289     },
41290      
41291     
41292     
41293     beforeSlide : function(){
41294         this.grid.getView().scroller.clip();
41295     },
41296     
41297     afterSlide : function(){
41298         this.grid.getView().scroller.unclip();
41299     },
41300     
41301     destroy : function(){
41302         this.grid.destroy();
41303         delete this.grid;
41304         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41305     }
41306 });
41307
41308 /**
41309  * @class Roo.bootstrap.panel.Nest
41310  * @extends Roo.bootstrap.panel.Content
41311  * @constructor
41312  * Create a new Panel, that can contain a layout.Border.
41313  * 
41314  * 
41315  * @param {Roo.BorderLayout} layout The layout for this panel
41316  * @param {String/Object} config A string to set only the title or a config object
41317  */
41318 Roo.bootstrap.panel.Nest = function(config)
41319 {
41320     // construct with only one argument..
41321     /* FIXME - implement nicer consturctors
41322     if (layout.layout) {
41323         config = layout;
41324         layout = config.layout;
41325         delete config.layout;
41326     }
41327     if (layout.xtype && !layout.getEl) {
41328         // then layout needs constructing..
41329         layout = Roo.factory(layout, Roo);
41330     }
41331     */
41332     
41333     config.el =  config.layout.getEl();
41334     
41335     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41336     
41337     config.layout.monitorWindowResize = false; // turn off autosizing
41338     this.layout = config.layout;
41339     this.layout.getEl().addClass("roo-layout-nested-layout");
41340     this.layout.parent = this;
41341     
41342     
41343     
41344     
41345 };
41346
41347 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41348
41349     setSize : function(width, height){
41350         if(!this.ignoreResize(width, height)){
41351             var size = this.adjustForComponents(width, height);
41352             var el = this.layout.getEl();
41353             if (size.height < 1) {
41354                 el.setWidth(size.width);   
41355             } else {
41356                 el.setSize(size.width, size.height);
41357             }
41358             var touch = el.dom.offsetWidth;
41359             this.layout.layout();
41360             // ie requires a double layout on the first pass
41361             if(Roo.isIE && !this.initialized){
41362                 this.initialized = true;
41363                 this.layout.layout();
41364             }
41365         }
41366     },
41367     
41368     // activate all subpanels if not currently active..
41369     
41370     setActiveState : function(active){
41371         this.active = active;
41372         this.setActiveClass(active);
41373         
41374         if(!active){
41375             this.fireEvent("deactivate", this);
41376             return;
41377         }
41378         
41379         this.fireEvent("activate", this);
41380         // not sure if this should happen before or after..
41381         if (!this.layout) {
41382             return; // should not happen..
41383         }
41384         var reg = false;
41385         for (var r in this.layout.regions) {
41386             reg = this.layout.getRegion(r);
41387             if (reg.getActivePanel()) {
41388                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41389                 reg.setActivePanel(reg.getActivePanel());
41390                 continue;
41391             }
41392             if (!reg.panels.length) {
41393                 continue;
41394             }
41395             reg.showPanel(reg.getPanel(0));
41396         }
41397         
41398         
41399         
41400         
41401     },
41402     
41403     /**
41404      * Returns the nested BorderLayout for this panel
41405      * @return {Roo.BorderLayout} 
41406      */
41407     getLayout : function(){
41408         return this.layout;
41409     },
41410     
41411      /**
41412      * Adds a xtype elements to the layout of the nested panel
41413      * <pre><code>
41414
41415 panel.addxtype({
41416        xtype : 'ContentPanel',
41417        region: 'west',
41418        items: [ .... ]
41419    }
41420 );
41421
41422 panel.addxtype({
41423         xtype : 'NestedLayoutPanel',
41424         region: 'west',
41425         layout: {
41426            center: { },
41427            west: { }   
41428         },
41429         items : [ ... list of content panels or nested layout panels.. ]
41430    }
41431 );
41432 </code></pre>
41433      * @param {Object} cfg Xtype definition of item to add.
41434      */
41435     addxtype : function(cfg) {
41436         return this.layout.addxtype(cfg);
41437     
41438     }
41439 });/*
41440  * Based on:
41441  * Ext JS Library 1.1.1
41442  * Copyright(c) 2006-2007, Ext JS, LLC.
41443  *
41444  * Originally Released Under LGPL - original licence link has changed is not relivant.
41445  *
41446  * Fork - LGPL
41447  * <script type="text/javascript">
41448  */
41449 /**
41450  * @class Roo.TabPanel
41451  * @extends Roo.util.Observable
41452  * A lightweight tab container.
41453  * <br><br>
41454  * Usage:
41455  * <pre><code>
41456 // basic tabs 1, built from existing content
41457 var tabs = new Roo.TabPanel("tabs1");
41458 tabs.addTab("script", "View Script");
41459 tabs.addTab("markup", "View Markup");
41460 tabs.activate("script");
41461
41462 // more advanced tabs, built from javascript
41463 var jtabs = new Roo.TabPanel("jtabs");
41464 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41465
41466 // set up the UpdateManager
41467 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41468 var updater = tab2.getUpdateManager();
41469 updater.setDefaultUrl("ajax1.htm");
41470 tab2.on('activate', updater.refresh, updater, true);
41471
41472 // Use setUrl for Ajax loading
41473 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41474 tab3.setUrl("ajax2.htm", null, true);
41475
41476 // Disabled tab
41477 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41478 tab4.disable();
41479
41480 jtabs.activate("jtabs-1");
41481  * </code></pre>
41482  * @constructor
41483  * Create a new TabPanel.
41484  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41485  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41486  */
41487 Roo.bootstrap.panel.Tabs = function(config){
41488     /**
41489     * The container element for this TabPanel.
41490     * @type Roo.Element
41491     */
41492     this.el = Roo.get(config.el);
41493     delete config.el;
41494     if(config){
41495         if(typeof config == "boolean"){
41496             this.tabPosition = config ? "bottom" : "top";
41497         }else{
41498             Roo.apply(this, config);
41499         }
41500     }
41501     
41502     if(this.tabPosition == "bottom"){
41503         // if tabs are at the bottom = create the body first.
41504         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41505         this.el.addClass("roo-tabs-bottom");
41506     }
41507     // next create the tabs holders
41508     
41509     if (this.tabPosition == "west"){
41510         
41511         var reg = this.region; // fake it..
41512         while (reg) {
41513             if (!reg.mgr.parent) {
41514                 break;
41515             }
41516             reg = reg.mgr.parent.region;
41517         }
41518         Roo.log("got nest?");
41519         Roo.log(reg);
41520         if (reg.mgr.getRegion('west')) {
41521             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41522             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41523             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41524             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41525             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41526         
41527             
41528         }
41529         
41530         
41531     } else {
41532      
41533         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41534         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41535         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41536         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41537     }
41538     
41539     
41540     if(Roo.isIE){
41541         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41542     }
41543     
41544     // finally - if tabs are at the top, then create the body last..
41545     if(this.tabPosition != "bottom"){
41546         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41547          * @type Roo.Element
41548          */
41549         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41550         this.el.addClass("roo-tabs-top");
41551     }
41552     this.items = [];
41553
41554     this.bodyEl.setStyle("position", "relative");
41555
41556     this.active = null;
41557     this.activateDelegate = this.activate.createDelegate(this);
41558
41559     this.addEvents({
41560         /**
41561          * @event tabchange
41562          * Fires when the active tab changes
41563          * @param {Roo.TabPanel} this
41564          * @param {Roo.TabPanelItem} activePanel The new active tab
41565          */
41566         "tabchange": true,
41567         /**
41568          * @event beforetabchange
41569          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41570          * @param {Roo.TabPanel} this
41571          * @param {Object} e Set cancel to true on this object to cancel the tab change
41572          * @param {Roo.TabPanelItem} tab The tab being changed to
41573          */
41574         "beforetabchange" : true
41575     });
41576
41577     Roo.EventManager.onWindowResize(this.onResize, this);
41578     this.cpad = this.el.getPadding("lr");
41579     this.hiddenCount = 0;
41580
41581
41582     // toolbar on the tabbar support...
41583     if (this.toolbar) {
41584         alert("no toolbar support yet");
41585         this.toolbar  = false;
41586         /*
41587         var tcfg = this.toolbar;
41588         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41589         this.toolbar = new Roo.Toolbar(tcfg);
41590         if (Roo.isSafari) {
41591             var tbl = tcfg.container.child('table', true);
41592             tbl.setAttribute('width', '100%');
41593         }
41594         */
41595         
41596     }
41597    
41598
41599
41600     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41601 };
41602
41603 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41604     /*
41605      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41606      */
41607     tabPosition : "top",
41608     /*
41609      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41610      */
41611     currentTabWidth : 0,
41612     /*
41613      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41614      */
41615     minTabWidth : 40,
41616     /*
41617      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41618      */
41619     maxTabWidth : 250,
41620     /*
41621      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41622      */
41623     preferredTabWidth : 175,
41624     /*
41625      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41626      */
41627     resizeTabs : false,
41628     /*
41629      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41630      */
41631     monitorResize : true,
41632     /*
41633      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41634      */
41635     toolbar : false,  // set by caller..
41636     
41637     region : false, /// set by caller
41638     
41639     disableTooltips : true, // not used yet...
41640
41641     /**
41642      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41643      * @param {String} id The id of the div to use <b>or create</b>
41644      * @param {String} text The text for the tab
41645      * @param {String} content (optional) Content to put in the TabPanelItem body
41646      * @param {Boolean} closable (optional) True to create a close icon on the tab
41647      * @return {Roo.TabPanelItem} The created TabPanelItem
41648      */
41649     addTab : function(id, text, content, closable, tpl)
41650     {
41651         var item = new Roo.bootstrap.panel.TabItem({
41652             panel: this,
41653             id : id,
41654             text : text,
41655             closable : closable,
41656             tpl : tpl
41657         });
41658         this.addTabItem(item);
41659         if(content){
41660             item.setContent(content);
41661         }
41662         return item;
41663     },
41664
41665     /**
41666      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41667      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41668      * @return {Roo.TabPanelItem}
41669      */
41670     getTab : function(id){
41671         return this.items[id];
41672     },
41673
41674     /**
41675      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41676      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41677      */
41678     hideTab : function(id){
41679         var t = this.items[id];
41680         if(!t.isHidden()){
41681            t.setHidden(true);
41682            this.hiddenCount++;
41683            this.autoSizeTabs();
41684         }
41685     },
41686
41687     /**
41688      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41689      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41690      */
41691     unhideTab : function(id){
41692         var t = this.items[id];
41693         if(t.isHidden()){
41694            t.setHidden(false);
41695            this.hiddenCount--;
41696            this.autoSizeTabs();
41697         }
41698     },
41699
41700     /**
41701      * Adds an existing {@link Roo.TabPanelItem}.
41702      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41703      */
41704     addTabItem : function(item)
41705     {
41706         this.items[item.id] = item;
41707         this.items.push(item);
41708         this.autoSizeTabs();
41709       //  if(this.resizeTabs){
41710     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41711   //         this.autoSizeTabs();
41712 //        }else{
41713 //            item.autoSize();
41714        // }
41715     },
41716
41717     /**
41718      * Removes a {@link Roo.TabPanelItem}.
41719      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41720      */
41721     removeTab : function(id){
41722         var items = this.items;
41723         var tab = items[id];
41724         if(!tab) { return; }
41725         var index = items.indexOf(tab);
41726         if(this.active == tab && items.length > 1){
41727             var newTab = this.getNextAvailable(index);
41728             if(newTab) {
41729                 newTab.activate();
41730             }
41731         }
41732         this.stripEl.dom.removeChild(tab.pnode.dom);
41733         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41734             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41735         }
41736         items.splice(index, 1);
41737         delete this.items[tab.id];
41738         tab.fireEvent("close", tab);
41739         tab.purgeListeners();
41740         this.autoSizeTabs();
41741     },
41742
41743     getNextAvailable : function(start){
41744         var items = this.items;
41745         var index = start;
41746         // look for a next tab that will slide over to
41747         // replace the one being removed
41748         while(index < items.length){
41749             var item = items[++index];
41750             if(item && !item.isHidden()){
41751                 return item;
41752             }
41753         }
41754         // if one isn't found select the previous tab (on the left)
41755         index = start;
41756         while(index >= 0){
41757             var item = items[--index];
41758             if(item && !item.isHidden()){
41759                 return item;
41760             }
41761         }
41762         return null;
41763     },
41764
41765     /**
41766      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41767      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41768      */
41769     disableTab : function(id){
41770         var tab = this.items[id];
41771         if(tab && this.active != tab){
41772             tab.disable();
41773         }
41774     },
41775
41776     /**
41777      * Enables a {@link Roo.TabPanelItem} that is disabled.
41778      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41779      */
41780     enableTab : function(id){
41781         var tab = this.items[id];
41782         tab.enable();
41783     },
41784
41785     /**
41786      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41787      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41788      * @return {Roo.TabPanelItem} The TabPanelItem.
41789      */
41790     activate : function(id)
41791     {
41792         //Roo.log('activite:'  + id);
41793         
41794         var tab = this.items[id];
41795         if(!tab){
41796             return null;
41797         }
41798         if(tab == this.active || tab.disabled){
41799             return tab;
41800         }
41801         var e = {};
41802         this.fireEvent("beforetabchange", this, e, tab);
41803         if(e.cancel !== true && !tab.disabled){
41804             if(this.active){
41805                 this.active.hide();
41806             }
41807             this.active = this.items[id];
41808             this.active.show();
41809             this.fireEvent("tabchange", this, this.active);
41810         }
41811         return tab;
41812     },
41813
41814     /**
41815      * Gets the active {@link Roo.TabPanelItem}.
41816      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41817      */
41818     getActiveTab : function(){
41819         return this.active;
41820     },
41821
41822     /**
41823      * Updates the tab body element to fit the height of the container element
41824      * for overflow scrolling
41825      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41826      */
41827     syncHeight : function(targetHeight){
41828         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41829         var bm = this.bodyEl.getMargins();
41830         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41831         this.bodyEl.setHeight(newHeight);
41832         return newHeight;
41833     },
41834
41835     onResize : function(){
41836         if(this.monitorResize){
41837             this.autoSizeTabs();
41838         }
41839     },
41840
41841     /**
41842      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41843      */
41844     beginUpdate : function(){
41845         this.updating = true;
41846     },
41847
41848     /**
41849      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41850      */
41851     endUpdate : function(){
41852         this.updating = false;
41853         this.autoSizeTabs();
41854     },
41855
41856     /**
41857      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41858      */
41859     autoSizeTabs : function()
41860     {
41861         var count = this.items.length;
41862         var vcount = count - this.hiddenCount;
41863         
41864         if (vcount < 2) {
41865             this.stripEl.hide();
41866         } else {
41867             this.stripEl.show();
41868         }
41869         
41870         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41871             return;
41872         }
41873         
41874         
41875         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41876         var availWidth = Math.floor(w / vcount);
41877         var b = this.stripBody;
41878         if(b.getWidth() > w){
41879             var tabs = this.items;
41880             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41881             if(availWidth < this.minTabWidth){
41882                 /*if(!this.sleft){    // incomplete scrolling code
41883                     this.createScrollButtons();
41884                 }
41885                 this.showScroll();
41886                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41887             }
41888         }else{
41889             if(this.currentTabWidth < this.preferredTabWidth){
41890                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41891             }
41892         }
41893     },
41894
41895     /**
41896      * Returns the number of tabs in this TabPanel.
41897      * @return {Number}
41898      */
41899      getCount : function(){
41900          return this.items.length;
41901      },
41902
41903     /**
41904      * Resizes all the tabs to the passed width
41905      * @param {Number} The new width
41906      */
41907     setTabWidth : function(width){
41908         this.currentTabWidth = width;
41909         for(var i = 0, len = this.items.length; i < len; i++) {
41910                 if(!this.items[i].isHidden()) {
41911                 this.items[i].setWidth(width);
41912             }
41913         }
41914     },
41915
41916     /**
41917      * Destroys this TabPanel
41918      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41919      */
41920     destroy : function(removeEl){
41921         Roo.EventManager.removeResizeListener(this.onResize, this);
41922         for(var i = 0, len = this.items.length; i < len; i++){
41923             this.items[i].purgeListeners();
41924         }
41925         if(removeEl === true){
41926             this.el.update("");
41927             this.el.remove();
41928         }
41929     },
41930     
41931     createStrip : function(container)
41932     {
41933         var strip = document.createElement("nav");
41934         strip.className = Roo.bootstrap.version == 4 ?
41935             "navbar-light bg-light" : 
41936             "navbar navbar-default"; //"x-tabs-wrap";
41937         container.appendChild(strip);
41938         return strip;
41939     },
41940     
41941     createStripList : function(strip)
41942     {
41943         // div wrapper for retard IE
41944         // returns the "tr" element.
41945         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41946         //'<div class="x-tabs-strip-wrap">'+
41947           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41948           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41949         return strip.firstChild; //.firstChild.firstChild.firstChild;
41950     },
41951     createBody : function(container)
41952     {
41953         var body = document.createElement("div");
41954         Roo.id(body, "tab-body");
41955         //Roo.fly(body).addClass("x-tabs-body");
41956         Roo.fly(body).addClass("tab-content");
41957         container.appendChild(body);
41958         return body;
41959     },
41960     createItemBody :function(bodyEl, id){
41961         var body = Roo.getDom(id);
41962         if(!body){
41963             body = document.createElement("div");
41964             body.id = id;
41965         }
41966         //Roo.fly(body).addClass("x-tabs-item-body");
41967         Roo.fly(body).addClass("tab-pane");
41968          bodyEl.insertBefore(body, bodyEl.firstChild);
41969         return body;
41970     },
41971     /** @private */
41972     createStripElements :  function(stripEl, text, closable, tpl)
41973     {
41974         var td = document.createElement("li"); // was td..
41975         td.className = 'nav-item';
41976         
41977         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41978         
41979         
41980         stripEl.appendChild(td);
41981         /*if(closable){
41982             td.className = "x-tabs-closable";
41983             if(!this.closeTpl){
41984                 this.closeTpl = new Roo.Template(
41985                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41986                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41987                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41988                 );
41989             }
41990             var el = this.closeTpl.overwrite(td, {"text": text});
41991             var close = el.getElementsByTagName("div")[0];
41992             var inner = el.getElementsByTagName("em")[0];
41993             return {"el": el, "close": close, "inner": inner};
41994         } else {
41995         */
41996         // not sure what this is..
41997 //            if(!this.tabTpl){
41998                 //this.tabTpl = new Roo.Template(
41999                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42000                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42001                 //);
42002 //                this.tabTpl = new Roo.Template(
42003 //                   '<a href="#">' +
42004 //                   '<span unselectable="on"' +
42005 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42006 //                            ' >{text}</span></a>'
42007 //                );
42008 //                
42009 //            }
42010
42011
42012             var template = tpl || this.tabTpl || false;
42013             
42014             if(!template){
42015                 template =  new Roo.Template(
42016                         Roo.bootstrap.version == 4 ? 
42017                             (
42018                                 '<a class="nav-link" href="#" unselectable="on"' +
42019                                      (this.disableTooltips ? '' : ' title="{text}"') +
42020                                      ' >{text}</a>'
42021                             ) : (
42022                                 '<a class="nav-link" href="#">' +
42023                                 '<span unselectable="on"' +
42024                                          (this.disableTooltips ? '' : ' title="{text}"') +
42025                                     ' >{text}</span></a>'
42026                             )
42027                 );
42028             }
42029             
42030             switch (typeof(template)) {
42031                 case 'object' :
42032                     break;
42033                 case 'string' :
42034                     template = new Roo.Template(template);
42035                     break;
42036                 default :
42037                     break;
42038             }
42039             
42040             var el = template.overwrite(td, {"text": text});
42041             
42042             var inner = el.getElementsByTagName("span")[0];
42043             
42044             return {"el": el, "inner": inner};
42045             
42046     }
42047         
42048     
42049 });
42050
42051 /**
42052  * @class Roo.TabPanelItem
42053  * @extends Roo.util.Observable
42054  * Represents an individual item (tab plus body) in a TabPanel.
42055  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42056  * @param {String} id The id of this TabPanelItem
42057  * @param {String} text The text for the tab of this TabPanelItem
42058  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42059  */
42060 Roo.bootstrap.panel.TabItem = function(config){
42061     /**
42062      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42063      * @type Roo.TabPanel
42064      */
42065     this.tabPanel = config.panel;
42066     /**
42067      * The id for this TabPanelItem
42068      * @type String
42069      */
42070     this.id = config.id;
42071     /** @private */
42072     this.disabled = false;
42073     /** @private */
42074     this.text = config.text;
42075     /** @private */
42076     this.loaded = false;
42077     this.closable = config.closable;
42078
42079     /**
42080      * The body element for this TabPanelItem.
42081      * @type Roo.Element
42082      */
42083     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42084     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42085     this.bodyEl.setStyle("display", "block");
42086     this.bodyEl.setStyle("zoom", "1");
42087     //this.hideAction();
42088
42089     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42090     /** @private */
42091     this.el = Roo.get(els.el);
42092     this.inner = Roo.get(els.inner, true);
42093      this.textEl = Roo.bootstrap.version == 4 ?
42094         this.el : Roo.get(this.el.dom.firstChild, true);
42095
42096     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42097     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42098
42099     
42100 //    this.el.on("mousedown", this.onTabMouseDown, this);
42101     this.el.on("click", this.onTabClick, this);
42102     /** @private */
42103     if(config.closable){
42104         var c = Roo.get(els.close, true);
42105         c.dom.title = this.closeText;
42106         c.addClassOnOver("close-over");
42107         c.on("click", this.closeClick, this);
42108      }
42109
42110     this.addEvents({
42111          /**
42112          * @event activate
42113          * Fires when this tab becomes the active tab.
42114          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42115          * @param {Roo.TabPanelItem} this
42116          */
42117         "activate": true,
42118         /**
42119          * @event beforeclose
42120          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42121          * @param {Roo.TabPanelItem} this
42122          * @param {Object} e Set cancel to true on this object to cancel the close.
42123          */
42124         "beforeclose": true,
42125         /**
42126          * @event close
42127          * Fires when this tab is closed.
42128          * @param {Roo.TabPanelItem} this
42129          */
42130          "close": true,
42131         /**
42132          * @event deactivate
42133          * Fires when this tab is no longer the active tab.
42134          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42135          * @param {Roo.TabPanelItem} this
42136          */
42137          "deactivate" : true
42138     });
42139     this.hidden = false;
42140
42141     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42142 };
42143
42144 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42145            {
42146     purgeListeners : function(){
42147        Roo.util.Observable.prototype.purgeListeners.call(this);
42148        this.el.removeAllListeners();
42149     },
42150     /**
42151      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42152      */
42153     show : function(){
42154         this.status_node.addClass("active");
42155         this.showAction();
42156         if(Roo.isOpera){
42157             this.tabPanel.stripWrap.repaint();
42158         }
42159         this.fireEvent("activate", this.tabPanel, this);
42160     },
42161
42162     /**
42163      * Returns true if this tab is the active tab.
42164      * @return {Boolean}
42165      */
42166     isActive : function(){
42167         return this.tabPanel.getActiveTab() == this;
42168     },
42169
42170     /**
42171      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42172      */
42173     hide : function(){
42174         this.status_node.removeClass("active");
42175         this.hideAction();
42176         this.fireEvent("deactivate", this.tabPanel, this);
42177     },
42178
42179     hideAction : function(){
42180         this.bodyEl.hide();
42181         this.bodyEl.setStyle("position", "absolute");
42182         this.bodyEl.setLeft("-20000px");
42183         this.bodyEl.setTop("-20000px");
42184     },
42185
42186     showAction : function(){
42187         this.bodyEl.setStyle("position", "relative");
42188         this.bodyEl.setTop("");
42189         this.bodyEl.setLeft("");
42190         this.bodyEl.show();
42191     },
42192
42193     /**
42194      * Set the tooltip for the tab.
42195      * @param {String} tooltip The tab's tooltip
42196      */
42197     setTooltip : function(text){
42198         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42199             this.textEl.dom.qtip = text;
42200             this.textEl.dom.removeAttribute('title');
42201         }else{
42202             this.textEl.dom.title = text;
42203         }
42204     },
42205
42206     onTabClick : function(e){
42207         e.preventDefault();
42208         this.tabPanel.activate(this.id);
42209     },
42210
42211     onTabMouseDown : function(e){
42212         e.preventDefault();
42213         this.tabPanel.activate(this.id);
42214     },
42215 /*
42216     getWidth : function(){
42217         return this.inner.getWidth();
42218     },
42219
42220     setWidth : function(width){
42221         var iwidth = width - this.linode.getPadding("lr");
42222         this.inner.setWidth(iwidth);
42223         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42224         this.linode.setWidth(width);
42225     },
42226 */
42227     /**
42228      * Show or hide the tab
42229      * @param {Boolean} hidden True to hide or false to show.
42230      */
42231     setHidden : function(hidden){
42232         this.hidden = hidden;
42233         this.linode.setStyle("display", hidden ? "none" : "");
42234     },
42235
42236     /**
42237      * Returns true if this tab is "hidden"
42238      * @return {Boolean}
42239      */
42240     isHidden : function(){
42241         return this.hidden;
42242     },
42243
42244     /**
42245      * Returns the text for this tab
42246      * @return {String}
42247      */
42248     getText : function(){
42249         return this.text;
42250     },
42251     /*
42252     autoSize : function(){
42253         //this.el.beginMeasure();
42254         this.textEl.setWidth(1);
42255         /*
42256          *  #2804 [new] Tabs in Roojs
42257          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42258          */
42259         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42260         //this.el.endMeasure();
42261     //},
42262
42263     /**
42264      * Sets the text for the tab (Note: this also sets the tooltip text)
42265      * @param {String} text The tab's text and tooltip
42266      */
42267     setText : function(text){
42268         this.text = text;
42269         this.textEl.update(text);
42270         this.setTooltip(text);
42271         //if(!this.tabPanel.resizeTabs){
42272         //    this.autoSize();
42273         //}
42274     },
42275     /**
42276      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42277      */
42278     activate : function(){
42279         this.tabPanel.activate(this.id);
42280     },
42281
42282     /**
42283      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42284      */
42285     disable : function(){
42286         if(this.tabPanel.active != this){
42287             this.disabled = true;
42288             this.status_node.addClass("disabled");
42289         }
42290     },
42291
42292     /**
42293      * Enables this TabPanelItem if it was previously disabled.
42294      */
42295     enable : function(){
42296         this.disabled = false;
42297         this.status_node.removeClass("disabled");
42298     },
42299
42300     /**
42301      * Sets the content for this TabPanelItem.
42302      * @param {String} content The content
42303      * @param {Boolean} loadScripts true to look for and load scripts
42304      */
42305     setContent : function(content, loadScripts){
42306         this.bodyEl.update(content, loadScripts);
42307     },
42308
42309     /**
42310      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42311      * @return {Roo.UpdateManager} The UpdateManager
42312      */
42313     getUpdateManager : function(){
42314         return this.bodyEl.getUpdateManager();
42315     },
42316
42317     /**
42318      * Set a URL to be used to load the content for this TabPanelItem.
42319      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42320      * @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)
42321      * @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)
42322      * @return {Roo.UpdateManager} The UpdateManager
42323      */
42324     setUrl : function(url, params, loadOnce){
42325         if(this.refreshDelegate){
42326             this.un('activate', this.refreshDelegate);
42327         }
42328         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42329         this.on("activate", this.refreshDelegate);
42330         return this.bodyEl.getUpdateManager();
42331     },
42332
42333     /** @private */
42334     _handleRefresh : function(url, params, loadOnce){
42335         if(!loadOnce || !this.loaded){
42336             var updater = this.bodyEl.getUpdateManager();
42337             updater.update(url, params, this._setLoaded.createDelegate(this));
42338         }
42339     },
42340
42341     /**
42342      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42343      *   Will fail silently if the setUrl method has not been called.
42344      *   This does not activate the panel, just updates its content.
42345      */
42346     refresh : function(){
42347         if(this.refreshDelegate){
42348            this.loaded = false;
42349            this.refreshDelegate();
42350         }
42351     },
42352
42353     /** @private */
42354     _setLoaded : function(){
42355         this.loaded = true;
42356     },
42357
42358     /** @private */
42359     closeClick : function(e){
42360         var o = {};
42361         e.stopEvent();
42362         this.fireEvent("beforeclose", this, o);
42363         if(o.cancel !== true){
42364             this.tabPanel.removeTab(this.id);
42365         }
42366     },
42367     /**
42368      * The text displayed in the tooltip for the close icon.
42369      * @type String
42370      */
42371     closeText : "Close this tab"
42372 });
42373 /**
42374 *    This script refer to:
42375 *    Title: International Telephone Input
42376 *    Author: Jack O'Connor
42377 *    Code version:  v12.1.12
42378 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42379 **/
42380
42381 Roo.bootstrap.PhoneInputData = function() {
42382     var d = [
42383       [
42384         "Afghanistan (‫افغانستان‬‎)",
42385         "af",
42386         "93"
42387       ],
42388       [
42389         "Albania (Shqipëri)",
42390         "al",
42391         "355"
42392       ],
42393       [
42394         "Algeria (‫الجزائر‬‎)",
42395         "dz",
42396         "213"
42397       ],
42398       [
42399         "American Samoa",
42400         "as",
42401         "1684"
42402       ],
42403       [
42404         "Andorra",
42405         "ad",
42406         "376"
42407       ],
42408       [
42409         "Angola",
42410         "ao",
42411         "244"
42412       ],
42413       [
42414         "Anguilla",
42415         "ai",
42416         "1264"
42417       ],
42418       [
42419         "Antigua and Barbuda",
42420         "ag",
42421         "1268"
42422       ],
42423       [
42424         "Argentina",
42425         "ar",
42426         "54"
42427       ],
42428       [
42429         "Armenia (Հայաստան)",
42430         "am",
42431         "374"
42432       ],
42433       [
42434         "Aruba",
42435         "aw",
42436         "297"
42437       ],
42438       [
42439         "Australia",
42440         "au",
42441         "61",
42442         0
42443       ],
42444       [
42445         "Austria (Österreich)",
42446         "at",
42447         "43"
42448       ],
42449       [
42450         "Azerbaijan (Azərbaycan)",
42451         "az",
42452         "994"
42453       ],
42454       [
42455         "Bahamas",
42456         "bs",
42457         "1242"
42458       ],
42459       [
42460         "Bahrain (‫البحرين‬‎)",
42461         "bh",
42462         "973"
42463       ],
42464       [
42465         "Bangladesh (বাংলাদেশ)",
42466         "bd",
42467         "880"
42468       ],
42469       [
42470         "Barbados",
42471         "bb",
42472         "1246"
42473       ],
42474       [
42475         "Belarus (Беларусь)",
42476         "by",
42477         "375"
42478       ],
42479       [
42480         "Belgium (België)",
42481         "be",
42482         "32"
42483       ],
42484       [
42485         "Belize",
42486         "bz",
42487         "501"
42488       ],
42489       [
42490         "Benin (Bénin)",
42491         "bj",
42492         "229"
42493       ],
42494       [
42495         "Bermuda",
42496         "bm",
42497         "1441"
42498       ],
42499       [
42500         "Bhutan (འབྲུག)",
42501         "bt",
42502         "975"
42503       ],
42504       [
42505         "Bolivia",
42506         "bo",
42507         "591"
42508       ],
42509       [
42510         "Bosnia and Herzegovina (Босна и Херцеговина)",
42511         "ba",
42512         "387"
42513       ],
42514       [
42515         "Botswana",
42516         "bw",
42517         "267"
42518       ],
42519       [
42520         "Brazil (Brasil)",
42521         "br",
42522         "55"
42523       ],
42524       [
42525         "British Indian Ocean Territory",
42526         "io",
42527         "246"
42528       ],
42529       [
42530         "British Virgin Islands",
42531         "vg",
42532         "1284"
42533       ],
42534       [
42535         "Brunei",
42536         "bn",
42537         "673"
42538       ],
42539       [
42540         "Bulgaria (България)",
42541         "bg",
42542         "359"
42543       ],
42544       [
42545         "Burkina Faso",
42546         "bf",
42547         "226"
42548       ],
42549       [
42550         "Burundi (Uburundi)",
42551         "bi",
42552         "257"
42553       ],
42554       [
42555         "Cambodia (កម្ពុជា)",
42556         "kh",
42557         "855"
42558       ],
42559       [
42560         "Cameroon (Cameroun)",
42561         "cm",
42562         "237"
42563       ],
42564       [
42565         "Canada",
42566         "ca",
42567         "1",
42568         1,
42569         ["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"]
42570       ],
42571       [
42572         "Cape Verde (Kabu Verdi)",
42573         "cv",
42574         "238"
42575       ],
42576       [
42577         "Caribbean Netherlands",
42578         "bq",
42579         "599",
42580         1
42581       ],
42582       [
42583         "Cayman Islands",
42584         "ky",
42585         "1345"
42586       ],
42587       [
42588         "Central African Republic (République centrafricaine)",
42589         "cf",
42590         "236"
42591       ],
42592       [
42593         "Chad (Tchad)",
42594         "td",
42595         "235"
42596       ],
42597       [
42598         "Chile",
42599         "cl",
42600         "56"
42601       ],
42602       [
42603         "China (中国)",
42604         "cn",
42605         "86"
42606       ],
42607       [
42608         "Christmas Island",
42609         "cx",
42610         "61",
42611         2
42612       ],
42613       [
42614         "Cocos (Keeling) Islands",
42615         "cc",
42616         "61",
42617         1
42618       ],
42619       [
42620         "Colombia",
42621         "co",
42622         "57"
42623       ],
42624       [
42625         "Comoros (‫جزر القمر‬‎)",
42626         "km",
42627         "269"
42628       ],
42629       [
42630         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42631         "cd",
42632         "243"
42633       ],
42634       [
42635         "Congo (Republic) (Congo-Brazzaville)",
42636         "cg",
42637         "242"
42638       ],
42639       [
42640         "Cook Islands",
42641         "ck",
42642         "682"
42643       ],
42644       [
42645         "Costa Rica",
42646         "cr",
42647         "506"
42648       ],
42649       [
42650         "Côte d’Ivoire",
42651         "ci",
42652         "225"
42653       ],
42654       [
42655         "Croatia (Hrvatska)",
42656         "hr",
42657         "385"
42658       ],
42659       [
42660         "Cuba",
42661         "cu",
42662         "53"
42663       ],
42664       [
42665         "Curaçao",
42666         "cw",
42667         "599",
42668         0
42669       ],
42670       [
42671         "Cyprus (Κύπρος)",
42672         "cy",
42673         "357"
42674       ],
42675       [
42676         "Czech Republic (Česká republika)",
42677         "cz",
42678         "420"
42679       ],
42680       [
42681         "Denmark (Danmark)",
42682         "dk",
42683         "45"
42684       ],
42685       [
42686         "Djibouti",
42687         "dj",
42688         "253"
42689       ],
42690       [
42691         "Dominica",
42692         "dm",
42693         "1767"
42694       ],
42695       [
42696         "Dominican Republic (República Dominicana)",
42697         "do",
42698         "1",
42699         2,
42700         ["809", "829", "849"]
42701       ],
42702       [
42703         "Ecuador",
42704         "ec",
42705         "593"
42706       ],
42707       [
42708         "Egypt (‫مصر‬‎)",
42709         "eg",
42710         "20"
42711       ],
42712       [
42713         "El Salvador",
42714         "sv",
42715         "503"
42716       ],
42717       [
42718         "Equatorial Guinea (Guinea Ecuatorial)",
42719         "gq",
42720         "240"
42721       ],
42722       [
42723         "Eritrea",
42724         "er",
42725         "291"
42726       ],
42727       [
42728         "Estonia (Eesti)",
42729         "ee",
42730         "372"
42731       ],
42732       [
42733         "Ethiopia",
42734         "et",
42735         "251"
42736       ],
42737       [
42738         "Falkland Islands (Islas Malvinas)",
42739         "fk",
42740         "500"
42741       ],
42742       [
42743         "Faroe Islands (Føroyar)",
42744         "fo",
42745         "298"
42746       ],
42747       [
42748         "Fiji",
42749         "fj",
42750         "679"
42751       ],
42752       [
42753         "Finland (Suomi)",
42754         "fi",
42755         "358",
42756         0
42757       ],
42758       [
42759         "France",
42760         "fr",
42761         "33"
42762       ],
42763       [
42764         "French Guiana (Guyane française)",
42765         "gf",
42766         "594"
42767       ],
42768       [
42769         "French Polynesia (Polynésie française)",
42770         "pf",
42771         "689"
42772       ],
42773       [
42774         "Gabon",
42775         "ga",
42776         "241"
42777       ],
42778       [
42779         "Gambia",
42780         "gm",
42781         "220"
42782       ],
42783       [
42784         "Georgia (საქართველო)",
42785         "ge",
42786         "995"
42787       ],
42788       [
42789         "Germany (Deutschland)",
42790         "de",
42791         "49"
42792       ],
42793       [
42794         "Ghana (Gaana)",
42795         "gh",
42796         "233"
42797       ],
42798       [
42799         "Gibraltar",
42800         "gi",
42801         "350"
42802       ],
42803       [
42804         "Greece (Ελλάδα)",
42805         "gr",
42806         "30"
42807       ],
42808       [
42809         "Greenland (Kalaallit Nunaat)",
42810         "gl",
42811         "299"
42812       ],
42813       [
42814         "Grenada",
42815         "gd",
42816         "1473"
42817       ],
42818       [
42819         "Guadeloupe",
42820         "gp",
42821         "590",
42822         0
42823       ],
42824       [
42825         "Guam",
42826         "gu",
42827         "1671"
42828       ],
42829       [
42830         "Guatemala",
42831         "gt",
42832         "502"
42833       ],
42834       [
42835         "Guernsey",
42836         "gg",
42837         "44",
42838         1
42839       ],
42840       [
42841         "Guinea (Guinée)",
42842         "gn",
42843         "224"
42844       ],
42845       [
42846         "Guinea-Bissau (Guiné Bissau)",
42847         "gw",
42848         "245"
42849       ],
42850       [
42851         "Guyana",
42852         "gy",
42853         "592"
42854       ],
42855       [
42856         "Haiti",
42857         "ht",
42858         "509"
42859       ],
42860       [
42861         "Honduras",
42862         "hn",
42863         "504"
42864       ],
42865       [
42866         "Hong Kong (香港)",
42867         "hk",
42868         "852"
42869       ],
42870       [
42871         "Hungary (Magyarország)",
42872         "hu",
42873         "36"
42874       ],
42875       [
42876         "Iceland (Ísland)",
42877         "is",
42878         "354"
42879       ],
42880       [
42881         "India (भारत)",
42882         "in",
42883         "91"
42884       ],
42885       [
42886         "Indonesia",
42887         "id",
42888         "62"
42889       ],
42890       [
42891         "Iran (‫ایران‬‎)",
42892         "ir",
42893         "98"
42894       ],
42895       [
42896         "Iraq (‫العراق‬‎)",
42897         "iq",
42898         "964"
42899       ],
42900       [
42901         "Ireland",
42902         "ie",
42903         "353"
42904       ],
42905       [
42906         "Isle of Man",
42907         "im",
42908         "44",
42909         2
42910       ],
42911       [
42912         "Israel (‫ישראל‬‎)",
42913         "il",
42914         "972"
42915       ],
42916       [
42917         "Italy (Italia)",
42918         "it",
42919         "39",
42920         0
42921       ],
42922       [
42923         "Jamaica",
42924         "jm",
42925         "1876"
42926       ],
42927       [
42928         "Japan (日本)",
42929         "jp",
42930         "81"
42931       ],
42932       [
42933         "Jersey",
42934         "je",
42935         "44",
42936         3
42937       ],
42938       [
42939         "Jordan (‫الأردن‬‎)",
42940         "jo",
42941         "962"
42942       ],
42943       [
42944         "Kazakhstan (Казахстан)",
42945         "kz",
42946         "7",
42947         1
42948       ],
42949       [
42950         "Kenya",
42951         "ke",
42952         "254"
42953       ],
42954       [
42955         "Kiribati",
42956         "ki",
42957         "686"
42958       ],
42959       [
42960         "Kosovo",
42961         "xk",
42962         "383"
42963       ],
42964       [
42965         "Kuwait (‫الكويت‬‎)",
42966         "kw",
42967         "965"
42968       ],
42969       [
42970         "Kyrgyzstan (Кыргызстан)",
42971         "kg",
42972         "996"
42973       ],
42974       [
42975         "Laos (ລາວ)",
42976         "la",
42977         "856"
42978       ],
42979       [
42980         "Latvia (Latvija)",
42981         "lv",
42982         "371"
42983       ],
42984       [
42985         "Lebanon (‫لبنان‬‎)",
42986         "lb",
42987         "961"
42988       ],
42989       [
42990         "Lesotho",
42991         "ls",
42992         "266"
42993       ],
42994       [
42995         "Liberia",
42996         "lr",
42997         "231"
42998       ],
42999       [
43000         "Libya (‫ليبيا‬‎)",
43001         "ly",
43002         "218"
43003       ],
43004       [
43005         "Liechtenstein",
43006         "li",
43007         "423"
43008       ],
43009       [
43010         "Lithuania (Lietuva)",
43011         "lt",
43012         "370"
43013       ],
43014       [
43015         "Luxembourg",
43016         "lu",
43017         "352"
43018       ],
43019       [
43020         "Macau (澳門)",
43021         "mo",
43022         "853"
43023       ],
43024       [
43025         "Macedonia (FYROM) (Македонија)",
43026         "mk",
43027         "389"
43028       ],
43029       [
43030         "Madagascar (Madagasikara)",
43031         "mg",
43032         "261"
43033       ],
43034       [
43035         "Malawi",
43036         "mw",
43037         "265"
43038       ],
43039       [
43040         "Malaysia",
43041         "my",
43042         "60"
43043       ],
43044       [
43045         "Maldives",
43046         "mv",
43047         "960"
43048       ],
43049       [
43050         "Mali",
43051         "ml",
43052         "223"
43053       ],
43054       [
43055         "Malta",
43056         "mt",
43057         "356"
43058       ],
43059       [
43060         "Marshall Islands",
43061         "mh",
43062         "692"
43063       ],
43064       [
43065         "Martinique",
43066         "mq",
43067         "596"
43068       ],
43069       [
43070         "Mauritania (‫موريتانيا‬‎)",
43071         "mr",
43072         "222"
43073       ],
43074       [
43075         "Mauritius (Moris)",
43076         "mu",
43077         "230"
43078       ],
43079       [
43080         "Mayotte",
43081         "yt",
43082         "262",
43083         1
43084       ],
43085       [
43086         "Mexico (México)",
43087         "mx",
43088         "52"
43089       ],
43090       [
43091         "Micronesia",
43092         "fm",
43093         "691"
43094       ],
43095       [
43096         "Moldova (Republica Moldova)",
43097         "md",
43098         "373"
43099       ],
43100       [
43101         "Monaco",
43102         "mc",
43103         "377"
43104       ],
43105       [
43106         "Mongolia (Монгол)",
43107         "mn",
43108         "976"
43109       ],
43110       [
43111         "Montenegro (Crna Gora)",
43112         "me",
43113         "382"
43114       ],
43115       [
43116         "Montserrat",
43117         "ms",
43118         "1664"
43119       ],
43120       [
43121         "Morocco (‫المغرب‬‎)",
43122         "ma",
43123         "212",
43124         0
43125       ],
43126       [
43127         "Mozambique (Moçambique)",
43128         "mz",
43129         "258"
43130       ],
43131       [
43132         "Myanmar (Burma) (မြန်မာ)",
43133         "mm",
43134         "95"
43135       ],
43136       [
43137         "Namibia (Namibië)",
43138         "na",
43139         "264"
43140       ],
43141       [
43142         "Nauru",
43143         "nr",
43144         "674"
43145       ],
43146       [
43147         "Nepal (नेपाल)",
43148         "np",
43149         "977"
43150       ],
43151       [
43152         "Netherlands (Nederland)",
43153         "nl",
43154         "31"
43155       ],
43156       [
43157         "New Caledonia (Nouvelle-Calédonie)",
43158         "nc",
43159         "687"
43160       ],
43161       [
43162         "New Zealand",
43163         "nz",
43164         "64"
43165       ],
43166       [
43167         "Nicaragua",
43168         "ni",
43169         "505"
43170       ],
43171       [
43172         "Niger (Nijar)",
43173         "ne",
43174         "227"
43175       ],
43176       [
43177         "Nigeria",
43178         "ng",
43179         "234"
43180       ],
43181       [
43182         "Niue",
43183         "nu",
43184         "683"
43185       ],
43186       [
43187         "Norfolk Island",
43188         "nf",
43189         "672"
43190       ],
43191       [
43192         "North Korea (조선 민주주의 인민 공화국)",
43193         "kp",
43194         "850"
43195       ],
43196       [
43197         "Northern Mariana Islands",
43198         "mp",
43199         "1670"
43200       ],
43201       [
43202         "Norway (Norge)",
43203         "no",
43204         "47",
43205         0
43206       ],
43207       [
43208         "Oman (‫عُمان‬‎)",
43209         "om",
43210         "968"
43211       ],
43212       [
43213         "Pakistan (‫پاکستان‬‎)",
43214         "pk",
43215         "92"
43216       ],
43217       [
43218         "Palau",
43219         "pw",
43220         "680"
43221       ],
43222       [
43223         "Palestine (‫فلسطين‬‎)",
43224         "ps",
43225         "970"
43226       ],
43227       [
43228         "Panama (Panamá)",
43229         "pa",
43230         "507"
43231       ],
43232       [
43233         "Papua New Guinea",
43234         "pg",
43235         "675"
43236       ],
43237       [
43238         "Paraguay",
43239         "py",
43240         "595"
43241       ],
43242       [
43243         "Peru (Perú)",
43244         "pe",
43245         "51"
43246       ],
43247       [
43248         "Philippines",
43249         "ph",
43250         "63"
43251       ],
43252       [
43253         "Poland (Polska)",
43254         "pl",
43255         "48"
43256       ],
43257       [
43258         "Portugal",
43259         "pt",
43260         "351"
43261       ],
43262       [
43263         "Puerto Rico",
43264         "pr",
43265         "1",
43266         3,
43267         ["787", "939"]
43268       ],
43269       [
43270         "Qatar (‫قطر‬‎)",
43271         "qa",
43272         "974"
43273       ],
43274       [
43275         "Réunion (La Réunion)",
43276         "re",
43277         "262",
43278         0
43279       ],
43280       [
43281         "Romania (România)",
43282         "ro",
43283         "40"
43284       ],
43285       [
43286         "Russia (Россия)",
43287         "ru",
43288         "7",
43289         0
43290       ],
43291       [
43292         "Rwanda",
43293         "rw",
43294         "250"
43295       ],
43296       [
43297         "Saint Barthélemy",
43298         "bl",
43299         "590",
43300         1
43301       ],
43302       [
43303         "Saint Helena",
43304         "sh",
43305         "290"
43306       ],
43307       [
43308         "Saint Kitts and Nevis",
43309         "kn",
43310         "1869"
43311       ],
43312       [
43313         "Saint Lucia",
43314         "lc",
43315         "1758"
43316       ],
43317       [
43318         "Saint Martin (Saint-Martin (partie française))",
43319         "mf",
43320         "590",
43321         2
43322       ],
43323       [
43324         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43325         "pm",
43326         "508"
43327       ],
43328       [
43329         "Saint Vincent and the Grenadines",
43330         "vc",
43331         "1784"
43332       ],
43333       [
43334         "Samoa",
43335         "ws",
43336         "685"
43337       ],
43338       [
43339         "San Marino",
43340         "sm",
43341         "378"
43342       ],
43343       [
43344         "São Tomé and Príncipe (São Tomé e Príncipe)",
43345         "st",
43346         "239"
43347       ],
43348       [
43349         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43350         "sa",
43351         "966"
43352       ],
43353       [
43354         "Senegal (Sénégal)",
43355         "sn",
43356         "221"
43357       ],
43358       [
43359         "Serbia (Србија)",
43360         "rs",
43361         "381"
43362       ],
43363       [
43364         "Seychelles",
43365         "sc",
43366         "248"
43367       ],
43368       [
43369         "Sierra Leone",
43370         "sl",
43371         "232"
43372       ],
43373       [
43374         "Singapore",
43375         "sg",
43376         "65"
43377       ],
43378       [
43379         "Sint Maarten",
43380         "sx",
43381         "1721"
43382       ],
43383       [
43384         "Slovakia (Slovensko)",
43385         "sk",
43386         "421"
43387       ],
43388       [
43389         "Slovenia (Slovenija)",
43390         "si",
43391         "386"
43392       ],
43393       [
43394         "Solomon Islands",
43395         "sb",
43396         "677"
43397       ],
43398       [
43399         "Somalia (Soomaaliya)",
43400         "so",
43401         "252"
43402       ],
43403       [
43404         "South Africa",
43405         "za",
43406         "27"
43407       ],
43408       [
43409         "South Korea (대한민국)",
43410         "kr",
43411         "82"
43412       ],
43413       [
43414         "South Sudan (‫جنوب السودان‬‎)",
43415         "ss",
43416         "211"
43417       ],
43418       [
43419         "Spain (España)",
43420         "es",
43421         "34"
43422       ],
43423       [
43424         "Sri Lanka (ශ්‍රී ලංකාව)",
43425         "lk",
43426         "94"
43427       ],
43428       [
43429         "Sudan (‫السودان‬‎)",
43430         "sd",
43431         "249"
43432       ],
43433       [
43434         "Suriname",
43435         "sr",
43436         "597"
43437       ],
43438       [
43439         "Svalbard and Jan Mayen",
43440         "sj",
43441         "47",
43442         1
43443       ],
43444       [
43445         "Swaziland",
43446         "sz",
43447         "268"
43448       ],
43449       [
43450         "Sweden (Sverige)",
43451         "se",
43452         "46"
43453       ],
43454       [
43455         "Switzerland (Schweiz)",
43456         "ch",
43457         "41"
43458       ],
43459       [
43460         "Syria (‫سوريا‬‎)",
43461         "sy",
43462         "963"
43463       ],
43464       [
43465         "Taiwan (台灣)",
43466         "tw",
43467         "886"
43468       ],
43469       [
43470         "Tajikistan",
43471         "tj",
43472         "992"
43473       ],
43474       [
43475         "Tanzania",
43476         "tz",
43477         "255"
43478       ],
43479       [
43480         "Thailand (ไทย)",
43481         "th",
43482         "66"
43483       ],
43484       [
43485         "Timor-Leste",
43486         "tl",
43487         "670"
43488       ],
43489       [
43490         "Togo",
43491         "tg",
43492         "228"
43493       ],
43494       [
43495         "Tokelau",
43496         "tk",
43497         "690"
43498       ],
43499       [
43500         "Tonga",
43501         "to",
43502         "676"
43503       ],
43504       [
43505         "Trinidad and Tobago",
43506         "tt",
43507         "1868"
43508       ],
43509       [
43510         "Tunisia (‫تونس‬‎)",
43511         "tn",
43512         "216"
43513       ],
43514       [
43515         "Turkey (Türkiye)",
43516         "tr",
43517         "90"
43518       ],
43519       [
43520         "Turkmenistan",
43521         "tm",
43522         "993"
43523       ],
43524       [
43525         "Turks and Caicos Islands",
43526         "tc",
43527         "1649"
43528       ],
43529       [
43530         "Tuvalu",
43531         "tv",
43532         "688"
43533       ],
43534       [
43535         "U.S. Virgin Islands",
43536         "vi",
43537         "1340"
43538       ],
43539       [
43540         "Uganda",
43541         "ug",
43542         "256"
43543       ],
43544       [
43545         "Ukraine (Україна)",
43546         "ua",
43547         "380"
43548       ],
43549       [
43550         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43551         "ae",
43552         "971"
43553       ],
43554       [
43555         "United Kingdom",
43556         "gb",
43557         "44",
43558         0
43559       ],
43560       [
43561         "United States",
43562         "us",
43563         "1",
43564         0
43565       ],
43566       [
43567         "Uruguay",
43568         "uy",
43569         "598"
43570       ],
43571       [
43572         "Uzbekistan (Oʻzbekiston)",
43573         "uz",
43574         "998"
43575       ],
43576       [
43577         "Vanuatu",
43578         "vu",
43579         "678"
43580       ],
43581       [
43582         "Vatican City (Città del Vaticano)",
43583         "va",
43584         "39",
43585         1
43586       ],
43587       [
43588         "Venezuela",
43589         "ve",
43590         "58"
43591       ],
43592       [
43593         "Vietnam (Việt Nam)",
43594         "vn",
43595         "84"
43596       ],
43597       [
43598         "Wallis and Futuna (Wallis-et-Futuna)",
43599         "wf",
43600         "681"
43601       ],
43602       [
43603         "Western Sahara (‫الصحراء الغربية‬‎)",
43604         "eh",
43605         "212",
43606         1
43607       ],
43608       [
43609         "Yemen (‫اليمن‬‎)",
43610         "ye",
43611         "967"
43612       ],
43613       [
43614         "Zambia",
43615         "zm",
43616         "260"
43617       ],
43618       [
43619         "Zimbabwe",
43620         "zw",
43621         "263"
43622       ],
43623       [
43624         "Åland Islands",
43625         "ax",
43626         "358",
43627         1
43628       ]
43629   ];
43630   
43631   return d;
43632 }/**
43633 *    This script refer to:
43634 *    Title: International Telephone Input
43635 *    Author: Jack O'Connor
43636 *    Code version:  v12.1.12
43637 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43638 **/
43639
43640 /**
43641  * @class Roo.bootstrap.PhoneInput
43642  * @extends Roo.bootstrap.TriggerField
43643  * An input with International dial-code selection
43644  
43645  * @cfg {String} defaultDialCode default '+852'
43646  * @cfg {Array} preferedCountries default []
43647   
43648  * @constructor
43649  * Create a new PhoneInput.
43650  * @param {Object} config Configuration options
43651  */
43652
43653 Roo.bootstrap.PhoneInput = function(config) {
43654     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43655 };
43656
43657 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43658         /**
43659         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43660         */
43661         listWidth: undefined,
43662         
43663         selectedClass: 'active',
43664         
43665         invalidClass : "has-warning",
43666         
43667         validClass: 'has-success',
43668         
43669         allowed: '0123456789',
43670         
43671         max_length: 15,
43672         
43673         /**
43674          * @cfg {String} defaultDialCode The default dial code when initializing the input
43675          */
43676         defaultDialCode: '+852',
43677         
43678         /**
43679          * @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
43680          */
43681         preferedCountries: false,
43682         
43683         getAutoCreate : function()
43684         {
43685             var data = Roo.bootstrap.PhoneInputData();
43686             var align = this.labelAlign || this.parentLabelAlign();
43687             var id = Roo.id();
43688             
43689             this.allCountries = [];
43690             this.dialCodeMapping = [];
43691             
43692             for (var i = 0; i < data.length; i++) {
43693               var c = data[i];
43694               this.allCountries[i] = {
43695                 name: c[0],
43696                 iso2: c[1],
43697                 dialCode: c[2],
43698                 priority: c[3] || 0,
43699                 areaCodes: c[4] || null
43700               };
43701               this.dialCodeMapping[c[2]] = {
43702                   name: c[0],
43703                   iso2: c[1],
43704                   priority: c[3] || 0,
43705                   areaCodes: c[4] || null
43706               };
43707             }
43708             
43709             var cfg = {
43710                 cls: 'form-group',
43711                 cn: []
43712             };
43713             
43714             var input =  {
43715                 tag: 'input',
43716                 id : id,
43717                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43718                 maxlength: this.max_length,
43719                 cls : 'form-control tel-input',
43720                 autocomplete: 'new-password'
43721             };
43722             
43723             var hiddenInput = {
43724                 tag: 'input',
43725                 type: 'hidden',
43726                 cls: 'hidden-tel-input'
43727             };
43728             
43729             if (this.name) {
43730                 hiddenInput.name = this.name;
43731             }
43732             
43733             if (this.disabled) {
43734                 input.disabled = true;
43735             }
43736             
43737             var flag_container = {
43738                 tag: 'div',
43739                 cls: 'flag-box',
43740                 cn: [
43741                     {
43742                         tag: 'div',
43743                         cls: 'flag'
43744                     },
43745                     {
43746                         tag: 'div',
43747                         cls: 'caret'
43748                     }
43749                 ]
43750             };
43751             
43752             var box = {
43753                 tag: 'div',
43754                 cls: this.hasFeedback ? 'has-feedback' : '',
43755                 cn: [
43756                     hiddenInput,
43757                     input,
43758                     {
43759                         tag: 'input',
43760                         cls: 'dial-code-holder',
43761                         disabled: true
43762                     }
43763                 ]
43764             };
43765             
43766             var container = {
43767                 cls: 'roo-select2-container input-group',
43768                 cn: [
43769                     flag_container,
43770                     box
43771                 ]
43772             };
43773             
43774             if (this.fieldLabel.length) {
43775                 var indicator = {
43776                     tag: 'i',
43777                     tooltip: 'This field is required'
43778                 };
43779                 
43780                 var label = {
43781                     tag: 'label',
43782                     'for':  id,
43783                     cls: 'control-label',
43784                     cn: []
43785                 };
43786                 
43787                 var label_text = {
43788                     tag: 'span',
43789                     html: this.fieldLabel
43790                 };
43791                 
43792                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43793                 label.cn = [
43794                     indicator,
43795                     label_text
43796                 ];
43797                 
43798                 if(this.indicatorpos == 'right') {
43799                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43800                     label.cn = [
43801                         label_text,
43802                         indicator
43803                     ];
43804                 }
43805                 
43806                 if(align == 'left') {
43807                     container = {
43808                         tag: 'div',
43809                         cn: [
43810                             container
43811                         ]
43812                     };
43813                     
43814                     if(this.labelWidth > 12){
43815                         label.style = "width: " + this.labelWidth + 'px';
43816                     }
43817                     if(this.labelWidth < 13 && this.labelmd == 0){
43818                         this.labelmd = this.labelWidth;
43819                     }
43820                     if(this.labellg > 0){
43821                         label.cls += ' col-lg-' + this.labellg;
43822                         input.cls += ' col-lg-' + (12 - this.labellg);
43823                     }
43824                     if(this.labelmd > 0){
43825                         label.cls += ' col-md-' + this.labelmd;
43826                         container.cls += ' col-md-' + (12 - this.labelmd);
43827                     }
43828                     if(this.labelsm > 0){
43829                         label.cls += ' col-sm-' + this.labelsm;
43830                         container.cls += ' col-sm-' + (12 - this.labelsm);
43831                     }
43832                     if(this.labelxs > 0){
43833                         label.cls += ' col-xs-' + this.labelxs;
43834                         container.cls += ' col-xs-' + (12 - this.labelxs);
43835                     }
43836                 }
43837             }
43838             
43839             cfg.cn = [
43840                 label,
43841                 container
43842             ];
43843             
43844             var settings = this;
43845             
43846             ['xs','sm','md','lg'].map(function(size){
43847                 if (settings[size]) {
43848                     cfg.cls += ' col-' + size + '-' + settings[size];
43849                 }
43850             });
43851             
43852             this.store = new Roo.data.Store({
43853                 proxy : new Roo.data.MemoryProxy({}),
43854                 reader : new Roo.data.JsonReader({
43855                     fields : [
43856                         {
43857                             'name' : 'name',
43858                             'type' : 'string'
43859                         },
43860                         {
43861                             'name' : 'iso2',
43862                             'type' : 'string'
43863                         },
43864                         {
43865                             'name' : 'dialCode',
43866                             'type' : 'string'
43867                         },
43868                         {
43869                             'name' : 'priority',
43870                             'type' : 'string'
43871                         },
43872                         {
43873                             'name' : 'areaCodes',
43874                             'type' : 'string'
43875                         }
43876                     ]
43877                 })
43878             });
43879             
43880             if(!this.preferedCountries) {
43881                 this.preferedCountries = [
43882                     'hk',
43883                     'gb',
43884                     'us'
43885                 ];
43886             }
43887             
43888             var p = this.preferedCountries.reverse();
43889             
43890             if(p) {
43891                 for (var i = 0; i < p.length; i++) {
43892                     for (var j = 0; j < this.allCountries.length; j++) {
43893                         if(this.allCountries[j].iso2 == p[i]) {
43894                             var t = this.allCountries[j];
43895                             this.allCountries.splice(j,1);
43896                             this.allCountries.unshift(t);
43897                         }
43898                     } 
43899                 }
43900             }
43901             
43902             this.store.proxy.data = {
43903                 success: true,
43904                 data: this.allCountries
43905             };
43906             
43907             return cfg;
43908         },
43909         
43910         initEvents : function()
43911         {
43912             this.createList();
43913             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43914             
43915             this.indicator = this.indicatorEl();
43916             this.flag = this.flagEl();
43917             this.dialCodeHolder = this.dialCodeHolderEl();
43918             
43919             this.trigger = this.el.select('div.flag-box',true).first();
43920             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43921             
43922             var _this = this;
43923             
43924             (function(){
43925                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43926                 _this.list.setWidth(lw);
43927             }).defer(100);
43928             
43929             this.list.on('mouseover', this.onViewOver, this);
43930             this.list.on('mousemove', this.onViewMove, this);
43931             this.inputEl().on("keyup", this.onKeyUp, this);
43932             this.inputEl().on("keypress", this.onKeyPress, this);
43933             
43934             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43935
43936             this.view = new Roo.View(this.list, this.tpl, {
43937                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43938             });
43939             
43940             this.view.on('click', this.onViewClick, this);
43941             this.setValue(this.defaultDialCode);
43942         },
43943         
43944         onTriggerClick : function(e)
43945         {
43946             Roo.log('trigger click');
43947             if(this.disabled){
43948                 return;
43949             }
43950             
43951             if(this.isExpanded()){
43952                 this.collapse();
43953                 this.hasFocus = false;
43954             }else {
43955                 this.store.load({});
43956                 this.hasFocus = true;
43957                 this.expand();
43958             }
43959         },
43960         
43961         isExpanded : function()
43962         {
43963             return this.list.isVisible();
43964         },
43965         
43966         collapse : function()
43967         {
43968             if(!this.isExpanded()){
43969                 return;
43970             }
43971             this.list.hide();
43972             Roo.get(document).un('mousedown', this.collapseIf, this);
43973             Roo.get(document).un('mousewheel', this.collapseIf, this);
43974             this.fireEvent('collapse', this);
43975             this.validate();
43976         },
43977         
43978         expand : function()
43979         {
43980             Roo.log('expand');
43981
43982             if(this.isExpanded() || !this.hasFocus){
43983                 return;
43984             }
43985             
43986             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43987             this.list.setWidth(lw);
43988             
43989             this.list.show();
43990             this.restrictHeight();
43991             
43992             Roo.get(document).on('mousedown', this.collapseIf, this);
43993             Roo.get(document).on('mousewheel', this.collapseIf, this);
43994             
43995             this.fireEvent('expand', this);
43996         },
43997         
43998         restrictHeight : function()
43999         {
44000             this.list.alignTo(this.inputEl(), this.listAlign);
44001             this.list.alignTo(this.inputEl(), this.listAlign);
44002         },
44003         
44004         onViewOver : function(e, t)
44005         {
44006             if(this.inKeyMode){
44007                 return;
44008             }
44009             var item = this.view.findItemFromChild(t);
44010             
44011             if(item){
44012                 var index = this.view.indexOf(item);
44013                 this.select(index, false);
44014             }
44015         },
44016
44017         // private
44018         onViewClick : function(view, doFocus, el, e)
44019         {
44020             var index = this.view.getSelectedIndexes()[0];
44021             
44022             var r = this.store.getAt(index);
44023             
44024             if(r){
44025                 this.onSelect(r, index);
44026             }
44027             if(doFocus !== false && !this.blockFocus){
44028                 this.inputEl().focus();
44029             }
44030         },
44031         
44032         onViewMove : function(e, t)
44033         {
44034             this.inKeyMode = false;
44035         },
44036         
44037         select : function(index, scrollIntoView)
44038         {
44039             this.selectedIndex = index;
44040             this.view.select(index);
44041             if(scrollIntoView !== false){
44042                 var el = this.view.getNode(index);
44043                 if(el){
44044                     this.list.scrollChildIntoView(el, false);
44045                 }
44046             }
44047         },
44048         
44049         createList : function()
44050         {
44051             this.list = Roo.get(document.body).createChild({
44052                 tag: 'ul',
44053                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44054                 style: 'display:none'
44055             });
44056             
44057             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44058         },
44059         
44060         collapseIf : function(e)
44061         {
44062             var in_combo  = e.within(this.el);
44063             var in_list =  e.within(this.list);
44064             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44065             
44066             if (in_combo || in_list || is_list) {
44067                 return;
44068             }
44069             this.collapse();
44070         },
44071         
44072         onSelect : function(record, index)
44073         {
44074             if(this.fireEvent('beforeselect', this, record, index) !== false){
44075                 
44076                 this.setFlagClass(record.data.iso2);
44077                 this.setDialCode(record.data.dialCode);
44078                 this.hasFocus = false;
44079                 this.collapse();
44080                 this.fireEvent('select', this, record, index);
44081             }
44082         },
44083         
44084         flagEl : function()
44085         {
44086             var flag = this.el.select('div.flag',true).first();
44087             if(!flag){
44088                 return false;
44089             }
44090             return flag;
44091         },
44092         
44093         dialCodeHolderEl : function()
44094         {
44095             var d = this.el.select('input.dial-code-holder',true).first();
44096             if(!d){
44097                 return false;
44098             }
44099             return d;
44100         },
44101         
44102         setDialCode : function(v)
44103         {
44104             this.dialCodeHolder.dom.value = '+'+v;
44105         },
44106         
44107         setFlagClass : function(n)
44108         {
44109             this.flag.dom.className = 'flag '+n;
44110         },
44111         
44112         getValue : function()
44113         {
44114             var v = this.inputEl().getValue();
44115             if(this.dialCodeHolder) {
44116                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44117             }
44118             return v;
44119         },
44120         
44121         setValue : function(v)
44122         {
44123             var d = this.getDialCode(v);
44124             
44125             //invalid dial code
44126             if(v.length == 0 || !d || d.length == 0) {
44127                 if(this.rendered){
44128                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44129                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44130                 }
44131                 return;
44132             }
44133             
44134             //valid dial code
44135             this.setFlagClass(this.dialCodeMapping[d].iso2);
44136             this.setDialCode(d);
44137             this.inputEl().dom.value = v.replace('+'+d,'');
44138             this.hiddenEl().dom.value = this.getValue();
44139             
44140             this.validate();
44141         },
44142         
44143         getDialCode : function(v)
44144         {
44145             v = v ||  '';
44146             
44147             if (v.length == 0) {
44148                 return this.dialCodeHolder.dom.value;
44149             }
44150             
44151             var dialCode = "";
44152             if (v.charAt(0) != "+") {
44153                 return false;
44154             }
44155             var numericChars = "";
44156             for (var i = 1; i < v.length; i++) {
44157               var c = v.charAt(i);
44158               if (!isNaN(c)) {
44159                 numericChars += c;
44160                 if (this.dialCodeMapping[numericChars]) {
44161                   dialCode = v.substr(1, i);
44162                 }
44163                 if (numericChars.length == 4) {
44164                   break;
44165                 }
44166               }
44167             }
44168             return dialCode;
44169         },
44170         
44171         reset : function()
44172         {
44173             this.setValue(this.defaultDialCode);
44174             this.validate();
44175         },
44176         
44177         hiddenEl : function()
44178         {
44179             return this.el.select('input.hidden-tel-input',true).first();
44180         },
44181         
44182         // after setting val
44183         onKeyUp : function(e){
44184             this.setValue(this.getValue());
44185         },
44186         
44187         onKeyPress : function(e){
44188             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44189                 e.stopEvent();
44190             }
44191         }
44192         
44193 });
44194 /**
44195  * @class Roo.bootstrap.MoneyField
44196  * @extends Roo.bootstrap.ComboBox
44197  * Bootstrap MoneyField class
44198  * 
44199  * @constructor
44200  * Create a new MoneyField.
44201  * @param {Object} config Configuration options
44202  */
44203
44204 Roo.bootstrap.MoneyField = function(config) {
44205     
44206     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44207     
44208 };
44209
44210 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44211     
44212     /**
44213      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44214      */
44215     allowDecimals : true,
44216     /**
44217      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44218      */
44219     decimalSeparator : ".",
44220     /**
44221      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44222      */
44223     decimalPrecision : 0,
44224     /**
44225      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44226      */
44227     allowNegative : true,
44228     /**
44229      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44230      */
44231     allowZero: true,
44232     /**
44233      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44234      */
44235     minValue : Number.NEGATIVE_INFINITY,
44236     /**
44237      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44238      */
44239     maxValue : Number.MAX_VALUE,
44240     /**
44241      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44242      */
44243     minText : "The minimum value for this field is {0}",
44244     /**
44245      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44246      */
44247     maxText : "The maximum value for this field is {0}",
44248     /**
44249      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44250      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44251      */
44252     nanText : "{0} is not a valid number",
44253     /**
44254      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44255      */
44256     castInt : true,
44257     /**
44258      * @cfg {String} defaults currency of the MoneyField
44259      * value should be in lkey
44260      */
44261     defaultCurrency : false,
44262     /**
44263      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44264      */
44265     thousandsDelimiter : false,
44266     /**
44267      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44268      */
44269     max_length: false,
44270     
44271     inputlg : 9,
44272     inputmd : 9,
44273     inputsm : 9,
44274     inputxs : 6,
44275     
44276     store : false,
44277     
44278     getAutoCreate : function()
44279     {
44280         var align = this.labelAlign || this.parentLabelAlign();
44281         
44282         var id = Roo.id();
44283
44284         var cfg = {
44285             cls: 'form-group',
44286             cn: []
44287         };
44288
44289         var input =  {
44290             tag: 'input',
44291             id : id,
44292             cls : 'form-control roo-money-amount-input',
44293             autocomplete: 'new-password'
44294         };
44295         
44296         var hiddenInput = {
44297             tag: 'input',
44298             type: 'hidden',
44299             id: Roo.id(),
44300             cls: 'hidden-number-input'
44301         };
44302         
44303         if(this.max_length) {
44304             input.maxlength = this.max_length; 
44305         }
44306         
44307         if (this.name) {
44308             hiddenInput.name = this.name;
44309         }
44310
44311         if (this.disabled) {
44312             input.disabled = true;
44313         }
44314
44315         var clg = 12 - this.inputlg;
44316         var cmd = 12 - this.inputmd;
44317         var csm = 12 - this.inputsm;
44318         var cxs = 12 - this.inputxs;
44319         
44320         var container = {
44321             tag : 'div',
44322             cls : 'row roo-money-field',
44323             cn : [
44324                 {
44325                     tag : 'div',
44326                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44327                     cn : [
44328                         {
44329                             tag : 'div',
44330                             cls: 'roo-select2-container input-group',
44331                             cn: [
44332                                 {
44333                                     tag : 'input',
44334                                     cls : 'form-control roo-money-currency-input',
44335                                     autocomplete: 'new-password',
44336                                     readOnly : 1,
44337                                     name : this.currencyName
44338                                 },
44339                                 {
44340                                     tag :'span',
44341                                     cls : 'input-group-addon',
44342                                     cn : [
44343                                         {
44344                                             tag: 'span',
44345                                             cls: 'caret'
44346                                         }
44347                                     ]
44348                                 }
44349                             ]
44350                         }
44351                     ]
44352                 },
44353                 {
44354                     tag : 'div',
44355                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44356                     cn : [
44357                         {
44358                             tag: 'div',
44359                             cls: this.hasFeedback ? 'has-feedback' : '',
44360                             cn: [
44361                                 input
44362                             ]
44363                         }
44364                     ]
44365                 }
44366             ]
44367             
44368         };
44369         
44370         if (this.fieldLabel.length) {
44371             var indicator = {
44372                 tag: 'i',
44373                 tooltip: 'This field is required'
44374             };
44375
44376             var label = {
44377                 tag: 'label',
44378                 'for':  id,
44379                 cls: 'control-label',
44380                 cn: []
44381             };
44382
44383             var label_text = {
44384                 tag: 'span',
44385                 html: this.fieldLabel
44386             };
44387
44388             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44389             label.cn = [
44390                 indicator,
44391                 label_text
44392             ];
44393
44394             if(this.indicatorpos == 'right') {
44395                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44396                 label.cn = [
44397                     label_text,
44398                     indicator
44399                 ];
44400             }
44401
44402             if(align == 'left') {
44403                 container = {
44404                     tag: 'div',
44405                     cn: [
44406                         container
44407                     ]
44408                 };
44409
44410                 if(this.labelWidth > 12){
44411                     label.style = "width: " + this.labelWidth + 'px';
44412                 }
44413                 if(this.labelWidth < 13 && this.labelmd == 0){
44414                     this.labelmd = this.labelWidth;
44415                 }
44416                 if(this.labellg > 0){
44417                     label.cls += ' col-lg-' + this.labellg;
44418                     input.cls += ' col-lg-' + (12 - this.labellg);
44419                 }
44420                 if(this.labelmd > 0){
44421                     label.cls += ' col-md-' + this.labelmd;
44422                     container.cls += ' col-md-' + (12 - this.labelmd);
44423                 }
44424                 if(this.labelsm > 0){
44425                     label.cls += ' col-sm-' + this.labelsm;
44426                     container.cls += ' col-sm-' + (12 - this.labelsm);
44427                 }
44428                 if(this.labelxs > 0){
44429                     label.cls += ' col-xs-' + this.labelxs;
44430                     container.cls += ' col-xs-' + (12 - this.labelxs);
44431                 }
44432             }
44433         }
44434
44435         cfg.cn = [
44436             label,
44437             container,
44438             hiddenInput
44439         ];
44440         
44441         var settings = this;
44442
44443         ['xs','sm','md','lg'].map(function(size){
44444             if (settings[size]) {
44445                 cfg.cls += ' col-' + size + '-' + settings[size];
44446             }
44447         });
44448         
44449         return cfg;
44450     },
44451     
44452     initEvents : function()
44453     {
44454         this.indicator = this.indicatorEl();
44455         
44456         this.initCurrencyEvent();
44457         
44458         this.initNumberEvent();
44459     },
44460     
44461     initCurrencyEvent : function()
44462     {
44463         if (!this.store) {
44464             throw "can not find store for combo";
44465         }
44466         
44467         this.store = Roo.factory(this.store, Roo.data);
44468         this.store.parent = this;
44469         
44470         this.createList();
44471         
44472         this.triggerEl = this.el.select('.input-group-addon', true).first();
44473         
44474         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44475         
44476         var _this = this;
44477         
44478         (function(){
44479             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44480             _this.list.setWidth(lw);
44481         }).defer(100);
44482         
44483         this.list.on('mouseover', this.onViewOver, this);
44484         this.list.on('mousemove', this.onViewMove, this);
44485         this.list.on('scroll', this.onViewScroll, this);
44486         
44487         if(!this.tpl){
44488             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44489         }
44490         
44491         this.view = new Roo.View(this.list, this.tpl, {
44492             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44493         });
44494         
44495         this.view.on('click', this.onViewClick, this);
44496         
44497         this.store.on('beforeload', this.onBeforeLoad, this);
44498         this.store.on('load', this.onLoad, this);
44499         this.store.on('loadexception', this.onLoadException, this);
44500         
44501         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44502             "up" : function(e){
44503                 this.inKeyMode = true;
44504                 this.selectPrev();
44505             },
44506
44507             "down" : function(e){
44508                 if(!this.isExpanded()){
44509                     this.onTriggerClick();
44510                 }else{
44511                     this.inKeyMode = true;
44512                     this.selectNext();
44513                 }
44514             },
44515
44516             "enter" : function(e){
44517                 this.collapse();
44518                 
44519                 if(this.fireEvent("specialkey", this, e)){
44520                     this.onViewClick(false);
44521                 }
44522                 
44523                 return true;
44524             },
44525
44526             "esc" : function(e){
44527                 this.collapse();
44528             },
44529
44530             "tab" : function(e){
44531                 this.collapse();
44532                 
44533                 if(this.fireEvent("specialkey", this, e)){
44534                     this.onViewClick(false);
44535                 }
44536                 
44537                 return true;
44538             },
44539
44540             scope : this,
44541
44542             doRelay : function(foo, bar, hname){
44543                 if(hname == 'down' || this.scope.isExpanded()){
44544                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44545                 }
44546                 return true;
44547             },
44548
44549             forceKeyDown: true
44550         });
44551         
44552         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44553         
44554     },
44555     
44556     initNumberEvent : function(e)
44557     {
44558         this.inputEl().on("keydown" , this.fireKey,  this);
44559         this.inputEl().on("focus", this.onFocus,  this);
44560         this.inputEl().on("blur", this.onBlur,  this);
44561         
44562         this.inputEl().relayEvent('keyup', this);
44563         
44564         if(this.indicator){
44565             this.indicator.addClass('invisible');
44566         }
44567  
44568         this.originalValue = this.getValue();
44569         
44570         if(this.validationEvent == 'keyup'){
44571             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44572             this.inputEl().on('keyup', this.filterValidation, this);
44573         }
44574         else if(this.validationEvent !== false){
44575             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44576         }
44577         
44578         if(this.selectOnFocus){
44579             this.on("focus", this.preFocus, this);
44580             
44581         }
44582         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44583             this.inputEl().on("keypress", this.filterKeys, this);
44584         } else {
44585             this.inputEl().relayEvent('keypress', this);
44586         }
44587         
44588         var allowed = "0123456789";
44589         
44590         if(this.allowDecimals){
44591             allowed += this.decimalSeparator;
44592         }
44593         
44594         if(this.allowNegative){
44595             allowed += "-";
44596         }
44597         
44598         if(this.thousandsDelimiter) {
44599             allowed += ",";
44600         }
44601         
44602         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44603         
44604         var keyPress = function(e){
44605             
44606             var k = e.getKey();
44607             
44608             var c = e.getCharCode();
44609             
44610             if(
44611                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44612                     allowed.indexOf(String.fromCharCode(c)) === -1
44613             ){
44614                 e.stopEvent();
44615                 return;
44616             }
44617             
44618             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44619                 return;
44620             }
44621             
44622             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44623                 e.stopEvent();
44624             }
44625         };
44626         
44627         this.inputEl().on("keypress", keyPress, this);
44628         
44629     },
44630     
44631     onTriggerClick : function(e)
44632     {   
44633         if(this.disabled){
44634             return;
44635         }
44636         
44637         this.page = 0;
44638         this.loadNext = false;
44639         
44640         if(this.isExpanded()){
44641             this.collapse();
44642             return;
44643         }
44644         
44645         this.hasFocus = true;
44646         
44647         if(this.triggerAction == 'all') {
44648             this.doQuery(this.allQuery, true);
44649             return;
44650         }
44651         
44652         this.doQuery(this.getRawValue());
44653     },
44654     
44655     getCurrency : function()
44656     {   
44657         var v = this.currencyEl().getValue();
44658         
44659         return v;
44660     },
44661     
44662     restrictHeight : function()
44663     {
44664         this.list.alignTo(this.currencyEl(), this.listAlign);
44665         this.list.alignTo(this.currencyEl(), this.listAlign);
44666     },
44667     
44668     onViewClick : function(view, doFocus, el, e)
44669     {
44670         var index = this.view.getSelectedIndexes()[0];
44671         
44672         var r = this.store.getAt(index);
44673         
44674         if(r){
44675             this.onSelect(r, index);
44676         }
44677     },
44678     
44679     onSelect : function(record, index){
44680         
44681         if(this.fireEvent('beforeselect', this, record, index) !== false){
44682         
44683             this.setFromCurrencyData(index > -1 ? record.data : false);
44684             
44685             this.collapse();
44686             
44687             this.fireEvent('select', this, record, index);
44688         }
44689     },
44690     
44691     setFromCurrencyData : function(o)
44692     {
44693         var currency = '';
44694         
44695         this.lastCurrency = o;
44696         
44697         if (this.currencyField) {
44698             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44699         } else {
44700             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44701         }
44702         
44703         this.lastSelectionText = currency;
44704         
44705         //setting default currency
44706         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44707             this.setCurrency(this.defaultCurrency);
44708             return;
44709         }
44710         
44711         this.setCurrency(currency);
44712     },
44713     
44714     setFromData : function(o)
44715     {
44716         var c = {};
44717         
44718         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44719         
44720         this.setFromCurrencyData(c);
44721         
44722         var value = '';
44723         
44724         if (this.name) {
44725             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44726         } else {
44727             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44728         }
44729         
44730         this.setValue(value);
44731         
44732     },
44733     
44734     setCurrency : function(v)
44735     {   
44736         this.currencyValue = v;
44737         
44738         if(this.rendered){
44739             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44740             this.validate();
44741         }
44742     },
44743     
44744     setValue : function(v)
44745     {
44746         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44747         
44748         this.value = v;
44749         
44750         if(this.rendered){
44751             
44752             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44753             
44754             this.inputEl().dom.value = (v == '') ? '' :
44755                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44756             
44757             if(!this.allowZero && v === '0') {
44758                 this.hiddenEl().dom.value = '';
44759                 this.inputEl().dom.value = '';
44760             }
44761             
44762             this.validate();
44763         }
44764     },
44765     
44766     getRawValue : function()
44767     {
44768         var v = this.inputEl().getValue();
44769         
44770         return v;
44771     },
44772     
44773     getValue : function()
44774     {
44775         return this.fixPrecision(this.parseValue(this.getRawValue()));
44776     },
44777     
44778     parseValue : function(value)
44779     {
44780         if(this.thousandsDelimiter) {
44781             value += "";
44782             r = new RegExp(",", "g");
44783             value = value.replace(r, "");
44784         }
44785         
44786         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44787         return isNaN(value) ? '' : value;
44788         
44789     },
44790     
44791     fixPrecision : function(value)
44792     {
44793         if(this.thousandsDelimiter) {
44794             value += "";
44795             r = new RegExp(",", "g");
44796             value = value.replace(r, "");
44797         }
44798         
44799         var nan = isNaN(value);
44800         
44801         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44802             return nan ? '' : value;
44803         }
44804         return parseFloat(value).toFixed(this.decimalPrecision);
44805     },
44806     
44807     decimalPrecisionFcn : function(v)
44808     {
44809         return Math.floor(v);
44810     },
44811     
44812     validateValue : function(value)
44813     {
44814         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44815             return false;
44816         }
44817         
44818         var num = this.parseValue(value);
44819         
44820         if(isNaN(num)){
44821             this.markInvalid(String.format(this.nanText, value));
44822             return false;
44823         }
44824         
44825         if(num < this.minValue){
44826             this.markInvalid(String.format(this.minText, this.minValue));
44827             return false;
44828         }
44829         
44830         if(num > this.maxValue){
44831             this.markInvalid(String.format(this.maxText, this.maxValue));
44832             return false;
44833         }
44834         
44835         return true;
44836     },
44837     
44838     validate : function()
44839     {
44840         if(this.disabled || this.allowBlank){
44841             this.markValid();
44842             return true;
44843         }
44844         
44845         var currency = this.getCurrency();
44846         
44847         if(this.validateValue(this.getRawValue()) && currency.length){
44848             this.markValid();
44849             return true;
44850         }
44851         
44852         this.markInvalid();
44853         return false;
44854     },
44855     
44856     getName: function()
44857     {
44858         return this.name;
44859     },
44860     
44861     beforeBlur : function()
44862     {
44863         if(!this.castInt){
44864             return;
44865         }
44866         
44867         var v = this.parseValue(this.getRawValue());
44868         
44869         if(v || v == 0){
44870             this.setValue(v);
44871         }
44872     },
44873     
44874     onBlur : function()
44875     {
44876         this.beforeBlur();
44877         
44878         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44879             //this.el.removeClass(this.focusClass);
44880         }
44881         
44882         this.hasFocus = false;
44883         
44884         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44885             this.validate();
44886         }
44887         
44888         var v = this.getValue();
44889         
44890         if(String(v) !== String(this.startValue)){
44891             this.fireEvent('change', this, v, this.startValue);
44892         }
44893         
44894         this.fireEvent("blur", this);
44895     },
44896     
44897     inputEl : function()
44898     {
44899         return this.el.select('.roo-money-amount-input', true).first();
44900     },
44901     
44902     currencyEl : function()
44903     {
44904         return this.el.select('.roo-money-currency-input', true).first();
44905     },
44906     
44907     hiddenEl : function()
44908     {
44909         return this.el.select('input.hidden-number-input',true).first();
44910     }
44911     
44912 });/**
44913  * @class Roo.bootstrap.BezierSignature
44914  * @extends Roo.bootstrap.Component
44915  * Bootstrap BezierSignature class
44916  * This script refer to:
44917  *    Title: Signature Pad
44918  *    Author: szimek
44919  *    Availability: https://github.com/szimek/signature_pad
44920  *
44921  * @constructor
44922  * Create a new BezierSignature
44923  * @param {Object} config The config object
44924  */
44925
44926 Roo.bootstrap.BezierSignature = function(config){
44927     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44928     this.addEvents({
44929         "resize" : true
44930     });
44931 };
44932
44933 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44934 {
44935      
44936     curve_data: [],
44937     
44938     is_empty: true,
44939     
44940     mouse_btn_down: true,
44941     
44942     /**
44943      * @cfg {int} canvas height
44944      */
44945     canvas_height: '200px',
44946     
44947     /**
44948      * @cfg {float|function} Radius of a single dot.
44949      */ 
44950     dot_size: false,
44951     
44952     /**
44953      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44954      */
44955     min_width: 0.5,
44956     
44957     /**
44958      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44959      */
44960     max_width: 2.5,
44961     
44962     /**
44963      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44964      */
44965     throttle: 16,
44966     
44967     /**
44968      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44969      */
44970     min_distance: 5,
44971     
44972     /**
44973      * @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.
44974      */
44975     bg_color: 'rgba(0, 0, 0, 0)',
44976     
44977     /**
44978      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44979      */
44980     dot_color: 'black',
44981     
44982     /**
44983      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44984      */ 
44985     velocity_filter_weight: 0.7,
44986     
44987     /**
44988      * @cfg {function} Callback when stroke begin. 
44989      */
44990     onBegin: false,
44991     
44992     /**
44993      * @cfg {function} Callback when stroke end.
44994      */
44995     onEnd: false,
44996     
44997     getAutoCreate : function()
44998     {
44999         var cls = 'roo-signature column';
45000         
45001         if(this.cls){
45002             cls += ' ' + this.cls;
45003         }
45004         
45005         var col_sizes = [
45006             'lg',
45007             'md',
45008             'sm',
45009             'xs'
45010         ];
45011         
45012         for(var i = 0; i < col_sizes.length; i++) {
45013             if(this[col_sizes[i]]) {
45014                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45015             }
45016         }
45017         
45018         var cfg = {
45019             tag: 'div',
45020             cls: cls,
45021             cn: [
45022                 {
45023                     tag: 'div',
45024                     cls: 'roo-signature-body',
45025                     cn: [
45026                         {
45027                             tag: 'canvas',
45028                             cls: 'roo-signature-body-canvas',
45029                             height: this.canvas_height,
45030                             width: this.canvas_width
45031                         }
45032                     ]
45033                 },
45034                 {
45035                     tag: 'input',
45036                     type: 'file',
45037                     style: 'display: none'
45038                 }
45039             ]
45040         };
45041         
45042         return cfg;
45043     },
45044     
45045     initEvents: function() 
45046     {
45047         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45048         
45049         var canvas = this.canvasEl();
45050         
45051         // mouse && touch event swapping...
45052         canvas.dom.style.touchAction = 'none';
45053         canvas.dom.style.msTouchAction = 'none';
45054         
45055         this.mouse_btn_down = false;
45056         canvas.on('mousedown', this._handleMouseDown, this);
45057         canvas.on('mousemove', this._handleMouseMove, this);
45058         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45059         
45060         if (window.PointerEvent) {
45061             canvas.on('pointerdown', this._handleMouseDown, this);
45062             canvas.on('pointermove', this._handleMouseMove, this);
45063             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45064         }
45065         
45066         if ('ontouchstart' in window) {
45067             canvas.on('touchstart', this._handleTouchStart, this);
45068             canvas.on('touchmove', this._handleTouchMove, this);
45069             canvas.on('touchend', this._handleTouchEnd, this);
45070         }
45071         
45072         Roo.EventManager.onWindowResize(this.resize, this, true);
45073         
45074         // file input event
45075         this.fileEl().on('change', this.uploadImage, this);
45076         
45077         this.clear();
45078         
45079         this.resize();
45080     },
45081     
45082     resize: function(){
45083         
45084         var canvas = this.canvasEl().dom;
45085         var ctx = this.canvasElCtx();
45086         var img_data = false;
45087         
45088         if(canvas.width > 0) {
45089             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45090         }
45091         // setting canvas width will clean img data
45092         canvas.width = 0;
45093         
45094         var style = window.getComputedStyle ? 
45095             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45096             
45097         var padding_left = parseInt(style.paddingLeft) || 0;
45098         var padding_right = parseInt(style.paddingRight) || 0;
45099         
45100         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45101         
45102         if(img_data) {
45103             ctx.putImageData(img_data, 0, 0);
45104         }
45105     },
45106     
45107     _handleMouseDown: function(e)
45108     {
45109         if (e.browserEvent.which === 1) {
45110             this.mouse_btn_down = true;
45111             this.strokeBegin(e);
45112         }
45113     },
45114     
45115     _handleMouseMove: function (e)
45116     {
45117         if (this.mouse_btn_down) {
45118             this.strokeMoveUpdate(e);
45119         }
45120     },
45121     
45122     _handleMouseUp: function (e)
45123     {
45124         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45125             this.mouse_btn_down = false;
45126             this.strokeEnd(e);
45127         }
45128     },
45129     
45130     _handleTouchStart: function (e) {
45131         
45132         e.preventDefault();
45133         if (e.browserEvent.targetTouches.length === 1) {
45134             // var touch = e.browserEvent.changedTouches[0];
45135             // this.strokeBegin(touch);
45136             
45137              this.strokeBegin(e); // assume e catching the correct xy...
45138         }
45139     },
45140     
45141     _handleTouchMove: function (e) {
45142         e.preventDefault();
45143         // var touch = event.targetTouches[0];
45144         // _this._strokeMoveUpdate(touch);
45145         this.strokeMoveUpdate(e);
45146     },
45147     
45148     _handleTouchEnd: function (e) {
45149         var wasCanvasTouched = e.target === this.canvasEl().dom;
45150         if (wasCanvasTouched) {
45151             e.preventDefault();
45152             // var touch = event.changedTouches[0];
45153             // _this._strokeEnd(touch);
45154             this.strokeEnd(e);
45155         }
45156     },
45157     
45158     reset: function () {
45159         this._lastPoints = [];
45160         this._lastVelocity = 0;
45161         this._lastWidth = (this.min_width + this.max_width) / 2;
45162         this.canvasElCtx().fillStyle = this.dot_color;
45163     },
45164     
45165     strokeMoveUpdate: function(e)
45166     {
45167         this.strokeUpdate(e);
45168         
45169         if (this.throttle) {
45170             this.throttleStroke(this.strokeUpdate, this.throttle);
45171         }
45172         else {
45173             this.strokeUpdate(e);
45174         }
45175     },
45176     
45177     strokeBegin: function(e)
45178     {
45179         var newPointGroup = {
45180             color: this.dot_color,
45181             points: []
45182         };
45183         
45184         if (typeof this.onBegin === 'function') {
45185             this.onBegin(e);
45186         }
45187         
45188         this.curve_data.push(newPointGroup);
45189         this.reset();
45190         this.strokeUpdate(e);
45191     },
45192     
45193     strokeUpdate: function(e)
45194     {
45195         var rect = this.canvasEl().dom.getBoundingClientRect();
45196         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45197         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45198         var lastPoints = lastPointGroup.points;
45199         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45200         var isLastPointTooClose = lastPoint
45201             ? point.distanceTo(lastPoint) <= this.min_distance
45202             : false;
45203         var color = lastPointGroup.color;
45204         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45205             var curve = this.addPoint(point);
45206             if (!lastPoint) {
45207                 this.drawDot({color: color, point: point});
45208             }
45209             else if (curve) {
45210                 this.drawCurve({color: color, curve: curve});
45211             }
45212             lastPoints.push({
45213                 time: point.time,
45214                 x: point.x,
45215                 y: point.y
45216             });
45217         }
45218     },
45219     
45220     strokeEnd: function(e)
45221     {
45222         this.strokeUpdate(e);
45223         if (typeof this.onEnd === 'function') {
45224             this.onEnd(e);
45225         }
45226     },
45227     
45228     addPoint:  function (point) {
45229         var _lastPoints = this._lastPoints;
45230         _lastPoints.push(point);
45231         if (_lastPoints.length > 2) {
45232             if (_lastPoints.length === 3) {
45233                 _lastPoints.unshift(_lastPoints[0]);
45234             }
45235             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45236             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45237             _lastPoints.shift();
45238             return curve;
45239         }
45240         return null;
45241     },
45242     
45243     calculateCurveWidths: function (startPoint, endPoint) {
45244         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45245             (1 - this.velocity_filter_weight) * this._lastVelocity;
45246
45247         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45248         var widths = {
45249             end: newWidth,
45250             start: this._lastWidth
45251         };
45252         
45253         this._lastVelocity = velocity;
45254         this._lastWidth = newWidth;
45255         return widths;
45256     },
45257     
45258     drawDot: function (_a) {
45259         var color = _a.color, point = _a.point;
45260         var ctx = this.canvasElCtx();
45261         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45262         ctx.beginPath();
45263         this.drawCurveSegment(point.x, point.y, width);
45264         ctx.closePath();
45265         ctx.fillStyle = color;
45266         ctx.fill();
45267     },
45268     
45269     drawCurve: function (_a) {
45270         var color = _a.color, curve = _a.curve;
45271         var ctx = this.canvasElCtx();
45272         var widthDelta = curve.endWidth - curve.startWidth;
45273         var drawSteps = Math.floor(curve.length()) * 2;
45274         ctx.beginPath();
45275         ctx.fillStyle = color;
45276         for (var i = 0; i < drawSteps; i += 1) {
45277         var t = i / drawSteps;
45278         var tt = t * t;
45279         var ttt = tt * t;
45280         var u = 1 - t;
45281         var uu = u * u;
45282         var uuu = uu * u;
45283         var x = uuu * curve.startPoint.x;
45284         x += 3 * uu * t * curve.control1.x;
45285         x += 3 * u * tt * curve.control2.x;
45286         x += ttt * curve.endPoint.x;
45287         var y = uuu * curve.startPoint.y;
45288         y += 3 * uu * t * curve.control1.y;
45289         y += 3 * u * tt * curve.control2.y;
45290         y += ttt * curve.endPoint.y;
45291         var width = curve.startWidth + ttt * widthDelta;
45292         this.drawCurveSegment(x, y, width);
45293         }
45294         ctx.closePath();
45295         ctx.fill();
45296     },
45297     
45298     drawCurveSegment: function (x, y, width) {
45299         var ctx = this.canvasElCtx();
45300         ctx.moveTo(x, y);
45301         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45302         this.is_empty = false;
45303     },
45304     
45305     clear: function()
45306     {
45307         var ctx = this.canvasElCtx();
45308         var canvas = this.canvasEl().dom;
45309         ctx.fillStyle = this.bg_color;
45310         ctx.clearRect(0, 0, canvas.width, canvas.height);
45311         ctx.fillRect(0, 0, canvas.width, canvas.height);
45312         this.curve_data = [];
45313         this.reset();
45314         this.is_empty = true;
45315     },
45316     
45317     fileEl: function()
45318     {
45319         return  this.el.select('input',true).first();
45320     },
45321     
45322     canvasEl: function()
45323     {
45324         return this.el.select('canvas',true).first();
45325     },
45326     
45327     canvasElCtx: function()
45328     {
45329         return this.el.select('canvas',true).first().dom.getContext('2d');
45330     },
45331     
45332     getImage: function(type)
45333     {
45334         if(this.is_empty) {
45335             return false;
45336         }
45337         
45338         // encryption ?
45339         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45340     },
45341     
45342     drawFromImage: function(img_src)
45343     {
45344         var img = new Image();
45345         
45346         img.onload = function(){
45347             this.canvasElCtx().drawImage(img, 0, 0);
45348         }.bind(this);
45349         
45350         img.src = img_src;
45351         
45352         this.is_empty = false;
45353     },
45354     
45355     selectImage: function()
45356     {
45357         this.fileEl().dom.click();
45358     },
45359     
45360     uploadImage: function(e)
45361     {
45362         var reader = new FileReader();
45363         
45364         reader.onload = function(e){
45365             var img = new Image();
45366             img.onload = function(){
45367                 this.reset();
45368                 this.canvasElCtx().drawImage(img, 0, 0);
45369             }.bind(this);
45370             img.src = e.target.result;
45371         }.bind(this);
45372         
45373         reader.readAsDataURL(e.target.files[0]);
45374     },
45375     
45376     // Bezier Point Constructor
45377     Point: (function () {
45378         function Point(x, y, time) {
45379             this.x = x;
45380             this.y = y;
45381             this.time = time || Date.now();
45382         }
45383         Point.prototype.distanceTo = function (start) {
45384             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45385         };
45386         Point.prototype.equals = function (other) {
45387             return this.x === other.x && this.y === other.y && this.time === other.time;
45388         };
45389         Point.prototype.velocityFrom = function (start) {
45390             return this.time !== start.time
45391             ? this.distanceTo(start) / (this.time - start.time)
45392             : 0;
45393         };
45394         return Point;
45395     }()),
45396     
45397     
45398     // Bezier Constructor
45399     Bezier: (function () {
45400         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45401             this.startPoint = startPoint;
45402             this.control2 = control2;
45403             this.control1 = control1;
45404             this.endPoint = endPoint;
45405             this.startWidth = startWidth;
45406             this.endWidth = endWidth;
45407         }
45408         Bezier.fromPoints = function (points, widths, scope) {
45409             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45410             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45411             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45412         };
45413         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45414             var dx1 = s1.x - s2.x;
45415             var dy1 = s1.y - s2.y;
45416             var dx2 = s2.x - s3.x;
45417             var dy2 = s2.y - s3.y;
45418             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45419             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45420             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45421             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45422             var dxm = m1.x - m2.x;
45423             var dym = m1.y - m2.y;
45424             var k = l2 / (l1 + l2);
45425             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45426             var tx = s2.x - cm.x;
45427             var ty = s2.y - cm.y;
45428             return {
45429                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45430                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45431             };
45432         };
45433         Bezier.prototype.length = function () {
45434             var steps = 10;
45435             var length = 0;
45436             var px;
45437             var py;
45438             for (var i = 0; i <= steps; i += 1) {
45439                 var t = i / steps;
45440                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45441                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45442                 if (i > 0) {
45443                     var xdiff = cx - px;
45444                     var ydiff = cy - py;
45445                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45446                 }
45447                 px = cx;
45448                 py = cy;
45449             }
45450             return length;
45451         };
45452         Bezier.prototype.point = function (t, start, c1, c2, end) {
45453             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45454             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45455             + (3.0 * c2 * (1.0 - t) * t * t)
45456             + (end * t * t * t);
45457         };
45458         return Bezier;
45459     }()),
45460     
45461     throttleStroke: function(fn, wait) {
45462       if (wait === void 0) { wait = 250; }
45463       var previous = 0;
45464       var timeout = null;
45465       var result;
45466       var storedContext;
45467       var storedArgs;
45468       var later = function () {
45469           previous = Date.now();
45470           timeout = null;
45471           result = fn.apply(storedContext, storedArgs);
45472           if (!timeout) {
45473               storedContext = null;
45474               storedArgs = [];
45475           }
45476       };
45477       return function wrapper() {
45478           var args = [];
45479           for (var _i = 0; _i < arguments.length; _i++) {
45480               args[_i] = arguments[_i];
45481           }
45482           var now = Date.now();
45483           var remaining = wait - (now - previous);
45484           storedContext = this;
45485           storedArgs = args;
45486           if (remaining <= 0 || remaining > wait) {
45487               if (timeout) {
45488                   clearTimeout(timeout);
45489                   timeout = null;
45490               }
45491               previous = now;
45492               result = fn.apply(storedContext, storedArgs);
45493               if (!timeout) {
45494                   storedContext = null;
45495                   storedArgs = [];
45496               }
45497           }
45498           else if (!timeout) {
45499               timeout = window.setTimeout(later, remaining);
45500           }
45501           return result;
45502       };
45503   }
45504   
45505 });
45506
45507  
45508
45509