roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * @children Roo.bootstrap.Component
656  * Bootstrap Element class (basically a DIV used to make random stuff )
657  * 
658  * @cfg {String} html contents of the element
659  * @cfg {String} tag tag of the element
660  * @cfg {String} cls class of the element
661  * @cfg {Boolean} preventDefault (true|false) default false
662  * @cfg {Boolean} clickable (true|false) default false
663  * @cfg {String} role default blank - set to button to force cursor pointer
664  
665  * 
666  * @constructor
667  * Create a new Element
668  * @param {Object} config The config object
669  */
670
671 Roo.bootstrap.Element = function(config){
672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
673     
674     this.addEvents({
675         // raw events
676         /**
677          * @event click
678          * When a element is chick
679          * @param {Roo.bootstrap.Element} this
680          * @param {Roo.EventObject} e
681          */
682         "click" : true 
683         
684       
685     });
686 };
687
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
689     
690     tag: 'div',
691     cls: '',
692     html: '',
693     preventDefault: false, 
694     clickable: false,
695     tapedTwice : false,
696     role : false,
697     
698     getAutoCreate : function(){
699         
700         var cfg = {
701             tag: this.tag,
702             // cls: this.cls, double assign in parent class Component.js :: onRender
703             html: this.html
704         };
705         if (this.role !== false) {
706             cfg.role = this.role;
707         }
708         
709         return cfg;
710     },
711     
712     initEvents: function() 
713     {
714         Roo.bootstrap.Element.superclass.initEvents.call(this);
715         
716         if(this.clickable){
717             this.el.on('click', this.onClick, this);
718         }
719         
720         
721     },
722     
723     onClick : function(e)
724     {
725         if(this.preventDefault){
726             e.preventDefault();
727         }
728         
729         this.fireEvent('click', this, e); // why was this double click before?
730     },
731     
732     
733     
734
735     
736     
737     getValue : function()
738     {
739         return this.el.dom.innerHTML;
740     },
741     
742     setValue : function(value)
743     {
744         this.el.dom.innerHTML = value;
745     }
746    
747 });
748
749  
750
751  /*
752  * - LGPL
753  *
754  * dropable area
755  * 
756  */
757
758 /**
759  * @class Roo.bootstrap.DropTarget
760  * @extends Roo.bootstrap.Element
761  * Bootstrap DropTarget class
762  
763  * @cfg {string} name dropable name
764  * 
765  * @constructor
766  * Create a new Dropable Area
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.DropTarget = function(config){
771     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772     
773     this.addEvents({
774         // raw events
775         /**
776          * @event click
777          * When a element is chick
778          * @param {Roo.bootstrap.Element} this
779          * @param {Roo.EventObject} e
780          */
781         "drop" : true
782     });
783 };
784
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
786     
787     
788     getAutoCreate : function(){
789         
790          
791     },
792     
793     initEvents: function() 
794     {
795         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797             ddGroup: this.name,
798             listeners : {
799                 drop : this.dragDrop.createDelegate(this),
800                 enter : this.dragEnter.createDelegate(this),
801                 out : this.dragOut.createDelegate(this),
802                 over : this.dragOver.createDelegate(this)
803             }
804             
805         });
806         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807     },
808     
809     dragDrop : function(source,e,data)
810     {
811         // user has to decide how to impliment this.
812         Roo.log('drop');
813         Roo.log(this);
814         //this.fireEvent('drop', this, source, e ,data);
815         return false;
816     },
817     
818     dragEnter : function(n, dd, e, data)
819     {
820         // probably want to resize the element to match the dropped element..
821         Roo.log("enter");
822         this.originalSize = this.el.getSize();
823         this.el.setSize( n.el.getSize());
824         this.dropZone.DDM.refreshCache(this.name);
825         Roo.log([n, dd, e, data]);
826     },
827     
828     dragOut : function(value)
829     {
830         // resize back to normal
831         Roo.log("out");
832         this.el.setSize(this.originalSize);
833         this.dropZone.resetConstraints();
834     },
835     
836     dragOver : function()
837     {
838         // ??? do nothing?
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * Body
849  *
850  */
851
852 /**
853  * @class Roo.bootstrap.Body
854  * @extends Roo.bootstrap.Component
855  * @builder-top
856  * @children Roo.bootstrap.Component
857  * @parent none
858  * Bootstrap Body class
859  *
860  * @constructor
861  * Create a new body
862  * @param {Object} config The config object
863  */
864
865 Roo.bootstrap.Body = function(config){
866
867     config = config || {};
868
869     Roo.bootstrap.Body.superclass.constructor.call(this, config);
870     this.el = Roo.get(config.el ? config.el : document.body );
871     if (this.cls && this.cls.length) {
872         Roo.get(document.body).addClass(this.cls);
873     }
874 };
875
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
877
878     is_body : true,// just to make sure it's constructed?
879
880         autoCreate : {
881         cls: 'container'
882     },
883     onRender : function(ct, position)
884     {
885        /* Roo.log("Roo.bootstrap.Body - onRender");
886         if (this.cls && this.cls.length) {
887             Roo.get(document.body).addClass(this.cls);
888         }
889         // style??? xttr???
890         */
891     }
892
893
894
895
896 });
897 /*
898  * - LGPL
899  *
900  * button group
901  * 
902  */
903
904
905 /**
906  * @class Roo.bootstrap.ButtonGroup
907  * @extends Roo.bootstrap.Component
908  * Bootstrap ButtonGroup class
909  * @cfg {String} size lg | sm | xs (default empty normal)
910  * @cfg {String} align vertical | justified  (default none)
911  * @cfg {String} direction up | down (default down)
912  * @cfg {Boolean} toolbar false | true
913  * @cfg {Boolean} btn true | false
914  * 
915  * 
916  * @constructor
917  * Create a new Input
918  * @param {Object} config The config object
919  */
920
921 Roo.bootstrap.ButtonGroup = function(config){
922     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
923 };
924
925 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
926     
927     size: '',
928     align: '',
929     direction: '',
930     toolbar: false,
931     btn: true,
932
933     getAutoCreate : function(){
934         var cfg = {
935             cls: 'btn-group',
936             html : null
937         };
938         
939         cfg.html = this.html || cfg.html;
940         
941         if (this.toolbar) {
942             cfg = {
943                 cls: 'btn-toolbar',
944                 html: null
945             };
946             
947             return cfg;
948         }
949         
950         if (['vertical','justified'].indexOf(this.align)!==-1) {
951             cfg.cls = 'btn-group-' + this.align;
952             
953             if (this.align == 'justified') {
954                 console.log(this.items);
955             }
956         }
957         
958         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
959             cfg.cls += ' btn-group-' + this.size;
960         }
961         
962         if (this.direction == 'up') {
963             cfg.cls += ' dropup' ;
964         }
965         
966         return cfg;
967     },
968     /**
969      * Add a button to the group (similar to NavItem API.)
970      */
971     addItem : function(cfg)
972     {
973         var cn = new Roo.bootstrap.Button(cfg);
974         //this.register(cn);
975         cn.parentId = this.id;
976         cn.onRender(this.el, null);
977         return cn;
978     }
979    
980 });
981
982  /*
983  * - LGPL
984  *
985  * button
986  * 
987  */
988
989 /**
990  * @class Roo.bootstrap.Button
991  * @extends Roo.bootstrap.Component
992  * Bootstrap Button class
993  * @cfg {String} html The button content
994  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
995  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
996  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
997  * @cfg {String} size (lg|sm|xs)
998  * @cfg {String} tag (a|input|submit)
999  * @cfg {String} href empty or href
1000  * @cfg {Boolean} disabled default false;
1001  * @cfg {Boolean} isClose default false;
1002  * @cfg {String} glyphicon depricated - use fa
1003  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1004  * @cfg {String} badge text for badge
1005  * @cfg {String} theme (default|glow)  
1006  * @cfg {Boolean} inverse dark themed version
1007  * @cfg {Boolean} toggle is it a slidy toggle button
1008  * @cfg {Boolean} pressed   default null - if the button ahs active state
1009  * @cfg {String} ontext text for on slidy toggle state
1010  * @cfg {String} offtext text for off slidy toggle state
1011  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1012  * @cfg {Boolean} removeClass remove the standard class..
1013  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1014  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1015  * 
1016  * @constructor
1017  * Create a new button
1018  * @param {Object} config The config object
1019  */
1020
1021
1022 Roo.bootstrap.Button = function(config){
1023     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1024     
1025     this.addEvents({
1026         // raw events
1027         /**
1028          * @event click
1029          * When a button is pressed
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "click" : true,
1034         /**
1035          * @event dblclick
1036          * When a button is double clicked
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          */
1040         "dblclick" : true,
1041          /**
1042          * @event toggle
1043          * After the button has been toggles
1044          * @param {Roo.bootstrap.Button} btn
1045          * @param {Roo.EventObject} e
1046          * @param {boolean} pressed (also available as button.pressed)
1047          */
1048         "toggle" : true
1049     });
1050 };
1051
1052 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1053     html: false,
1054     active: false,
1055     weight: '',
1056     badge_weight: '',
1057     outline : false,
1058     size: '',
1059     tag: 'button',
1060     href: '',
1061     disabled: false,
1062     isClose: false,
1063     glyphicon: '',
1064     fa: '',
1065     badge: '',
1066     theme: 'default',
1067     inverse: false,
1068     
1069     toggle: false,
1070     ontext: 'ON',
1071     offtext: 'OFF',
1072     defaulton: true,
1073     preventDefault: true,
1074     removeClass: false,
1075     name: false,
1076     target: false,
1077     group : false,
1078      
1079     pressed : null,
1080      
1081     
1082     getAutoCreate : function(){
1083         
1084         var cfg = {
1085             tag : 'button',
1086             cls : 'roo-button',
1087             html: ''
1088         };
1089         
1090         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1091             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1092             this.tag = 'button';
1093         } else {
1094             cfg.tag = this.tag;
1095         }
1096         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1097         
1098         if (this.toggle == true) {
1099             cfg={
1100                 tag: 'div',
1101                 cls: 'slider-frame roo-button',
1102                 cn: [
1103                     {
1104                         tag: 'span',
1105                         'data-on-text':'ON',
1106                         'data-off-text':'OFF',
1107                         cls: 'slider-button',
1108                         html: this.offtext
1109                     }
1110                 ]
1111             };
1112             // why are we validating the weights?
1113             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1114                 cfg.cls +=  ' ' + this.weight;
1115             }
1116             
1117             return cfg;
1118         }
1119         
1120         if (this.isClose) {
1121             cfg.cls += ' close';
1122             
1123             cfg["aria-hidden"] = true;
1124             
1125             cfg.html = "&times;";
1126             
1127             return cfg;
1128         }
1129              
1130         
1131         if (this.theme==='default') {
1132             cfg.cls = 'btn roo-button';
1133             
1134             //if (this.parentType != 'Navbar') {
1135             this.weight = this.weight.length ?  this.weight : 'default';
1136             //}
1137             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1138                 
1139                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1140                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1141                 cfg.cls += ' btn-' + outline + weight;
1142                 if (this.weight == 'default') {
1143                     // BC
1144                     cfg.cls += ' btn-' + this.weight;
1145                 }
1146             }
1147         } else if (this.theme==='glow') {
1148             
1149             cfg.tag = 'a';
1150             cfg.cls = 'btn-glow roo-button';
1151             
1152             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1153                 
1154                 cfg.cls += ' ' + this.weight;
1155             }
1156         }
1157    
1158         
1159         if (this.inverse) {
1160             this.cls += ' inverse';
1161         }
1162         
1163         
1164         if (this.active || this.pressed === true) {
1165             cfg.cls += ' active';
1166         }
1167         
1168         if (this.disabled) {
1169             cfg.disabled = 'disabled';
1170         }
1171         
1172         if (this.items) {
1173             Roo.log('changing to ul' );
1174             cfg.tag = 'ul';
1175             this.glyphicon = 'caret';
1176             if (Roo.bootstrap.version == 4) {
1177                 this.fa = 'caret-down';
1178             }
1179             
1180         }
1181         
1182         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1183          
1184         //gsRoo.log(this.parentType);
1185         if (this.parentType === 'Navbar' && !this.parent().bar) {
1186             Roo.log('changing to li?');
1187             
1188             cfg.tag = 'li';
1189             
1190             cfg.cls = '';
1191             cfg.cn =  [{
1192                 tag : 'a',
1193                 cls : 'roo-button',
1194                 html : this.html,
1195                 href : this.href || '#'
1196             }];
1197             if (this.menu) {
1198                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1199                 cfg.cls += ' dropdown';
1200             }   
1201             
1202             delete cfg.html;
1203             
1204         }
1205         
1206        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1207         
1208         if (this.glyphicon) {
1209             cfg.html = ' ' + cfg.html;
1210             
1211             cfg.cn = [
1212                 {
1213                     tag: 'span',
1214                     cls: 'glyphicon glyphicon-' + this.glyphicon
1215                 }
1216             ];
1217         }
1218         if (this.fa) {
1219             cfg.html = ' ' + cfg.html;
1220             
1221             cfg.cn = [
1222                 {
1223                     tag: 'i',
1224                     cls: 'fa fas fa-' + this.fa
1225                 }
1226             ];
1227         }
1228         
1229         if (this.badge) {
1230             cfg.html += ' ';
1231             
1232             cfg.tag = 'a';
1233             
1234 //            cfg.cls='btn roo-button';
1235             
1236             cfg.href=this.href;
1237             
1238             var value = cfg.html;
1239             
1240             if(this.glyphicon){
1241                 value = {
1242                     tag: 'span',
1243                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1244                     html: this.html
1245                 };
1246             }
1247             if(this.fa){
1248                 value = {
1249                     tag: 'i',
1250                     cls: 'fa fas fa-' + this.fa,
1251                     html: this.html
1252                 };
1253             }
1254             
1255             var bw = this.badge_weight.length ? this.badge_weight :
1256                 (this.weight.length ? this.weight : 'secondary');
1257             bw = bw == 'default' ? 'secondary' : bw;
1258             
1259             cfg.cn = [
1260                 value,
1261                 {
1262                     tag: 'span',
1263                     cls: 'badge badge-' + bw,
1264                     html: this.badge
1265                 }
1266             ];
1267             
1268             cfg.html='';
1269         }
1270         
1271         if (this.menu) {
1272             cfg.cls += ' dropdown';
1273             cfg.html = typeof(cfg.html) != 'undefined' ?
1274                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1275         }
1276         
1277         if (cfg.tag !== 'a' && this.href !== '') {
1278             throw "Tag must be a to set href.";
1279         } else if (this.href.length > 0) {
1280             cfg.href = this.href;
1281         }
1282         
1283         if(this.removeClass){
1284             cfg.cls = '';
1285         }
1286         
1287         if(this.target){
1288             cfg.target = this.target;
1289         }
1290         
1291         return cfg;
1292     },
1293     initEvents: function() {
1294        // Roo.log('init events?');
1295 //        Roo.log(this.el.dom);
1296         // add the menu...
1297         
1298         if (typeof (this.menu) != 'undefined') {
1299             this.menu.parentType = this.xtype;
1300             this.menu.triggerEl = this.el;
1301             this.addxtype(Roo.apply({}, this.menu));
1302         }
1303
1304
1305         if (this.el.hasClass('roo-button')) {
1306              this.el.on('click', this.onClick, this);
1307              this.el.on('dblclick', this.onDblClick, this);
1308         } else {
1309              this.el.select('.roo-button').on('click', this.onClick, this);
1310              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1311              
1312         }
1313         // why?
1314         if(this.removeClass){
1315             this.el.on('click', this.onClick, this);
1316         }
1317         
1318         if (this.group === true) {
1319              if (this.pressed === false || this.pressed === true) {
1320                 // nothing
1321             } else {
1322                 this.pressed = false;
1323                 this.setActive(this.pressed);
1324             }
1325             
1326         }
1327         
1328         this.el.enableDisplayMode();
1329         
1330     },
1331     onClick : function(e)
1332     {
1333         if (this.disabled) {
1334             return;
1335         }
1336         
1337         Roo.log('button on click ');
1338         if(this.preventDefault){
1339             e.preventDefault();
1340         }
1341         
1342         if (this.group) {
1343             if (this.pressed) {
1344                 // do nothing -
1345                 return;
1346             }
1347             this.setActive(true);
1348             var pi = this.parent().items;
1349             for (var i = 0;i < pi.length;i++) {
1350                 if (this == pi[i]) {
1351                     continue;
1352                 }
1353                 if (pi[i].el.hasClass('roo-button')) {
1354                     pi[i].setActive(false);
1355                 }
1356             }
1357             this.fireEvent('click', this, e);            
1358             return;
1359         }
1360         
1361         if (this.pressed === true || this.pressed === false) {
1362             this.toggleActive(e);
1363         }
1364         
1365         
1366         this.fireEvent('click', this, e);
1367     },
1368     onDblClick: function(e)
1369     {
1370         if (this.disabled) {
1371             return;
1372         }
1373         if(this.preventDefault){
1374             e.preventDefault();
1375         }
1376         this.fireEvent('dblclick', this, e);
1377     },
1378     /**
1379      * Enables this button
1380      */
1381     enable : function()
1382     {
1383         this.disabled = false;
1384         this.el.removeClass('disabled');
1385         this.el.dom.removeAttribute("disabled");
1386     },
1387     
1388     /**
1389      * Disable this button
1390      */
1391     disable : function()
1392     {
1393         this.disabled = true;
1394         this.el.addClass('disabled');
1395         this.el.attr("disabled", "disabled")
1396     },
1397      /**
1398      * sets the active state on/off, 
1399      * @param {Boolean} state (optional) Force a particular state
1400      */
1401     setActive : function(v) {
1402         
1403         this.el[v ? 'addClass' : 'removeClass']('active');
1404         this.pressed = v;
1405     },
1406      /**
1407      * toggles the current active state 
1408      */
1409     toggleActive : function(e)
1410     {
1411         this.setActive(!this.pressed); // this modifies pressed...
1412         this.fireEvent('toggle', this, e, this.pressed);
1413     },
1414      /**
1415      * get the current active state
1416      * @return {boolean} true if it's active
1417      */
1418     isActive : function()
1419     {
1420         return this.el.hasClass('active');
1421     },
1422     /**
1423      * set the text of the first selected button
1424      */
1425     setText : function(str)
1426     {
1427         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1428     },
1429     /**
1430      * get the text of the first selected button
1431      */
1432     getText : function()
1433     {
1434         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1435     },
1436     
1437     setWeight : function(str)
1438     {
1439         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1440         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1441         this.weight = str;
1442         var outline = this.outline ? 'outline-' : '';
1443         if (str == 'default') {
1444             this.el.addClass('btn-default btn-outline-secondary');        
1445             return;
1446         }
1447         this.el.addClass('btn-' + outline + str);        
1448     }
1449     
1450     
1451 });
1452 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1453
1454 Roo.bootstrap.Button.weights = [
1455     'default',
1456     'secondary' ,
1457     'primary',
1458     'success',
1459     'info',
1460     'warning',
1461     'danger',
1462     'link',
1463     'light',
1464     'dark'              
1465    
1466 ];/*
1467  * - LGPL
1468  *
1469  * column
1470  * 
1471  */
1472
1473 /**
1474  * @class Roo.bootstrap.Column
1475  * @extends Roo.bootstrap.Component
1476  * @children Roo.bootstrap.Component
1477  * Bootstrap Column class
1478  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1479  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1480  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1481  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1482  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1483  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1484  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1485  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1486  *
1487  * 
1488  * @cfg {Boolean} hidden (true|false) hide the element
1489  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1490  * @cfg {String} fa (ban|check|...) font awesome icon
1491  * @cfg {Number} fasize (1|2|....) font awsome size
1492
1493  * @cfg {String} icon (info-sign|check|...) glyphicon name
1494
1495  * @cfg {String} html content of column.
1496  * 
1497  * @constructor
1498  * Create a new Column
1499  * @param {Object} config The config object
1500  */
1501
1502 Roo.bootstrap.Column = function(config){
1503     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1504 };
1505
1506 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1507     
1508     xs: false,
1509     sm: false,
1510     md: false,
1511     lg: false,
1512     xsoff: false,
1513     smoff: false,
1514     mdoff: false,
1515     lgoff: false,
1516     html: '',
1517     offset: 0,
1518     alert: false,
1519     fa: false,
1520     icon : false,
1521     hidden : false,
1522     fasize : 1,
1523     
1524     getAutoCreate : function(){
1525         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1526         
1527         cfg = {
1528             tag: 'div',
1529             cls: 'column'
1530         };
1531         
1532         var settings=this;
1533         var sizes =   ['xs','sm','md','lg'];
1534         sizes.map(function(size ,ix){
1535             //Roo.log( size + ':' + settings[size]);
1536             
1537             if (settings[size+'off'] !== false) {
1538                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1539             }
1540             
1541             if (settings[size] === false) {
1542                 return;
1543             }
1544             
1545             if (!settings[size]) { // 0 = hidden
1546                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1547                 // bootsrap4
1548                 for (var i = ix; i > -1; i--) {
1549                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1550                 }
1551                 
1552                 
1553                 return;
1554             }
1555             cfg.cls += ' col-' + size + '-' + settings[size] + (
1556                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1557             );
1558             
1559         });
1560         
1561         if (this.hidden) {
1562             cfg.cls += ' hidden';
1563         }
1564         
1565         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1566             cfg.cls +=' alert alert-' + this.alert;
1567         }
1568         
1569         
1570         if (this.html.length) {
1571             cfg.html = this.html;
1572         }
1573         if (this.fa) {
1574             var fasize = '';
1575             if (this.fasize > 1) {
1576                 fasize = ' fa-' + this.fasize + 'x';
1577             }
1578             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1579             
1580             
1581         }
1582         if (this.icon) {
1583             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1584         }
1585         
1586         return cfg;
1587     }
1588    
1589 });
1590
1591  
1592
1593  /*
1594  * - LGPL
1595  *
1596  * page container.
1597  * 
1598  */
1599
1600
1601 /**
1602  * @class Roo.bootstrap.Container
1603  * @extends Roo.bootstrap.Component
1604  * @builder-top
1605  * @children Roo.bootstrap.Component
1606  * Bootstrap Container class
1607  * @cfg {Boolean} jumbotron is it a jumbotron element
1608  * @cfg {String} html content of element
1609  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1610  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1611  * @cfg {String} header content of header (for panel)
1612  * @cfg {String} footer content of footer (for panel)
1613  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1614  * @cfg {String} tag (header|aside|section) type of HTML tag.
1615  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1616  * @cfg {String} fa font awesome icon
1617  * @cfg {String} icon (info-sign|check|...) glyphicon name
1618  * @cfg {Boolean} hidden (true|false) hide the element
1619  * @cfg {Boolean} expandable (true|false) default false
1620  * @cfg {Boolean} expanded (true|false) default true
1621  * @cfg {String} rheader contet on the right of header
1622  * @cfg {Boolean} clickable (true|false) default false
1623
1624  *     
1625  * @constructor
1626  * Create a new Container
1627  * @param {Object} config The config object
1628  */
1629
1630 Roo.bootstrap.Container = function(config){
1631     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1632     
1633     this.addEvents({
1634         // raw events
1635          /**
1636          * @event expand
1637          * After the panel has been expand
1638          * 
1639          * @param {Roo.bootstrap.Container} this
1640          */
1641         "expand" : true,
1642         /**
1643          * @event collapse
1644          * After the panel has been collapsed
1645          * 
1646          * @param {Roo.bootstrap.Container} this
1647          */
1648         "collapse" : true,
1649         /**
1650          * @event click
1651          * When a element is chick
1652          * @param {Roo.bootstrap.Container} this
1653          * @param {Roo.EventObject} e
1654          */
1655         "click" : true
1656     });
1657 };
1658
1659 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1660     
1661     jumbotron : false,
1662     well: '',
1663     panel : '',
1664     header: '',
1665     footer : '',
1666     sticky: '',
1667     tag : false,
1668     alert : false,
1669     fa: false,
1670     icon : false,
1671     expandable : false,
1672     rheader : '',
1673     expanded : true,
1674     clickable: false,
1675   
1676      
1677     getChildContainer : function() {
1678         
1679         if(!this.el){
1680             return false;
1681         }
1682         
1683         if (this.panel.length) {
1684             return this.el.select('.panel-body',true).first();
1685         }
1686         
1687         return this.el;
1688     },
1689     
1690     
1691     getAutoCreate : function(){
1692         
1693         var cfg = {
1694             tag : this.tag || 'div',
1695             html : '',
1696             cls : ''
1697         };
1698         if (this.jumbotron) {
1699             cfg.cls = 'jumbotron';
1700         }
1701         
1702         
1703         
1704         // - this is applied by the parent..
1705         //if (this.cls) {
1706         //    cfg.cls = this.cls + '';
1707         //}
1708         
1709         if (this.sticky.length) {
1710             
1711             var bd = Roo.get(document.body);
1712             if (!bd.hasClass('bootstrap-sticky')) {
1713                 bd.addClass('bootstrap-sticky');
1714                 Roo.select('html',true).setStyle('height', '100%');
1715             }
1716              
1717             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1718         }
1719         
1720         
1721         if (this.well.length) {
1722             switch (this.well) {
1723                 case 'lg':
1724                 case 'sm':
1725                     cfg.cls +=' well well-' +this.well;
1726                     break;
1727                 default:
1728                     cfg.cls +=' well';
1729                     break;
1730             }
1731         }
1732         
1733         if (this.hidden) {
1734             cfg.cls += ' hidden';
1735         }
1736         
1737         
1738         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1739             cfg.cls +=' alert alert-' + this.alert;
1740         }
1741         
1742         var body = cfg;
1743         
1744         if (this.panel.length) {
1745             cfg.cls += ' panel panel-' + this.panel;
1746             cfg.cn = [];
1747             if (this.header.length) {
1748                 
1749                 var h = [];
1750                 
1751                 if(this.expandable){
1752                     
1753                     cfg.cls = cfg.cls + ' expandable';
1754                     
1755                     h.push({
1756                         tag: 'i',
1757                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1758                     });
1759                     
1760                 }
1761                 
1762                 h.push(
1763                     {
1764                         tag: 'span',
1765                         cls : 'panel-title',
1766                         html : (this.expandable ? '&nbsp;' : '') + this.header
1767                     },
1768                     {
1769                         tag: 'span',
1770                         cls: 'panel-header-right',
1771                         html: this.rheader
1772                     }
1773                 );
1774                 
1775                 cfg.cn.push({
1776                     cls : 'panel-heading',
1777                     style : this.expandable ? 'cursor: pointer' : '',
1778                     cn : h
1779                 });
1780                 
1781             }
1782             
1783             body = false;
1784             cfg.cn.push({
1785                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1786                 html : this.html
1787             });
1788             
1789             
1790             if (this.footer.length) {
1791                 cfg.cn.push({
1792                     cls : 'panel-footer',
1793                     html : this.footer
1794                     
1795                 });
1796             }
1797             
1798         }
1799         
1800         if (body) {
1801             body.html = this.html || cfg.html;
1802             // prefix with the icons..
1803             if (this.fa) {
1804                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1805             }
1806             if (this.icon) {
1807                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1808             }
1809             
1810             
1811         }
1812         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1813             cfg.cls =  'container';
1814         }
1815         
1816         return cfg;
1817     },
1818     
1819     initEvents: function() 
1820     {
1821         if(this.expandable){
1822             var headerEl = this.headerEl();
1823         
1824             if(headerEl){
1825                 headerEl.on('click', this.onToggleClick, this);
1826             }
1827         }
1828         
1829         if(this.clickable){
1830             this.el.on('click', this.onClick, this);
1831         }
1832         
1833     },
1834     
1835     onToggleClick : function()
1836     {
1837         var headerEl = this.headerEl();
1838         
1839         if(!headerEl){
1840             return;
1841         }
1842         
1843         if(this.expanded){
1844             this.collapse();
1845             return;
1846         }
1847         
1848         this.expand();
1849     },
1850     
1851     expand : function()
1852     {
1853         if(this.fireEvent('expand', this)) {
1854             
1855             this.expanded = true;
1856             
1857             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1858             
1859             this.el.select('.panel-body',true).first().removeClass('hide');
1860             
1861             var toggleEl = this.toggleEl();
1862
1863             if(!toggleEl){
1864                 return;
1865             }
1866
1867             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1868         }
1869         
1870     },
1871     
1872     collapse : function()
1873     {
1874         if(this.fireEvent('collapse', this)) {
1875             
1876             this.expanded = false;
1877             
1878             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1879             this.el.select('.panel-body',true).first().addClass('hide');
1880         
1881             var toggleEl = this.toggleEl();
1882
1883             if(!toggleEl){
1884                 return;
1885             }
1886
1887             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1888         }
1889     },
1890     
1891     toggleEl : function()
1892     {
1893         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1894             return;
1895         }
1896         
1897         return this.el.select('.panel-heading .fa',true).first();
1898     },
1899     
1900     headerEl : function()
1901     {
1902         if(!this.el || !this.panel.length || !this.header.length){
1903             return;
1904         }
1905         
1906         return this.el.select('.panel-heading',true).first()
1907     },
1908     
1909     bodyEl : function()
1910     {
1911         if(!this.el || !this.panel.length){
1912             return;
1913         }
1914         
1915         return this.el.select('.panel-body',true).first()
1916     },
1917     
1918     titleEl : function()
1919     {
1920         if(!this.el || !this.panel.length || !this.header.length){
1921             return;
1922         }
1923         
1924         return this.el.select('.panel-title',true).first();
1925     },
1926     
1927     setTitle : function(v)
1928     {
1929         var titleEl = this.titleEl();
1930         
1931         if(!titleEl){
1932             return;
1933         }
1934         
1935         titleEl.dom.innerHTML = v;
1936     },
1937     
1938     getTitle : function()
1939     {
1940         
1941         var titleEl = this.titleEl();
1942         
1943         if(!titleEl){
1944             return '';
1945         }
1946         
1947         return titleEl.dom.innerHTML;
1948     },
1949     
1950     setRightTitle : function(v)
1951     {
1952         var t = this.el.select('.panel-header-right',true).first();
1953         
1954         if(!t){
1955             return;
1956         }
1957         
1958         t.dom.innerHTML = v;
1959     },
1960     
1961     onClick : function(e)
1962     {
1963         e.preventDefault();
1964         
1965         this.fireEvent('click', this, e);
1966     }
1967 });
1968
1969  /*
1970  *  - LGPL
1971  *
1972  *  This is BS4's Card element.. - similar to our containers probably..
1973  * 
1974  */
1975 /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * Bootstrap Card class
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * Bootstrap CardHeader class
2781  * @constructor
2782  * Create a new Card Header - that you can embed children into
2783  * @param {Object} config The config object
2784  */
2785
2786 Roo.bootstrap.CardHeader = function(config){
2787     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2788 };
2789
2790 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2791     
2792     
2793     container_method : 'getCardHeader' 
2794     
2795      
2796     
2797     
2798    
2799 });
2800
2801  
2802
2803  /*
2804  * - LGPL
2805  *
2806  * Card footer - holder for the card footer elements.
2807  * 
2808  */
2809
2810 /**
2811  * @class Roo.bootstrap.CardFooter
2812  * @extends Roo.bootstrap.Element
2813  * Bootstrap CardFooter class
2814  * @constructor
2815  * Create a new Card Footer - that you can embed children into
2816  * @param {Object} config The config object
2817  */
2818
2819 Roo.bootstrap.CardFooter = function(config){
2820     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2821 };
2822
2823 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2824     
2825     
2826     container_method : 'getCardFooter' 
2827     
2828      
2829     
2830     
2831    
2832 });
2833
2834  
2835
2836  /*
2837  * - LGPL
2838  *
2839  * Card header - holder for the card header elements.
2840  * 
2841  */
2842
2843 /**
2844  * @class Roo.bootstrap.CardImageTop
2845  * @extends Roo.bootstrap.Element
2846  * Bootstrap CardImageTop class
2847  * @constructor
2848  * Create a new Card Image Top container
2849  * @param {Object} config The config object
2850  */
2851
2852 Roo.bootstrap.CardImageTop = function(config){
2853     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2854 };
2855
2856 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2857     
2858    
2859     container_method : 'getCardImageTop' 
2860     
2861      
2862     
2863    
2864 });
2865
2866  
2867
2868  
2869 /*
2870 * Licence: LGPL
2871 */
2872
2873 /**
2874  * @class Roo.bootstrap.ButtonUploader
2875  * @extends Roo.bootstrap.Button
2876  * Bootstrap Button Uploader class - it's a button which when you add files to it
2877  *
2878  * 
2879  * @cfg {Number} errorTimeout default 3000
2880  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2881  * @cfg {Array}  html The button text.
2882  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2883  *
2884  * @constructor
2885  * Create a new CardUploader
2886  * @param {Object} config The config object
2887  */
2888
2889 Roo.bootstrap.ButtonUploader = function(config){
2890     
2891  
2892     
2893     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2894     
2895      
2896      this.addEvents({
2897          // raw events
2898         /**
2899          * @event beforeselect
2900          * When button is pressed, before show upload files dialog is shown
2901          * @param {Roo.bootstrap.UploaderButton} this
2902          *
2903          */
2904         'beforeselect' : true,
2905          /**
2906          * @event fired when files have been selected, 
2907          * When a the download link is clicked
2908          * @param {Roo.bootstrap.UploaderButton} this
2909          * @param {Array} Array of files that have been uploaded
2910          */
2911         'uploaded' : true
2912         
2913     });
2914 };
2915  
2916 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2917     
2918      
2919     errorTimeout : 3000,
2920      
2921     images : false,
2922    
2923     fileCollection : false,
2924     allowBlank : true,
2925     
2926     multiple : true,
2927     
2928     getAutoCreate : function()
2929     {
2930         var im = {
2931             tag: 'input',
2932             type : 'file',
2933             cls : 'd-none  roo-card-upload-selector' 
2934           
2935         };
2936         if (this.multiple) {
2937             im.multiple = 'multiple';
2938         }
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2944                 im
2945
2946             ]
2947         };
2948            
2949          
2950     },
2951      
2952    
2953     initEvents : function()
2954     {
2955         
2956         Roo.bootstrap.Button.prototype.initEvents.call(this);
2957         
2958         
2959         
2960         
2961         
2962         this.urlAPI = (window.createObjectURL && window) || 
2963                                 (window.URL && URL.revokeObjectURL && URL) || 
2964                                 (window.webkitURL && webkitURL);
2965                         
2966          
2967          
2968          
2969         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2970         
2971         this.selectorEl.on('change', this.onFileSelected, this);
2972          
2973          
2974        
2975     },
2976     
2977    
2978     onClick : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if ( this.fireEvent('beforeselect', this) === false) {
2983             return;
2984         }
2985          
2986         this.selectorEl.dom.click();
2987          
2988     },
2989     
2990     onFileSelected : function(e)
2991     {
2992         e.preventDefault();
2993         
2994         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2995             return;
2996         }
2997         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2998         this.selectorEl.dom.value  = '';// hopefully reset..
2999         
3000         this.fireEvent('uploaded', this,  files );
3001         
3002     },
3003     
3004        
3005    
3006     
3007     /**
3008      * addCard - add an Attachment to the uploader
3009      * @param data - the data about the image to upload
3010      *
3011      * {
3012           id : 123
3013           title : "Title of file",
3014           is_uploaded : false,
3015           src : "http://.....",
3016           srcfile : { the File upload object },
3017           mimetype : file.type,
3018           preview : false,
3019           is_deleted : 0
3020           .. any other data...
3021         }
3022      *
3023      * 
3024     */
3025      
3026     reset: function()
3027     {
3028          
3029          this.selectorEl
3030     } 
3031     
3032     
3033     
3034     
3035 });
3036  /*
3037  * - LGPL
3038  *
3039  * image
3040  * 
3041  */
3042
3043
3044 /**
3045  * @class Roo.bootstrap.Img
3046  * @extends Roo.bootstrap.Component
3047  * Bootstrap Img class
3048  * @cfg {Boolean} imgResponsive false | true
3049  * @cfg {String} border rounded | circle | thumbnail
3050  * @cfg {String} src image source
3051  * @cfg {String} alt image alternative text
3052  * @cfg {String} href a tag href
3053  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3054  * @cfg {String} xsUrl xs image source
3055  * @cfg {String} smUrl sm image source
3056  * @cfg {String} mdUrl md image source
3057  * @cfg {String} lgUrl lg image source
3058  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3059  * 
3060  * @constructor
3061  * Create a new Input
3062  * @param {Object} config The config object
3063  */
3064
3065 Roo.bootstrap.Img = function(config){
3066     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3067     
3068     this.addEvents({
3069         // img events
3070         /**
3071          * @event click
3072          * The img click event for the img.
3073          * @param {Roo.EventObject} e
3074          */
3075         "click" : true,
3076         /**
3077          * @event load
3078          * The when any image loads
3079          * @param {Roo.EventObject} e
3080          */
3081         "load" : true
3082     });
3083 };
3084
3085 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3086     
3087     imgResponsive: true,
3088     border: '',
3089     src: 'about:blank',
3090     href: false,
3091     target: false,
3092     xsUrl: '',
3093     smUrl: '',
3094     mdUrl: '',
3095     lgUrl: '',
3096     backgroundContain : false,
3097
3098     getAutoCreate : function()
3099     {   
3100         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3101             return this.createSingleImg();
3102         }
3103         
3104         var cfg = {
3105             tag: 'div',
3106             cls: 'roo-image-responsive-group',
3107             cn: []
3108         };
3109         var _this = this;
3110         
3111         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3112             
3113             if(!_this[size + 'Url']){
3114                 return;
3115             }
3116             
3117             var img = {
3118                 tag: 'img',
3119                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3120                 html: _this.html || cfg.html,
3121                 src: _this[size + 'Url']
3122             };
3123             
3124             img.cls += ' roo-image-responsive-' + size;
3125             
3126             var s = ['xs', 'sm', 'md', 'lg'];
3127             
3128             s.splice(s.indexOf(size), 1);
3129             
3130             Roo.each(s, function(ss){
3131                 img.cls += ' hidden-' + ss;
3132             });
3133             
3134             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3135                 cfg.cls += ' img-' + _this.border;
3136             }
3137             
3138             if(_this.alt){
3139                 cfg.alt = _this.alt;
3140             }
3141             
3142             if(_this.href){
3143                 var a = {
3144                     tag: 'a',
3145                     href: _this.href,
3146                     cn: [
3147                         img
3148                     ]
3149                 };
3150
3151                 if(this.target){
3152                     a.target = _this.target;
3153                 }
3154             }
3155             
3156             cfg.cn.push((_this.href) ? a : img);
3157             
3158         });
3159         
3160         return cfg;
3161     },
3162     
3163     createSingleImg : function()
3164     {
3165         var cfg = {
3166             tag: 'img',
3167             cls: (this.imgResponsive) ? 'img-responsive' : '',
3168             html : null,
3169             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3170         };
3171         
3172         if (this.backgroundContain) {
3173             cfg.cls += ' background-contain';
3174         }
3175         
3176         cfg.html = this.html || cfg.html;
3177         
3178         if (this.backgroundContain) {
3179             cfg.style="background-image: url(" + this.src + ')';
3180         } else {
3181             cfg.src = this.src || cfg.src;
3182         }
3183         
3184         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3185             cfg.cls += ' img-' + this.border;
3186         }
3187         
3188         if(this.alt){
3189             cfg.alt = this.alt;
3190         }
3191         
3192         if(this.href){
3193             var a = {
3194                 tag: 'a',
3195                 href: this.href,
3196                 cn: [
3197                     cfg
3198                 ]
3199             };
3200             
3201             if(this.target){
3202                 a.target = this.target;
3203             }
3204             
3205         }
3206         
3207         return (this.href) ? a : cfg;
3208     },
3209     
3210     initEvents: function() 
3211     {
3212         if(!this.href){
3213             this.el.on('click', this.onClick, this);
3214         }
3215         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3216             this.el.on('load', this.onImageLoad, this);
3217         } else {
3218             // not sure if this works.. not tested
3219             this.el.select('img', true).on('load', this.onImageLoad, this);
3220         }
3221         
3222     },
3223     
3224     onClick : function(e)
3225     {
3226         Roo.log('img onclick');
3227         this.fireEvent('click', this, e);
3228     },
3229     onImageLoad: function(e)
3230     {
3231         Roo.log('img load');
3232         this.fireEvent('load', this, e);
3233     },
3234     
3235     /**
3236      * Sets the url of the image - used to update it
3237      * @param {String} url the url of the image
3238      */
3239     
3240     setSrc : function(url)
3241     {
3242         this.src =  url;
3243         
3244         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3245             if (this.backgroundContain) {
3246                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3247             } else {
3248                 this.el.dom.src =  url;
3249             }
3250             return;
3251         }
3252         
3253         this.el.select('img', true).first().dom.src =  url;
3254     }
3255     
3256     
3257    
3258 });
3259
3260  /*
3261  * - LGPL
3262  *
3263  * image
3264  * 
3265  */
3266
3267
3268 /**
3269  * @class Roo.bootstrap.Link
3270  * @extends Roo.bootstrap.Component
3271  * Bootstrap Link Class
3272  * @cfg {String} alt image alternative text
3273  * @cfg {String} href a tag href
3274  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3275  * @cfg {String} html the content of the link.
3276  * @cfg {String} anchor name for the anchor link
3277  * @cfg {String} fa - favicon
3278
3279  * @cfg {Boolean} preventDefault (true | false) default false
3280
3281  * 
3282  * @constructor
3283  * Create a new Input
3284  * @param {Object} config The config object
3285  */
3286
3287 Roo.bootstrap.Link = function(config){
3288     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3289     
3290     this.addEvents({
3291         // img events
3292         /**
3293          * @event click
3294          * The img click event for the img.
3295          * @param {Roo.EventObject} e
3296          */
3297         "click" : true
3298     });
3299 };
3300
3301 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3302     
3303     href: false,
3304     target: false,
3305     preventDefault: false,
3306     anchor : false,
3307     alt : false,
3308     fa: false,
3309
3310
3311     getAutoCreate : function()
3312     {
3313         var html = this.html || '';
3314         
3315         if (this.fa !== false) {
3316             html = '<i class="fa fa-' + this.fa + '"></i>';
3317         }
3318         var cfg = {
3319             tag: 'a'
3320         };
3321         // anchor's do not require html/href...
3322         if (this.anchor === false) {
3323             cfg.html = html;
3324             cfg.href = this.href || '#';
3325         } else {
3326             cfg.name = this.anchor;
3327             if (this.html !== false || this.fa !== false) {
3328                 cfg.html = html;
3329             }
3330             if (this.href !== false) {
3331                 cfg.href = this.href;
3332             }
3333         }
3334         
3335         if(this.alt !== false){
3336             cfg.alt = this.alt;
3337         }
3338         
3339         
3340         if(this.target !== false) {
3341             cfg.target = this.target;
3342         }
3343         
3344         return cfg;
3345     },
3346     
3347     initEvents: function() {
3348         
3349         if(!this.href || this.preventDefault){
3350             this.el.on('click', this.onClick, this);
3351         }
3352     },
3353     
3354     onClick : function(e)
3355     {
3356         if(this.preventDefault){
3357             e.preventDefault();
3358         }
3359         //Roo.log('img onclick');
3360         this.fireEvent('click', this, e);
3361     }
3362    
3363 });
3364
3365  /*
3366  * - LGPL
3367  *
3368  * header
3369  * 
3370  */
3371
3372 /**
3373  * @class Roo.bootstrap.Header
3374  * @extends Roo.bootstrap.Component
3375  * Bootstrap Header class
3376  * @cfg {String} html content of header
3377  * @cfg {Number} level (1|2|3|4|5|6) default 1
3378  * 
3379  * @constructor
3380  * Create a new Header
3381  * @param {Object} config The config object
3382  */
3383
3384
3385 Roo.bootstrap.Header  = function(config){
3386     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3387 };
3388
3389 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3390     
3391     //href : false,
3392     html : false,
3393     level : 1,
3394     
3395     
3396     
3397     getAutoCreate : function(){
3398         
3399         
3400         
3401         var cfg = {
3402             tag: 'h' + (1 *this.level),
3403             html: this.html || ''
3404         } ;
3405         
3406         return cfg;
3407     }
3408    
3409 });
3410
3411  
3412
3413  /*
3414  * Based on:
3415  * Ext JS Library 1.1.1
3416  * Copyright(c) 2006-2007, Ext JS, LLC.
3417  *
3418  * Originally Released Under LGPL - original licence link has changed is not relivant.
3419  *
3420  * Fork - LGPL
3421  * <script type="text/javascript">
3422  */
3423  
3424 /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3427  * @singleton
3428  */
3429 Roo.bootstrap.MenuMgr = function(){
3430    var menus, active, groups = {}, attached = false, lastShow = new Date();
3431
3432    // private - called when first menu is created
3433    function init(){
3434        menus = {};
3435        active = new Roo.util.MixedCollection();
3436        Roo.get(document).addKeyListener(27, function(){
3437            if(active.length > 0){
3438                hideAll();
3439            }
3440        });
3441    }
3442
3443    // private
3444    function hideAll(){
3445        if(active && active.length > 0){
3446            var c = active.clone();
3447            c.each(function(m){
3448                m.hide();
3449            });
3450        }
3451    }
3452
3453    // private
3454    function onHide(m){
3455        active.remove(m);
3456        if(active.length < 1){
3457            Roo.get(document).un("mouseup", onMouseDown);
3458             
3459            attached = false;
3460        }
3461    }
3462
3463    // private
3464    function onShow(m){
3465        var last = active.last();
3466        lastShow = new Date();
3467        active.add(m);
3468        if(!attached){
3469           Roo.get(document).on("mouseup", onMouseDown);
3470            
3471            attached = true;
3472        }
3473        if(m.parentMenu){
3474           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3475           m.parentMenu.activeChild = m;
3476        }else if(last && last.isVisible()){
3477           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3478        }
3479    }
3480
3481    // private
3482    function onBeforeHide(m){
3483        if(m.activeChild){
3484            m.activeChild.hide();
3485        }
3486        if(m.autoHideTimer){
3487            clearTimeout(m.autoHideTimer);
3488            delete m.autoHideTimer;
3489        }
3490    }
3491
3492    // private
3493    function onBeforeShow(m){
3494        var pm = m.parentMenu;
3495        if(!pm && !m.allowOtherMenus){
3496            hideAll();
3497        }else if(pm && pm.activeChild && active != m){
3498            pm.activeChild.hide();
3499        }
3500    }
3501
3502    // private this should really trigger on mouseup..
3503    function onMouseDown(e){
3504         Roo.log("on Mouse Up");
3505         
3506         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3507             Roo.log("MenuManager hideAll");
3508             hideAll();
3509             e.stopEvent();
3510         }
3511         
3512         
3513    }
3514
3515    // private
3516    function onBeforeCheck(mi, state){
3517        if(state){
3518            var g = groups[mi.group];
3519            for(var i = 0, l = g.length; i < l; i++){
3520                if(g[i] != mi){
3521                    g[i].setChecked(false);
3522                }
3523            }
3524        }
3525    }
3526
3527    return {
3528
3529        /**
3530         * Hides all menus that are currently visible
3531         */
3532        hideAll : function(){
3533             hideAll();  
3534        },
3535
3536        // private
3537        register : function(menu){
3538            if(!menus){
3539                init();
3540            }
3541            menus[menu.id] = menu;
3542            menu.on("beforehide", onBeforeHide);
3543            menu.on("hide", onHide);
3544            menu.on("beforeshow", onBeforeShow);
3545            menu.on("show", onShow);
3546            var g = menu.group;
3547            if(g && menu.events["checkchange"]){
3548                if(!groups[g]){
3549                    groups[g] = [];
3550                }
3551                groups[g].push(menu);
3552                menu.on("checkchange", onCheck);
3553            }
3554        },
3555
3556         /**
3557          * Returns a {@link Roo.menu.Menu} object
3558          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3559          * be used to generate and return a new Menu instance.
3560          */
3561        get : function(menu){
3562            if(typeof menu == "string"){ // menu id
3563                return menus[menu];
3564            }else if(menu.events){  // menu instance
3565                return menu;
3566            }
3567            /*else if(typeof menu.length == 'number'){ // array of menu items?
3568                return new Roo.bootstrap.Menu({items:menu});
3569            }else{ // otherwise, must be a config
3570                return new Roo.bootstrap.Menu(menu);
3571            }
3572            */
3573            return false;
3574        },
3575
3576        // private
3577        unregister : function(menu){
3578            delete menus[menu.id];
3579            menu.un("beforehide", onBeforeHide);
3580            menu.un("hide", onHide);
3581            menu.un("beforeshow", onBeforeShow);
3582            menu.un("show", onShow);
3583            var g = menu.group;
3584            if(g && menu.events["checkchange"]){
3585                groups[g].remove(menu);
3586                menu.un("checkchange", onCheck);
3587            }
3588        },
3589
3590        // private
3591        registerCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                if(!groups[g]){
3595                    groups[g] = [];
3596                }
3597                groups[g].push(menuItem);
3598                menuItem.on("beforecheckchange", onBeforeCheck);
3599            }
3600        },
3601
3602        // private
3603        unregisterCheckable : function(menuItem){
3604            var g = menuItem.group;
3605            if(g){
3606                groups[g].remove(menuItem);
3607                menuItem.un("beforecheckchange", onBeforeCheck);
3608            }
3609        }
3610    };
3611 }();/*
3612  * - LGPL
3613  *
3614  * menu
3615  * 
3616  */
3617
3618 /**
3619  * @class Roo.bootstrap.Menu
3620  * @extends Roo.bootstrap.Component
3621  * Bootstrap Menu class - container for MenuItems
3622  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3623  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3624  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3625  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3626   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3627   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3628  
3629  * @constructor
3630  * Create a new Menu
3631  * @param {Object} config The config object
3632  */
3633
3634
3635 Roo.bootstrap.Menu = function(config){
3636     
3637     if (config.type == 'treeview') {
3638         // normally menu's are drawn attached to the document to handle layering etc..
3639         // however treeview (used by the docs menu is drawn into the parent element)
3640         this.container_method = 'getChildContainer'; 
3641     }
3642     
3643     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3644     if (this.registerMenu && this.type != 'treeview')  {
3645         Roo.bootstrap.MenuMgr.register(this);
3646     }
3647     
3648     
3649     this.addEvents({
3650         /**
3651          * @event beforeshow
3652          * Fires before this menu is displayed (return false to block)
3653          * @param {Roo.menu.Menu} this
3654          */
3655         beforeshow : true,
3656         /**
3657          * @event beforehide
3658          * Fires before this menu is hidden (return false to block)
3659          * @param {Roo.menu.Menu} this
3660          */
3661         beforehide : true,
3662         /**
3663          * @event show
3664          * Fires after this menu is displayed
3665          * @param {Roo.menu.Menu} this
3666          */
3667         show : true,
3668         /**
3669          * @event hide
3670          * Fires after this menu is hidden
3671          * @param {Roo.menu.Menu} this
3672          */
3673         hide : true,
3674         /**
3675          * @event click
3676          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3677          * @param {Roo.menu.Menu} this
3678          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3679          * @param {Roo.EventObject} e
3680          */
3681         click : true,
3682         /**
3683          * @event mouseover
3684          * Fires when the mouse is hovering over this menu
3685          * @param {Roo.menu.Menu} this
3686          * @param {Roo.EventObject} e
3687          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688          */
3689         mouseover : true,
3690         /**
3691          * @event mouseout
3692          * Fires when the mouse exits this menu
3693          * @param {Roo.menu.Menu} this
3694          * @param {Roo.EventObject} e
3695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3696          */
3697         mouseout : true,
3698         /**
3699          * @event itemclick
3700          * Fires when a menu item contained in this menu is clicked
3701          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3702          * @param {Roo.EventObject} e
3703          */
3704         itemclick: true
3705     });
3706     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3707 };
3708
3709 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3710     
3711    /// html : false,
3712    
3713     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3714     type: false,
3715     /**
3716      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717      */
3718     registerMenu : true,
3719     
3720     menuItems :false, // stores the menu items..
3721     
3722     hidden:true,
3723         
3724     parentMenu : false,
3725     
3726     stopEvent : true,
3727     
3728     isLink : false,
3729     
3730     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731     
3732     hideTrigger : false,
3733     
3734     align : 'tl-bl?',
3735     
3736     
3737     getChildContainer : function() {
3738         return this.el;  
3739     },
3740     
3741     getAutoCreate : function(){
3742          
3743         //if (['right'].indexOf(this.align)!==-1) {
3744         //    cfg.cn[1].cls += ' pull-right'
3745         //}
3746          
3747         var cfg = {
3748             tag : 'ul',
3749             cls : 'dropdown-menu shadow' ,
3750             style : 'z-index:1000'
3751             
3752         };
3753         
3754         if (this.type === 'submenu') {
3755             cfg.cls = 'submenu active';
3756         }
3757         if (this.type === 'treeview') {
3758             cfg.cls = 'treeview-menu';
3759         }
3760         
3761         return cfg;
3762     },
3763     initEvents : function() {
3764         
3765        // Roo.log("ADD event");
3766        // Roo.log(this.triggerEl.dom);
3767         if (this.triggerEl) {
3768             
3769             this.triggerEl.on('click', this.onTriggerClick, this);
3770             
3771             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772             
3773             if (!this.hideTrigger) {
3774                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3775                     // dropdown toggle on the 'a' in BS4?
3776                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777                 } else {
3778                     this.triggerEl.addClass('dropdown-toggle');
3779                 }
3780             }
3781         }
3782         
3783         if (Roo.isTouch) {
3784             this.el.on('touchstart'  , this.onTouch, this);
3785         }
3786         this.el.on('click' , this.onClick, this);
3787
3788         this.el.on("mouseover", this.onMouseOver, this);
3789         this.el.on("mouseout", this.onMouseOut, this);
3790         
3791     },
3792     
3793     findTargetItem : function(e)
3794     {
3795         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3796         if(!t){
3797             return false;
3798         }
3799         //Roo.log(t);         Roo.log(t.id);
3800         if(t && t.id){
3801             //Roo.log(this.menuitems);
3802             return this.menuitems.get(t.id);
3803             
3804             //return this.items.get(t.menuItemId);
3805         }
3806         
3807         return false;
3808     },
3809     
3810     onTouch : function(e) 
3811     {
3812         Roo.log("menu.onTouch");
3813         //e.stopEvent(); this make the user popdown broken
3814         this.onClick(e);
3815     },
3816     
3817     onClick : function(e)
3818     {
3819         Roo.log("menu.onClick");
3820         
3821         var t = this.findTargetItem(e);
3822         if(!t || t.isContainer){
3823             return;
3824         }
3825         Roo.log(e);
3826         /*
3827         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3828             if(t == this.activeItem && t.shouldDeactivate(e)){
3829                 this.activeItem.deactivate();
3830                 delete this.activeItem;
3831                 return;
3832             }
3833             if(t.canActivate){
3834                 this.setActiveItem(t, true);
3835             }
3836             return;
3837             
3838             
3839         }
3840         */
3841        
3842         Roo.log('pass click event');
3843         
3844         t.onClick(e);
3845         
3846         this.fireEvent("click", this, t, e);
3847         
3848         var _this = this;
3849         
3850         if(!t.href.length || t.href == '#'){
3851             (function() { _this.hide(); }).defer(100);
3852         }
3853         
3854     },
3855     
3856     onMouseOver : function(e){
3857         var t  = this.findTargetItem(e);
3858         //Roo.log(t);
3859         //if(t){
3860         //    if(t.canActivate && !t.disabled){
3861         //        this.setActiveItem(t, true);
3862         //    }
3863         //}
3864         
3865         this.fireEvent("mouseover", this, e, t);
3866     },
3867     isVisible : function(){
3868         return !this.hidden;
3869     },
3870     onMouseOut : function(e){
3871         var t  = this.findTargetItem(e);
3872         
3873         //if(t ){
3874         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3875         //        this.activeItem.deactivate();
3876         //        delete this.activeItem;
3877         //    }
3878         //}
3879         this.fireEvent("mouseout", this, e, t);
3880     },
3881     
3882     
3883     /**
3884      * Displays this menu relative to another element
3885      * @param {String/HTMLElement/Roo.Element} element The element to align to
3886      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3887      * the element (defaults to this.defaultAlign)
3888      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889      */
3890     show : function(el, pos, parentMenu)
3891     {
3892         if (false === this.fireEvent("beforeshow", this)) {
3893             Roo.log("show canceled");
3894             return;
3895         }
3896         this.parentMenu = parentMenu;
3897         if(!this.el){
3898             this.render();
3899         }
3900         this.el.addClass('show'); // show otherwise we do not know how big we are..
3901          
3902         var xy = this.el.getAlignToXY(el, pos);
3903         
3904         // bl-tl << left align  below
3905         // tl-bl << left align 
3906         
3907         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3908             // if it goes to far to the right.. -> align left.
3909             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3910         }
3911         if(xy[0] < 0){
3912             // was left align - go right?
3913             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3914         }
3915         
3916         // goes down the bottom
3917         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918            xy[1]  < 0 ){
3919             var a = this.align.replace('?', '').split('-');
3920             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3921             
3922         }
3923         
3924         this.showAt(  xy , parentMenu, false);
3925     },
3926      /**
3927      * Displays this menu at a specific xy position
3928      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3929      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930      */
3931     showAt : function(xy, parentMenu, /* private: */_e){
3932         this.parentMenu = parentMenu;
3933         if(!this.el){
3934             this.render();
3935         }
3936         if(_e !== false){
3937             this.fireEvent("beforeshow", this);
3938             //xy = this.el.adjustForConstraints(xy);
3939         }
3940         
3941         //this.el.show();
3942         this.hideMenuItems();
3943         this.hidden = false;
3944         if (this.triggerEl) {
3945             this.triggerEl.addClass('open');
3946         }
3947         
3948         this.el.addClass('show');
3949         
3950         
3951         
3952         // reassign x when hitting right
3953         
3954         // reassign y when hitting bottom
3955         
3956         // but the list may align on trigger left or trigger top... should it be a properity?
3957         
3958         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3959             this.el.setXY(xy);
3960         }
3961         
3962         this.focus();
3963         this.fireEvent("show", this);
3964     },
3965     
3966     focus : function(){
3967         return;
3968         if(!this.hidden){
3969             this.doFocus.defer(50, this);
3970         }
3971     },
3972
3973     doFocus : function(){
3974         if(!this.hidden){
3975             this.focusEl.focus();
3976         }
3977     },
3978
3979     /**
3980      * Hides this menu and optionally all parent menus
3981      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982      */
3983     hide : function(deep)
3984     {
3985         if (false === this.fireEvent("beforehide", this)) {
3986             Roo.log("hide canceled");
3987             return;
3988         }
3989         this.hideMenuItems();
3990         if(this.el && this.isVisible()){
3991            
3992             if(this.activeItem){
3993                 this.activeItem.deactivate();
3994                 this.activeItem = null;
3995             }
3996             if (this.triggerEl) {
3997                 this.triggerEl.removeClass('open');
3998             }
3999             
4000             this.el.removeClass('show');
4001             this.hidden = true;
4002             this.fireEvent("hide", this);
4003         }
4004         if(deep === true && this.parentMenu){
4005             this.parentMenu.hide(true);
4006         }
4007     },
4008     
4009     onTriggerClick : function(e)
4010     {
4011         Roo.log('trigger click');
4012         
4013         var target = e.getTarget();
4014         
4015         Roo.log(target.nodeName.toLowerCase());
4016         
4017         if(target.nodeName.toLowerCase() === 'i'){
4018             e.preventDefault();
4019         }
4020         
4021     },
4022     
4023     onTriggerPress  : function(e)
4024     {
4025         Roo.log('trigger press');
4026         //Roo.log(e.getTarget());
4027        // Roo.log(this.triggerEl.dom);
4028        
4029         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4030         var pel = Roo.get(e.getTarget());
4031         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4032             Roo.log('is treeview or dropdown?');
4033             return;
4034         }
4035         
4036         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4037             return;
4038         }
4039         
4040         if (this.isVisible()) {
4041             Roo.log('hide');
4042             this.hide();
4043         } else {
4044             Roo.log('show');
4045             
4046             this.show(this.triggerEl, this.align, false);
4047         }
4048         
4049         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4050             e.stopEvent();
4051         }
4052         
4053     },
4054        
4055     
4056     hideMenuItems : function()
4057     {
4058         Roo.log("hide Menu Items");
4059         if (!this.el) { 
4060             return;
4061         }
4062         
4063         this.el.select('.open',true).each(function(aa) {
4064             
4065             aa.removeClass('open');
4066          
4067         });
4068     },
4069     addxtypeChild : function (tree, cntr) {
4070         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071           
4072         this.menuitems.add(comp);
4073         return comp;
4074
4075     },
4076     getEl : function()
4077     {
4078         Roo.log(this.el);
4079         return this.el;
4080     },
4081     
4082     clear : function()
4083     {
4084         this.getEl().dom.innerHTML = '';
4085         this.menuitems.clear();
4086     }
4087 });
4088
4089  
4090  /*
4091  * - LGPL
4092  *
4093  * menu item
4094  * 
4095  */
4096
4097
4098 /**
4099  * @class Roo.bootstrap.MenuItem
4100  * @extends Roo.bootstrap.Component
4101  * Bootstrap MenuItem class
4102  * @cfg {String} html the menu label
4103  * @cfg {String} href the link
4104  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4105  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4106  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4107  * @cfg {String} fa favicon to show on left of menu item.
4108  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109  * 
4110  * 
4111  * @constructor
4112  * Create a new MenuItem
4113  * @param {Object} config The config object
4114  */
4115
4116
4117 Roo.bootstrap.MenuItem = function(config){
4118     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4119     this.addEvents({
4120         // raw events
4121         /**
4122          * @event click
4123          * The raw click event for the entire grid.
4124          * @param {Roo.bootstrap.MenuItem} this
4125          * @param {Roo.EventObject} e
4126          */
4127         "click" : true
4128     });
4129 };
4130
4131 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4132     
4133     href : false,
4134     html : false,
4135     preventDefault: false,
4136     isContainer : false,
4137     active : false,
4138     fa: false,
4139     
4140     getAutoCreate : function(){
4141         
4142         if(this.isContainer){
4143             return {
4144                 tag: 'li',
4145                 cls: 'dropdown-menu-item '
4146             };
4147         }
4148         var ctag = {
4149             tag: 'span',
4150             html: 'Link'
4151         };
4152         
4153         var anc = {
4154             tag : 'a',
4155             cls : 'dropdown-item',
4156             href : '#',
4157             cn : [  ]
4158         };
4159         
4160         if (this.fa !== false) {
4161             anc.cn.push({
4162                 tag : 'i',
4163                 cls : 'fa fa-' + this.fa
4164             });
4165         }
4166         
4167         anc.cn.push(ctag);
4168         
4169         
4170         var cfg= {
4171             tag: 'li',
4172             cls: 'dropdown-menu-item',
4173             cn: [ anc ]
4174         };
4175         if (this.parent().type == 'treeview') {
4176             cfg.cls = 'treeview-menu';
4177         }
4178         if (this.active) {
4179             cfg.cls += ' active';
4180         }
4181         
4182         
4183         
4184         anc.href = this.href || cfg.cn[0].href ;
4185         ctag.html = this.html || cfg.cn[0].html ;
4186         return cfg;
4187     },
4188     
4189     initEvents: function()
4190     {
4191         if (this.parent().type == 'treeview') {
4192             this.el.select('a').on('click', this.onClick, this);
4193         }
4194         
4195         if (this.menu) {
4196             this.menu.parentType = this.xtype;
4197             this.menu.triggerEl = this.el;
4198             this.menu = this.addxtype(Roo.apply({}, this.menu));
4199         }
4200         
4201     },
4202     onClick : function(e)
4203     {
4204         Roo.log('item on click ');
4205         
4206         if(this.preventDefault){
4207             e.preventDefault();
4208         }
4209         //this.parent().hideMenuItems();
4210         
4211         this.fireEvent('click', this, e);
4212     },
4213     getEl : function()
4214     {
4215         return this.el;
4216     } 
4217 });
4218
4219  
4220
4221  /*
4222  * - LGPL
4223  *
4224  * menu separator
4225  * 
4226  */
4227
4228
4229 /**
4230  * @class Roo.bootstrap.MenuSeparator
4231  * @extends Roo.bootstrap.Component
4232  * Bootstrap MenuSeparator class
4233  * 
4234  * @constructor
4235  * Create a new MenuItem
4236  * @param {Object} config The config object
4237  */
4238
4239
4240 Roo.bootstrap.MenuSeparator = function(config){
4241     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4242 };
4243
4244 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4245     
4246     getAutoCreate : function(){
4247         var cfg = {
4248             cls: 'divider',
4249             tag : 'li'
4250         };
4251         
4252         return cfg;
4253     }
4254    
4255 });
4256
4257  
4258
4259  
4260 /*
4261 * Licence: LGPL
4262 */
4263
4264 /**
4265  * @class Roo.bootstrap.Modal
4266  * @extends Roo.bootstrap.Component
4267  * @builder-top
4268  * @parent none
4269  * @children Roo.bootstrap.Component
4270  * Bootstrap Modal class
4271  * @cfg {String} title Title of dialog
4272  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4273  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4274  * @cfg {Boolean} specificTitle default false
4275  * @cfg {Array} buttons Array of buttons or standard button set..
4276  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4277  * @cfg {Boolean} animate default true
4278  * @cfg {Boolean} allow_close default true
4279  * @cfg {Boolean} fitwindow default false
4280  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4281  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4282  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4283  * @cfg {String} size (sm|lg|xl) default empty
4284  * @cfg {Number} max_width set the max width of modal
4285  * @cfg {Boolean} editableTitle can the title be edited
4286
4287  *
4288  *
4289  * @constructor
4290  * Create a new Modal Dialog
4291  * @param {Object} config The config object
4292  */
4293
4294 Roo.bootstrap.Modal = function(config){
4295     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4296     this.addEvents({
4297         // raw events
4298         /**
4299          * @event btnclick
4300          * The raw btnclick event for the button
4301          * @param {Roo.EventObject} e
4302          */
4303         "btnclick" : true,
4304         /**
4305          * @event resize
4306          * Fire when dialog resize
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} e
4309          */
4310         "resize" : true,
4311         /**
4312          * @event titlechanged
4313          * Fire when the editable title has been changed
4314          * @param {Roo.bootstrap.Modal} this
4315          * @param {Roo.EventObject} value
4316          */
4317         "titlechanged" : true 
4318         
4319     });
4320     this.buttons = this.buttons || [];
4321
4322     if (this.tmpl) {
4323         this.tmpl = Roo.factory(this.tmpl);
4324     }
4325
4326 };
4327
4328 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4329
4330     title : 'test dialog',
4331
4332     buttons : false,
4333
4334     // set on load...
4335
4336     html: false,
4337
4338     tmp: false,
4339
4340     specificTitle: false,
4341
4342     buttonPosition: 'right',
4343
4344     allow_close : true,
4345
4346     animate : true,
4347
4348     fitwindow: false,
4349     
4350      // private
4351     dialogEl: false,
4352     bodyEl:  false,
4353     footerEl:  false,
4354     titleEl:  false,
4355     closeEl:  false,
4356
4357     size: '',
4358     
4359     max_width: 0,
4360     
4361     max_height: 0,
4362     
4363     fit_content: false,
4364     editableTitle  : false,
4365
4366     onRender : function(ct, position)
4367     {
4368         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4369
4370         if(!this.el){
4371             var cfg = Roo.apply({},  this.getAutoCreate());
4372             cfg.id = Roo.id();
4373             //if(!cfg.name){
4374             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4375             //}
4376             //if (!cfg.name.length) {
4377             //    delete cfg.name;
4378            // }
4379             if (this.cls) {
4380                 cfg.cls += ' ' + this.cls;
4381             }
4382             if (this.style) {
4383                 cfg.style = this.style;
4384             }
4385             this.el = Roo.get(document.body).createChild(cfg, position);
4386         }
4387         //var type = this.el.dom.type;
4388
4389
4390         if(this.tabIndex !== undefined){
4391             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4392         }
4393
4394         this.dialogEl = this.el.select('.modal-dialog',true).first();
4395         this.bodyEl = this.el.select('.modal-body',true).first();
4396         this.closeEl = this.el.select('.modal-header .close', true).first();
4397         this.headerEl = this.el.select('.modal-header',true).first();
4398         this.titleEl = this.el.select('.modal-title',true).first();
4399         this.footerEl = this.el.select('.modal-footer',true).first();
4400
4401         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4402         
4403         //this.el.addClass("x-dlg-modal");
4404
4405         if (this.buttons.length) {
4406             Roo.each(this.buttons, function(bb) {
4407                 var b = Roo.apply({}, bb);
4408                 b.xns = b.xns || Roo.bootstrap;
4409                 b.xtype = b.xtype || 'Button';
4410                 if (typeof(b.listeners) == 'undefined') {
4411                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4412                 }
4413
4414                 var btn = Roo.factory(b);
4415
4416                 btn.render(this.getButtonContainer());
4417
4418             },this);
4419         }
4420         // render the children.
4421         var nitems = [];
4422
4423         if(typeof(this.items) != 'undefined'){
4424             var items = this.items;
4425             delete this.items;
4426
4427             for(var i =0;i < items.length;i++) {
4428                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4429             }
4430         }
4431
4432         this.items = nitems;
4433
4434         // where are these used - they used to be body/close/footer
4435
4436
4437         this.initEvents();
4438         //this.el.addClass([this.fieldClass, this.cls]);
4439
4440     },
4441
4442     getAutoCreate : function()
4443     {
4444         // we will default to modal-body-overflow - might need to remove or make optional later.
4445         var bdy = {
4446                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4447                 html : this.html || ''
4448         };
4449
4450         var title = {
4451             tag: 'h5',
4452             cls : 'modal-title',
4453             html : this.title
4454         };
4455
4456         if(this.specificTitle){ // WTF is this?
4457             title = this.title;
4458         }
4459
4460         var header = [];
4461         if (this.allow_close && Roo.bootstrap.version == 3) {
4462             header.push({
4463                 tag: 'button',
4464                 cls : 'close',
4465                 html : '&times'
4466             });
4467         }
4468
4469         header.push(title);
4470
4471         if (this.editableTitle) {
4472             header.push({
4473                 cls: 'form-control roo-editable-title d-none',
4474                 tag: 'input',
4475                 type: 'text'
4476             });
4477         }
4478         
4479         if (this.allow_close && Roo.bootstrap.version == 4) {
4480             header.push({
4481                 tag: 'button',
4482                 cls : 'close',
4483                 html : '&times'
4484             });
4485         }
4486         
4487         var size = '';
4488
4489         if(this.size.length){
4490             size = 'modal-' + this.size;
4491         }
4492         
4493         var footer = Roo.bootstrap.version == 3 ?
4494             {
4495                 cls : 'modal-footer',
4496                 cn : [
4497                     {
4498                         tag: 'div',
4499                         cls: 'btn-' + this.buttonPosition
4500                     }
4501                 ]
4502
4503             } :
4504             {  // BS4 uses mr-auto on left buttons....
4505                 cls : 'modal-footer'
4506             };
4507
4508             
4509
4510         
4511         
4512         var modal = {
4513             cls: "modal",
4514              cn : [
4515                 {
4516                     cls: "modal-dialog " + size,
4517                     cn : [
4518                         {
4519                             cls : "modal-content",
4520                             cn : [
4521                                 {
4522                                     cls : 'modal-header',
4523                                     cn : header
4524                                 },
4525                                 bdy,
4526                                 footer
4527                             ]
4528
4529                         }
4530                     ]
4531
4532                 }
4533             ]
4534         };
4535
4536         if(this.animate){
4537             modal.cls += ' fade';
4538         }
4539
4540         return modal;
4541
4542     },
4543     getChildContainer : function() {
4544
4545          return this.bodyEl;
4546
4547     },
4548     getButtonContainer : function() {
4549         
4550          return Roo.bootstrap.version == 4 ?
4551             this.el.select('.modal-footer',true).first()
4552             : this.el.select('.modal-footer div',true).first();
4553
4554     },
4555     initEvents : function()
4556     {
4557         if (this.allow_close) {
4558             this.closeEl.on('click', this.hide, this);
4559         }
4560         Roo.EventManager.onWindowResize(this.resize, this, true);
4561         if (this.editableTitle) {
4562             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4563             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4564             this.headerEditEl.on('keyup', function(e) {
4565                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4566                         this.toggleHeaderInput(false)
4567                     }
4568                 }, this);
4569             this.headerEditEl.on('blur', function(e) {
4570                 this.toggleHeaderInput(false)
4571             },this);
4572         }
4573
4574     },
4575   
4576
4577     resize : function()
4578     {
4579         this.maskEl.setSize(
4580             Roo.lib.Dom.getViewWidth(true),
4581             Roo.lib.Dom.getViewHeight(true)
4582         );
4583         
4584         if (this.fitwindow) {
4585             
4586            this.dialogEl.setStyle( { 'max-width' : '100%' });
4587             this.setSize(
4588                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4589                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4590             );
4591             return;
4592         }
4593         
4594         if(this.max_width !== 0) {
4595             
4596             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4597             
4598             if(this.height) {
4599                 this.setSize(w, this.height);
4600                 return;
4601             }
4602             
4603             if(this.max_height) {
4604                 this.setSize(w,Math.min(
4605                     this.max_height,
4606                     Roo.lib.Dom.getViewportHeight(true) - 60
4607                 ));
4608                 
4609                 return;
4610             }
4611             
4612             if(!this.fit_content) {
4613                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4614                 return;
4615             }
4616             
4617             this.setSize(w, Math.min(
4618                 60 +
4619                 this.headerEl.getHeight() + 
4620                 this.footerEl.getHeight() + 
4621                 this.getChildHeight(this.bodyEl.dom.childNodes),
4622                 Roo.lib.Dom.getViewportHeight(true) - 60)
4623             );
4624         }
4625         
4626     },
4627
4628     setSize : function(w,h)
4629     {
4630         if (!w && !h) {
4631             return;
4632         }
4633         
4634         this.resizeTo(w,h);
4635     },
4636
4637     show : function() {
4638
4639         if (!this.rendered) {
4640             this.render();
4641         }
4642         this.toggleHeaderInput(false);
4643         //this.el.setStyle('display', 'block');
4644         this.el.removeClass('hideing');
4645         this.el.dom.style.display='block';
4646         
4647         Roo.get(document.body).addClass('modal-open');
4648  
4649         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4650             
4651             (function(){
4652                 this.el.addClass('show');
4653                 this.el.addClass('in');
4654             }).defer(50, this);
4655         }else{
4656             this.el.addClass('show');
4657             this.el.addClass('in');
4658         }
4659
4660         // not sure how we can show data in here..
4661         //if (this.tmpl) {
4662         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4663         //}
4664
4665         Roo.get(document.body).addClass("x-body-masked");
4666         
4667         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4668         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4669         this.maskEl.dom.style.display = 'block';
4670         this.maskEl.addClass('show');
4671         
4672         
4673         this.resize();
4674         
4675         this.fireEvent('show', this);
4676
4677         // set zindex here - otherwise it appears to be ignored...
4678         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679
4680         (function () {
4681             this.items.forEach( function(e) {
4682                 e.layout ? e.layout() : false;
4683
4684             });
4685         }).defer(100,this);
4686
4687     },
4688     hide : function()
4689     {
4690         if(this.fireEvent("beforehide", this) !== false){
4691             
4692             this.maskEl.removeClass('show');
4693             
4694             this.maskEl.dom.style.display = '';
4695             Roo.get(document.body).removeClass("x-body-masked");
4696             this.el.removeClass('in');
4697             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4698
4699             if(this.animate){ // why
4700                 this.el.addClass('hideing');
4701                 this.el.removeClass('show');
4702                 (function(){
4703                     if (!this.el.hasClass('hideing')) {
4704                         return; // it's been shown again...
4705                     }
4706                     
4707                     this.el.dom.style.display='';
4708
4709                     Roo.get(document.body).removeClass('modal-open');
4710                     this.el.removeClass('hideing');
4711                 }).defer(150,this);
4712                 
4713             }else{
4714                 this.el.removeClass('show');
4715                 this.el.dom.style.display='';
4716                 Roo.get(document.body).removeClass('modal-open');
4717
4718             }
4719             this.fireEvent('hide', this);
4720         }
4721     },
4722     isVisible : function()
4723     {
4724         
4725         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4726         
4727     },
4728
4729     addButton : function(str, cb)
4730     {
4731
4732
4733         var b = Roo.apply({}, { html : str } );
4734         b.xns = b.xns || Roo.bootstrap;
4735         b.xtype = b.xtype || 'Button';
4736         if (typeof(b.listeners) == 'undefined') {
4737             b.listeners = { click : cb.createDelegate(this)  };
4738         }
4739
4740         var btn = Roo.factory(b);
4741
4742         btn.render(this.getButtonContainer());
4743
4744         return btn;
4745
4746     },
4747
4748     setDefaultButton : function(btn)
4749     {
4750         //this.el.select('.modal-footer').()
4751     },
4752
4753     resizeTo: function(w,h)
4754     {
4755         this.dialogEl.setWidth(w);
4756         
4757         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4758
4759         this.bodyEl.setHeight(h - diff);
4760         
4761         this.fireEvent('resize', this);
4762     },
4763     
4764     setContentSize  : function(w, h)
4765     {
4766
4767     },
4768     onButtonClick: function(btn,e)
4769     {
4770         //Roo.log([a,b,c]);
4771         this.fireEvent('btnclick', btn.name, e);
4772     },
4773      /**
4774      * Set the title of the Dialog
4775      * @param {String} str new Title
4776      */
4777     setTitle: function(str) {
4778         this.titleEl.dom.innerHTML = str;
4779         this.title = str;
4780     },
4781     /**
4782      * Set the body of the Dialog
4783      * @param {String} str new Title
4784      */
4785     setBody: function(str) {
4786         this.bodyEl.dom.innerHTML = str;
4787     },
4788     /**
4789      * Set the body of the Dialog using the template
4790      * @param {Obj} data - apply this data to the template and replace the body contents.
4791      */
4792     applyBody: function(obj)
4793     {
4794         if (!this.tmpl) {
4795             Roo.log("Error - using apply Body without a template");
4796             //code
4797         }
4798         this.tmpl.overwrite(this.bodyEl, obj);
4799     },
4800     
4801     getChildHeight : function(child_nodes)
4802     {
4803         if(
4804             !child_nodes ||
4805             child_nodes.length == 0
4806         ) {
4807             return 0;
4808         }
4809         
4810         var child_height = 0;
4811         
4812         for(var i = 0; i < child_nodes.length; i++) {
4813             
4814             /*
4815             * for modal with tabs...
4816             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4817                 
4818                 var layout_childs = child_nodes[i].childNodes;
4819                 
4820                 for(var j = 0; j < layout_childs.length; j++) {
4821                     
4822                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4823                         
4824                         var layout_body_childs = layout_childs[j].childNodes;
4825                         
4826                         for(var k = 0; k < layout_body_childs.length; k++) {
4827                             
4828                             if(layout_body_childs[k].classList.contains('navbar')) {
4829                                 child_height += layout_body_childs[k].offsetHeight;
4830                                 continue;
4831                             }
4832                             
4833                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4834                                 
4835                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4836                                 
4837                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4838                                     
4839                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4840                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4841                                         continue;
4842                                     }
4843                                     
4844                                 }
4845                                 
4846                             }
4847                             
4848                         }
4849                     }
4850                 }
4851                 continue;
4852             }
4853             */
4854             
4855             child_height += child_nodes[i].offsetHeight;
4856             // Roo.log(child_nodes[i].offsetHeight);
4857         }
4858         
4859         return child_height;
4860     },
4861     toggleHeaderInput : function(is_edit)
4862     {
4863         if (!this.editableTitle) {
4864             return; // not editable.
4865         }
4866         if (is_edit && this.is_header_editing) {
4867             return; // already editing..
4868         }
4869         if (is_edit) {
4870     
4871             this.headerEditEl.dom.value = this.title;
4872             this.headerEditEl.removeClass('d-none');
4873             this.headerEditEl.dom.focus();
4874             this.titleEl.addClass('d-none');
4875             
4876             this.is_header_editing = true;
4877             return
4878         }
4879         // flip back to not editing.
4880         this.title = this.headerEditEl.dom.value;
4881         this.headerEditEl.addClass('d-none');
4882         this.titleEl.removeClass('d-none');
4883         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4884         this.is_header_editing = false;
4885         this.fireEvent('titlechanged', this, this.title);
4886     
4887             
4888         
4889     }
4890
4891 });
4892
4893
4894 Roo.apply(Roo.bootstrap.Modal,  {
4895     /**
4896          * Button config that displays a single OK button
4897          * @type Object
4898          */
4899         OK :  [{
4900             name : 'ok',
4901             weight : 'primary',
4902             html : 'OK'
4903         }],
4904         /**
4905          * Button config that displays Yes and No buttons
4906          * @type Object
4907          */
4908         YESNO : [
4909             {
4910                 name  : 'no',
4911                 html : 'No'
4912             },
4913             {
4914                 name  :'yes',
4915                 weight : 'primary',
4916                 html : 'Yes'
4917             }
4918         ],
4919
4920         /**
4921          * Button config that displays OK and Cancel buttons
4922          * @type Object
4923          */
4924         OKCANCEL : [
4925             {
4926                name : 'cancel',
4927                 html : 'Cancel'
4928             },
4929             {
4930                 name : 'ok',
4931                 weight : 'primary',
4932                 html : 'OK'
4933             }
4934         ],
4935         /**
4936          * Button config that displays Yes, No and Cancel buttons
4937          * @type Object
4938          */
4939         YESNOCANCEL : [
4940             {
4941                 name : 'yes',
4942                 weight : 'primary',
4943                 html : 'Yes'
4944             },
4945             {
4946                 name : 'no',
4947                 html : 'No'
4948             },
4949             {
4950                 name : 'cancel',
4951                 html : 'Cancel'
4952             }
4953         ],
4954         
4955         zIndex : 10001
4956 });
4957
4958 /*
4959  * - LGPL
4960  *
4961  * messagebox - can be used as a replace
4962  * 
4963  */
4964 /**
4965  * @class Roo.MessageBox
4966  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4967  * Example usage:
4968  *<pre><code>
4969 // Basic alert:
4970 Roo.Msg.alert('Status', 'Changes saved successfully.');
4971
4972 // Prompt for user data:
4973 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4974     if (btn == 'ok'){
4975         // process text value...
4976     }
4977 });
4978
4979 // Show a dialog using config options:
4980 Roo.Msg.show({
4981    title:'Save Changes?',
4982    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4983    buttons: Roo.Msg.YESNOCANCEL,
4984    fn: processResult,
4985    animEl: 'elId'
4986 });
4987 </code></pre>
4988  * @singleton
4989  */
4990 Roo.bootstrap.MessageBox = function(){
4991     var dlg, opt, mask, waitTimer;
4992     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4993     var buttons, activeTextEl, bwidth;
4994
4995     
4996     // private
4997     var handleButton = function(button){
4998         dlg.hide();
4999         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5000     };
5001
5002     // private
5003     var handleHide = function(){
5004         if(opt && opt.cls){
5005             dlg.el.removeClass(opt.cls);
5006         }
5007         //if(waitTimer){
5008         //    Roo.TaskMgr.stop(waitTimer);
5009         //    waitTimer = null;
5010         //}
5011     };
5012
5013     // private
5014     var updateButtons = function(b){
5015         var width = 0;
5016         if(!b){
5017             buttons["ok"].hide();
5018             buttons["cancel"].hide();
5019             buttons["yes"].hide();
5020             buttons["no"].hide();
5021             dlg.footerEl.hide();
5022             
5023             return width;
5024         }
5025         dlg.footerEl.show();
5026         for(var k in buttons){
5027             if(typeof buttons[k] != "function"){
5028                 if(b[k]){
5029                     buttons[k].show();
5030                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5031                     width += buttons[k].el.getWidth()+15;
5032                 }else{
5033                     buttons[k].hide();
5034                 }
5035             }
5036         }
5037         return width;
5038     };
5039
5040     // private
5041     var handleEsc = function(d, k, e){
5042         if(opt && opt.closable !== false){
5043             dlg.hide();
5044         }
5045         if(e){
5046             e.stopEvent();
5047         }
5048     };
5049
5050     return {
5051         /**
5052          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5053          * @return {Roo.BasicDialog} The BasicDialog element
5054          */
5055         getDialog : function(){
5056            if(!dlg){
5057                 dlg = new Roo.bootstrap.Modal( {
5058                     //draggable: true,
5059                     //resizable:false,
5060                     //constraintoviewport:false,
5061                     //fixedcenter:true,
5062                     //collapsible : false,
5063                     //shim:true,
5064                     //modal: true,
5065                 //    width: 'auto',
5066                   //  height:100,
5067                     //buttonAlign:"center",
5068                     closeClick : function(){
5069                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5070                             handleButton("no");
5071                         }else{
5072                             handleButton("cancel");
5073                         }
5074                     }
5075                 });
5076                 dlg.render();
5077                 dlg.on("hide", handleHide);
5078                 mask = dlg.mask;
5079                 //dlg.addKeyListener(27, handleEsc);
5080                 buttons = {};
5081                 this.buttons = buttons;
5082                 var bt = this.buttonText;
5083                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5084                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5085                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5086                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5087                 //Roo.log(buttons);
5088                 bodyEl = dlg.bodyEl.createChild({
5089
5090                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5091                         '<textarea class="roo-mb-textarea"></textarea>' +
5092                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5093                 });
5094                 msgEl = bodyEl.dom.firstChild;
5095                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5096                 textboxEl.enableDisplayMode();
5097                 textboxEl.addKeyListener([10,13], function(){
5098                     if(dlg.isVisible() && opt && opt.buttons){
5099                         if(opt.buttons.ok){
5100                             handleButton("ok");
5101                         }else if(opt.buttons.yes){
5102                             handleButton("yes");
5103                         }
5104                     }
5105                 });
5106                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5107                 textareaEl.enableDisplayMode();
5108                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5109                 progressEl.enableDisplayMode();
5110                 
5111                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5112                 var pf = progressEl.dom.firstChild;
5113                 if (pf) {
5114                     pp = Roo.get(pf.firstChild);
5115                     pp.setHeight(pf.offsetHeight);
5116                 }
5117                 
5118             }
5119             return dlg;
5120         },
5121
5122         /**
5123          * Updates the message box body text
5124          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5125          * the XHTML-compliant non-breaking space character '&amp;#160;')
5126          * @return {Roo.MessageBox} This message box
5127          */
5128         updateText : function(text)
5129         {
5130             if(!dlg.isVisible() && !opt.width){
5131                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5132                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5133             }
5134             msgEl.innerHTML = text || '&#160;';
5135       
5136             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5137             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5138             var w = Math.max(
5139                     Math.min(opt.width || cw , this.maxWidth), 
5140                     Math.max(opt.minWidth || this.minWidth, bwidth)
5141             );
5142             if(opt.prompt){
5143                 activeTextEl.setWidth(w);
5144             }
5145             if(dlg.isVisible()){
5146                 dlg.fixedcenter = false;
5147             }
5148             // to big, make it scroll. = But as usual stupid IE does not support
5149             // !important..
5150             
5151             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5152                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5153                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5154             } else {
5155                 bodyEl.dom.style.height = '';
5156                 bodyEl.dom.style.overflowY = '';
5157             }
5158             if (cw > w) {
5159                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5160             } else {
5161                 bodyEl.dom.style.overflowX = '';
5162             }
5163             
5164             dlg.setContentSize(w, bodyEl.getHeight());
5165             if(dlg.isVisible()){
5166                 dlg.fixedcenter = true;
5167             }
5168             return this;
5169         },
5170
5171         /**
5172          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5173          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5174          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5175          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5176          * @return {Roo.MessageBox} This message box
5177          */
5178         updateProgress : function(value, text){
5179             if(text){
5180                 this.updateText(text);
5181             }
5182             
5183             if (pp) { // weird bug on my firefox - for some reason this is not defined
5184                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5185                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5186             }
5187             return this;
5188         },        
5189
5190         /**
5191          * Returns true if the message box is currently displayed
5192          * @return {Boolean} True if the message box is visible, else false
5193          */
5194         isVisible : function(){
5195             return dlg && dlg.isVisible();  
5196         },
5197
5198         /**
5199          * Hides the message box if it is displayed
5200          */
5201         hide : function(){
5202             if(this.isVisible()){
5203                 dlg.hide();
5204             }  
5205         },
5206
5207         /**
5208          * Displays a new message box, or reinitializes an existing message box, based on the config options
5209          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5210          * The following config object properties are supported:
5211          * <pre>
5212 Property    Type             Description
5213 ----------  ---------------  ------------------------------------------------------------------------------------
5214 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5215                                    closes (defaults to undefined)
5216 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5217                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5218 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5219                                    progress and wait dialogs will ignore this property and always hide the
5220                                    close button as they can only be closed programmatically.
5221 cls               String           A custom CSS class to apply to the message box element
5222 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5223                                    displayed (defaults to 75)
5224 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5225                                    function will be btn (the name of the button that was clicked, if applicable,
5226                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5227                                    Progress and wait dialogs will ignore this option since they do not respond to
5228                                    user actions and can only be closed programmatically, so any required function
5229                                    should be called by the same code after it closes the dialog.
5230 icon              String           A CSS class that provides a background image to be used as an icon for
5231                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5232 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5233 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5234 modal             Boolean          False to allow user interaction with the page while the message box is
5235                                    displayed (defaults to true)
5236 msg               String           A string that will replace the existing message box body text (defaults
5237                                    to the XHTML-compliant non-breaking space character '&#160;')
5238 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5239 progress          Boolean          True to display a progress bar (defaults to false)
5240 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5241 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5242 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5243 title             String           The title text
5244 value             String           The string value to set into the active textbox element if displayed
5245 wait              Boolean          True to display a progress bar (defaults to false)
5246 width             Number           The width of the dialog in pixels
5247 </pre>
5248          *
5249          * Example usage:
5250          * <pre><code>
5251 Roo.Msg.show({
5252    title: 'Address',
5253    msg: 'Please enter your address:',
5254    width: 300,
5255    buttons: Roo.MessageBox.OKCANCEL,
5256    multiline: true,
5257    fn: saveAddress,
5258    animEl: 'addAddressBtn'
5259 });
5260 </code></pre>
5261          * @param {Object} config Configuration options
5262          * @return {Roo.MessageBox} This message box
5263          */
5264         show : function(options)
5265         {
5266             
5267             // this causes nightmares if you show one dialog after another
5268             // especially on callbacks..
5269              
5270             if(this.isVisible()){
5271                 
5272                 this.hide();
5273                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5274                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5275                 Roo.log("New Dialog Message:" +  options.msg )
5276                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5277                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5278                 
5279             }
5280             var d = this.getDialog();
5281             opt = options;
5282             d.setTitle(opt.title || "&#160;");
5283             d.closeEl.setDisplayed(opt.closable !== false);
5284             activeTextEl = textboxEl;
5285             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5286             if(opt.prompt){
5287                 if(opt.multiline){
5288                     textboxEl.hide();
5289                     textareaEl.show();
5290                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5291                         opt.multiline : this.defaultTextHeight);
5292                     activeTextEl = textareaEl;
5293                 }else{
5294                     textboxEl.show();
5295                     textareaEl.hide();
5296                 }
5297             }else{
5298                 textboxEl.hide();
5299                 textareaEl.hide();
5300             }
5301             progressEl.setDisplayed(opt.progress === true);
5302             if (opt.progress) {
5303                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5304             }
5305             this.updateProgress(0);
5306             activeTextEl.dom.value = opt.value || "";
5307             if(opt.prompt){
5308                 dlg.setDefaultButton(activeTextEl);
5309             }else{
5310                 var bs = opt.buttons;
5311                 var db = null;
5312                 if(bs && bs.ok){
5313                     db = buttons["ok"];
5314                 }else if(bs && bs.yes){
5315                     db = buttons["yes"];
5316                 }
5317                 dlg.setDefaultButton(db);
5318             }
5319             bwidth = updateButtons(opt.buttons);
5320             this.updateText(opt.msg);
5321             if(opt.cls){
5322                 d.el.addClass(opt.cls);
5323             }
5324             d.proxyDrag = opt.proxyDrag === true;
5325             d.modal = opt.modal !== false;
5326             d.mask = opt.modal !== false ? mask : false;
5327             if(!d.isVisible()){
5328                 // force it to the end of the z-index stack so it gets a cursor in FF
5329                 document.body.appendChild(dlg.el.dom);
5330                 d.animateTarget = null;
5331                 d.show(options.animEl);
5332             }
5333             return this;
5334         },
5335
5336         /**
5337          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5338          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5339          * and closing the message box when the process is complete.
5340          * @param {String} title The title bar text
5341          * @param {String} msg The message box body text
5342          * @return {Roo.MessageBox} This message box
5343          */
5344         progress : function(title, msg){
5345             this.show({
5346                 title : title,
5347                 msg : msg,
5348                 buttons: false,
5349                 progress:true,
5350                 closable:false,
5351                 minWidth: this.minProgressWidth,
5352                 modal : true
5353             });
5354             return this;
5355         },
5356
5357         /**
5358          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5359          * If a callback function is passed it will be called after the user clicks the button, and the
5360          * id of the button that was clicked will be passed as the only parameter to the callback
5361          * (could also be the top-right close button).
5362          * @param {String} title The title bar text
5363          * @param {String} msg The message box body text
5364          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5365          * @param {Object} scope (optional) The scope of the callback function
5366          * @return {Roo.MessageBox} This message box
5367          */
5368         alert : function(title, msg, fn, scope)
5369         {
5370             this.show({
5371                 title : title,
5372                 msg : msg,
5373                 buttons: this.OK,
5374                 fn: fn,
5375                 closable : false,
5376                 scope : scope,
5377                 modal : true
5378             });
5379             return this;
5380         },
5381
5382         /**
5383          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5384          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5385          * You are responsible for closing the message box when the process is complete.
5386          * @param {String} msg The message box body text
5387          * @param {String} title (optional) The title bar text
5388          * @return {Roo.MessageBox} This message box
5389          */
5390         wait : function(msg, title){
5391             this.show({
5392                 title : title,
5393                 msg : msg,
5394                 buttons: false,
5395                 closable:false,
5396                 progress:true,
5397                 modal:true,
5398                 width:300,
5399                 wait:true
5400             });
5401             waitTimer = Roo.TaskMgr.start({
5402                 run: function(i){
5403                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5404                 },
5405                 interval: 1000
5406             });
5407             return this;
5408         },
5409
5410         /**
5411          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5412          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5413          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5414          * @param {String} title The title bar text
5415          * @param {String} msg The message box body text
5416          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5417          * @param {Object} scope (optional) The scope of the callback function
5418          * @return {Roo.MessageBox} This message box
5419          */
5420         confirm : function(title, msg, fn, scope){
5421             this.show({
5422                 title : title,
5423                 msg : msg,
5424                 buttons: this.YESNO,
5425                 fn: fn,
5426                 scope : scope,
5427                 modal : true
5428             });
5429             return this;
5430         },
5431
5432         /**
5433          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5434          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5435          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5436          * (could also be the top-right close button) and the text that was entered will be passed as the two
5437          * parameters to the callback.
5438          * @param {String} title The title bar text
5439          * @param {String} msg The message box body text
5440          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5441          * @param {Object} scope (optional) The scope of the callback function
5442          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5443          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5444          * @return {Roo.MessageBox} This message box
5445          */
5446         prompt : function(title, msg, fn, scope, multiline){
5447             this.show({
5448                 title : title,
5449                 msg : msg,
5450                 buttons: this.OKCANCEL,
5451                 fn: fn,
5452                 minWidth:250,
5453                 scope : scope,
5454                 prompt:true,
5455                 multiline: multiline,
5456                 modal : true
5457             });
5458             return this;
5459         },
5460
5461         /**
5462          * Button config that displays a single OK button
5463          * @type Object
5464          */
5465         OK : {ok:true},
5466         /**
5467          * Button config that displays Yes and No buttons
5468          * @type Object
5469          */
5470         YESNO : {yes:true, no:true},
5471         /**
5472          * Button config that displays OK and Cancel buttons
5473          * @type Object
5474          */
5475         OKCANCEL : {ok:true, cancel:true},
5476         /**
5477          * Button config that displays Yes, No and Cancel buttons
5478          * @type Object
5479          */
5480         YESNOCANCEL : {yes:true, no:true, cancel:true},
5481
5482         /**
5483          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5484          * @type Number
5485          */
5486         defaultTextHeight : 75,
5487         /**
5488          * The maximum width in pixels of the message box (defaults to 600)
5489          * @type Number
5490          */
5491         maxWidth : 600,
5492         /**
5493          * The minimum width in pixels of the message box (defaults to 100)
5494          * @type Number
5495          */
5496         minWidth : 100,
5497         /**
5498          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5499          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5500          * @type Number
5501          */
5502         minProgressWidth : 250,
5503         /**
5504          * An object containing the default button text strings that can be overriden for localized language support.
5505          * Supported properties are: ok, cancel, yes and no.
5506          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5507          * @type Object
5508          */
5509         buttonText : {
5510             ok : "OK",
5511             cancel : "Cancel",
5512             yes : "Yes",
5513             no : "No"
5514         }
5515     };
5516 }();
5517
5518 /**
5519  * Shorthand for {@link Roo.MessageBox}
5520  */
5521 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5522 Roo.Msg = Roo.Msg || Roo.MessageBox;
5523 /*
5524  * - LGPL
5525  *
5526  * navbar
5527  * 
5528  */
5529
5530 /**
5531  * @class Roo.bootstrap.Navbar
5532  * @extends Roo.bootstrap.Component
5533  * Bootstrap Navbar class
5534
5535  * @constructor
5536  * Create a new Navbar
5537  * @param {Object} config The config object
5538  */
5539
5540
5541 Roo.bootstrap.Navbar = function(config){
5542     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5543     this.addEvents({
5544         // raw events
5545         /**
5546          * @event beforetoggle
5547          * Fire before toggle the menu
5548          * @param {Roo.EventObject} e
5549          */
5550         "beforetoggle" : true
5551     });
5552 };
5553
5554 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5555     
5556     
5557    
5558     // private
5559     navItems : false,
5560     loadMask : false,
5561     
5562     
5563     getAutoCreate : function(){
5564         
5565         
5566         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5567         
5568     },
5569     
5570     initEvents :function ()
5571     {
5572         //Roo.log(this.el.select('.navbar-toggle',true));
5573         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5574         
5575         var mark = {
5576             tag: "div",
5577             cls:"x-dlg-mask"
5578         };
5579         
5580         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5581         
5582         var size = this.el.getSize();
5583         this.maskEl.setSize(size.width, size.height);
5584         this.maskEl.enableDisplayMode("block");
5585         this.maskEl.hide();
5586         
5587         if(this.loadMask){
5588             this.maskEl.show();
5589         }
5590     },
5591     
5592     
5593     getChildContainer : function()
5594     {
5595         if (this.el && this.el.select('.collapse').getCount()) {
5596             return this.el.select('.collapse',true).first();
5597         }
5598         
5599         return this.el;
5600     },
5601     
5602     mask : function()
5603     {
5604         this.maskEl.show();
5605     },
5606     
5607     unmask : function()
5608     {
5609         this.maskEl.hide();
5610     },
5611     onToggle : function()
5612     {
5613         
5614         if(this.fireEvent('beforetoggle', this) === false){
5615             return;
5616         }
5617         var ce = this.el.select('.navbar-collapse',true).first();
5618       
5619         if (!ce.hasClass('show')) {
5620            this.expand();
5621         } else {
5622             this.collapse();
5623         }
5624         
5625         
5626     
5627     },
5628     /**
5629      * Expand the navbar pulldown 
5630      */
5631     expand : function ()
5632     {
5633        
5634         var ce = this.el.select('.navbar-collapse',true).first();
5635         if (ce.hasClass('collapsing')) {
5636             return;
5637         }
5638         ce.dom.style.height = '';
5639                // show it...
5640         ce.addClass('in'); // old...
5641         ce.removeClass('collapse');
5642         ce.addClass('show');
5643         var h = ce.getHeight();
5644         Roo.log(h);
5645         ce.removeClass('show');
5646         // at this point we should be able to see it..
5647         ce.addClass('collapsing');
5648         
5649         ce.setHeight(0); // resize it ...
5650         ce.on('transitionend', function() {
5651             //Roo.log('done transition');
5652             ce.removeClass('collapsing');
5653             ce.addClass('show');
5654             ce.removeClass('collapse');
5655
5656             ce.dom.style.height = '';
5657         }, this, { single: true} );
5658         ce.setHeight(h);
5659         ce.dom.scrollTop = 0;
5660     },
5661     /**
5662      * Collapse the navbar pulldown 
5663      */
5664     collapse : function()
5665     {
5666          var ce = this.el.select('.navbar-collapse',true).first();
5667        
5668         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5669             // it's collapsed or collapsing..
5670             return;
5671         }
5672         ce.removeClass('in'); // old...
5673         ce.setHeight(ce.getHeight());
5674         ce.removeClass('show');
5675         ce.addClass('collapsing');
5676         
5677         ce.on('transitionend', function() {
5678             ce.dom.style.height = '';
5679             ce.removeClass('collapsing');
5680             ce.addClass('collapse');
5681         }, this, { single: true} );
5682         ce.setHeight(0);
5683     }
5684     
5685     
5686     
5687 });
5688
5689
5690
5691  
5692
5693  /*
5694  * - LGPL
5695  *
5696  * navbar
5697  * 
5698  */
5699
5700 /**
5701  * @class Roo.bootstrap.NavSimplebar
5702  * @extends Roo.bootstrap.Navbar
5703  * Bootstrap Sidebar class
5704  *
5705  * @cfg {Boolean} inverse is inverted color
5706  * 
5707  * @cfg {String} type (nav | pills | tabs)
5708  * @cfg {Boolean} arrangement stacked | justified
5709  * @cfg {String} align (left | right) alignment
5710  * 
5711  * @cfg {Boolean} main (true|false) main nav bar? default false
5712  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5713  * 
5714  * @cfg {String} tag (header|footer|nav|div) default is nav 
5715
5716  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5717  * 
5718  * 
5719  * @constructor
5720  * Create a new Sidebar
5721  * @param {Object} config The config object
5722  */
5723
5724
5725 Roo.bootstrap.NavSimplebar = function(config){
5726     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5727 };
5728
5729 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5730     
5731     inverse: false,
5732     
5733     type: false,
5734     arrangement: '',
5735     align : false,
5736     
5737     weight : 'light',
5738     
5739     main : false,
5740     
5741     
5742     tag : false,
5743     
5744     
5745     getAutoCreate : function(){
5746         
5747         
5748         var cfg = {
5749             tag : this.tag || 'div',
5750             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5751         };
5752         if (['light','white'].indexOf(this.weight) > -1) {
5753             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5754         }
5755         cfg.cls += ' bg-' + this.weight;
5756         
5757         if (this.inverse) {
5758             cfg.cls += ' navbar-inverse';
5759             
5760         }
5761         
5762         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5763         
5764         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5765             return cfg;
5766         }
5767         
5768         
5769     
5770         
5771         cfg.cn = [
5772             {
5773                 cls: 'nav nav-' + this.xtype,
5774                 tag : 'ul'
5775             }
5776         ];
5777         
5778          
5779         this.type = this.type || 'nav';
5780         if (['tabs','pills'].indexOf(this.type) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.type
5782         
5783         
5784         } else {
5785             if (this.type!=='nav') {
5786                 Roo.log('nav type must be nav/tabs/pills')
5787             }
5788             cfg.cn[0].cls += ' navbar-nav'
5789         }
5790         
5791         
5792         
5793         
5794         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5795             cfg.cn[0].cls += ' nav-' + this.arrangement;
5796         }
5797         
5798         
5799         if (this.align === 'right') {
5800             cfg.cn[0].cls += ' navbar-right';
5801         }
5802         
5803         
5804         
5805         
5806         return cfg;
5807     
5808         
5809     }
5810     
5811     
5812     
5813 });
5814
5815
5816
5817  
5818
5819  
5820        /*
5821  * - LGPL
5822  *
5823  * navbar
5824  * navbar-fixed-top
5825  * navbar-expand-md  fixed-top 
5826  */
5827
5828 /**
5829  * @class Roo.bootstrap.NavHeaderbar
5830  * @extends Roo.bootstrap.NavSimplebar
5831  * Bootstrap Sidebar class
5832  *
5833  * @cfg {String} brand what is brand
5834  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5835  * @cfg {String} brand_href href of the brand
5836  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5837  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5838  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5839  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5840  * 
5841  * @constructor
5842  * Create a new Sidebar
5843  * @param {Object} config The config object
5844  */
5845
5846
5847 Roo.bootstrap.NavHeaderbar = function(config){
5848     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5849       
5850 };
5851
5852 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5853     
5854     position: '',
5855     brand: '',
5856     brand_href: false,
5857     srButton : true,
5858     autohide : false,
5859     desktopCenter : false,
5860    
5861     
5862     getAutoCreate : function(){
5863         
5864         var   cfg = {
5865             tag: this.nav || 'nav',
5866             cls: 'navbar navbar-expand-md',
5867             role: 'navigation',
5868             cn: []
5869         };
5870         
5871         var cn = cfg.cn;
5872         if (this.desktopCenter) {
5873             cn.push({cls : 'container', cn : []});
5874             cn = cn[0].cn;
5875         }
5876         
5877         if(this.srButton){
5878             var btn = {
5879                 tag: 'button',
5880                 type: 'button',
5881                 cls: 'navbar-toggle navbar-toggler',
5882                 'data-toggle': 'collapse',
5883                 cn: [
5884                     {
5885                         tag: 'span',
5886                         cls: 'sr-only',
5887                         html: 'Toggle navigation'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar navbar-toggler-icon'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     },
5897                     {
5898                         tag: 'span',
5899                         cls: 'icon-bar'
5900                     }
5901                 ]
5902             };
5903             
5904             cn.push( Roo.bootstrap.version == 4 ? btn : {
5905                 tag: 'div',
5906                 cls: 'navbar-header',
5907                 cn: [
5908                     btn
5909                 ]
5910             });
5911         }
5912         
5913         cn.push({
5914             tag: 'div',
5915             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5916             cn : []
5917         });
5918         
5919         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5920         
5921         if (['light','white'].indexOf(this.weight) > -1) {
5922             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5923         }
5924         cfg.cls += ' bg-' + this.weight;
5925         
5926         
5927         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5928             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5929             
5930             // tag can override this..
5931             
5932             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5933         }
5934         
5935         if (this.brand !== '') {
5936             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5937             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5938                 tag: 'a',
5939                 href: this.brand_href ? this.brand_href : '#',
5940                 cls: 'navbar-brand',
5941                 cn: [
5942                 this.brand
5943                 ]
5944             });
5945         }
5946         
5947         if(this.main){
5948             cfg.cls += ' main-nav';
5949         }
5950         
5951         
5952         return cfg;
5953
5954         
5955     },
5956     getHeaderChildContainer : function()
5957     {
5958         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5959             return this.el.select('.navbar-header',true).first();
5960         }
5961         
5962         return this.getChildContainer();
5963     },
5964     
5965     getChildContainer : function()
5966     {
5967          
5968         return this.el.select('.roo-navbar-collapse',true).first();
5969          
5970         
5971     },
5972     
5973     initEvents : function()
5974     {
5975         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5976         
5977         if (this.autohide) {
5978             
5979             var prevScroll = 0;
5980             var ft = this.el;
5981             
5982             Roo.get(document).on('scroll',function(e) {
5983                 var ns = Roo.get(document).getScroll().top;
5984                 var os = prevScroll;
5985                 prevScroll = ns;
5986                 
5987                 if(ns > os){
5988                     ft.removeClass('slideDown');
5989                     ft.addClass('slideUp');
5990                     return;
5991                 }
5992                 ft.removeClass('slideUp');
5993                 ft.addClass('slideDown');
5994                  
5995               
5996           },this);
5997         }
5998     }    
5999     
6000 });
6001
6002
6003
6004  
6005
6006  /*
6007  * - LGPL
6008  *
6009  * navbar
6010  * 
6011  */
6012
6013 /**
6014  * @class Roo.bootstrap.NavSidebar
6015  * @extends Roo.bootstrap.Navbar
6016  * Bootstrap Sidebar class
6017  * 
6018  * @constructor
6019  * Create a new Sidebar
6020  * @param {Object} config The config object
6021  */
6022
6023
6024 Roo.bootstrap.NavSidebar = function(config){
6025     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6026 };
6027
6028 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6029     
6030     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6031     
6032     getAutoCreate : function(){
6033         
6034         
6035         return  {
6036             tag: 'div',
6037             cls: 'sidebar sidebar-nav'
6038         };
6039     
6040         
6041     }
6042     
6043     
6044     
6045 });
6046
6047
6048
6049  
6050
6051  /*
6052  * - LGPL
6053  *
6054  * nav group
6055  * 
6056  */
6057
6058 /**
6059  * @class Roo.bootstrap.NavGroup
6060  * @extends Roo.bootstrap.Component
6061  * Bootstrap NavGroup class
6062  * @cfg {String} align (left|right)
6063  * @cfg {Boolean} inverse
6064  * @cfg {String} type (nav|pills|tab) default nav
6065  * @cfg {String} navId - reference Id for navbar.
6066  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067  * 
6068  * @constructor
6069  * Create a new nav group
6070  * @param {Object} config The config object
6071  */
6072
6073 Roo.bootstrap.NavGroup = function(config){
6074     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6075     this.navItems = [];
6076    
6077     Roo.bootstrap.NavGroup.register(this);
6078      this.addEvents({
6079         /**
6080              * @event changed
6081              * Fires when the active item changes
6082              * @param {Roo.bootstrap.NavGroup} this
6083              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6084              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6085          */
6086         'changed': true
6087      });
6088     
6089 };
6090
6091 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6092     
6093     align: '',
6094     inverse: false,
6095     form: false,
6096     type: 'nav',
6097     navId : '',
6098     // private
6099     pilltype : true,
6100     
6101     navItems : false, 
6102     
6103     getAutoCreate : function()
6104     {
6105         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6106         
6107         cfg = {
6108             tag : 'ul',
6109             cls: 'nav' 
6110         };
6111         if (Roo.bootstrap.version == 4) {
6112             if (['tabs','pills'].indexOf(this.type) != -1) {
6113                 cfg.cls += ' nav-' + this.type; 
6114             } else {
6115                 // trying to remove so header bar can right align top?
6116                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6117                     // do not use on header bar... 
6118                     cfg.cls += ' navbar-nav';
6119                 }
6120             }
6121             
6122         } else {
6123             if (['tabs','pills'].indexOf(this.type) != -1) {
6124                 cfg.cls += ' nav-' + this.type
6125             } else {
6126                 if (this.type !== 'nav') {
6127                     Roo.log('nav type must be nav/tabs/pills')
6128                 }
6129                 cfg.cls += ' navbar-nav'
6130             }
6131         }
6132         
6133         if (this.parent() && this.parent().sidebar) {
6134             cfg = {
6135                 tag: 'ul',
6136                 cls: 'dashboard-menu sidebar-menu'
6137             };
6138             
6139             return cfg;
6140         }
6141         
6142         if (this.form === true) {
6143             cfg = {
6144                 tag: 'form',
6145                 cls: 'navbar-form form-inline'
6146             };
6147             //nav navbar-right ml-md-auto
6148             if (this.align === 'right') {
6149                 cfg.cls += ' navbar-right ml-md-auto';
6150             } else {
6151                 cfg.cls += ' navbar-left';
6152             }
6153         }
6154         
6155         if (this.align === 'right') {
6156             cfg.cls += ' navbar-right ml-md-auto';
6157         } else {
6158             cfg.cls += ' mr-auto';
6159         }
6160         
6161         if (this.inverse) {
6162             cfg.cls += ' navbar-inverse';
6163             
6164         }
6165         
6166         
6167         return cfg;
6168     },
6169     /**
6170     * sets the active Navigation item
6171     * @param {Roo.bootstrap.NavItem} the new current navitem
6172     */
6173     setActiveItem : function(item)
6174     {
6175         var prev = false;
6176         Roo.each(this.navItems, function(v){
6177             if (v == item) {
6178                 return ;
6179             }
6180             if (v.isActive()) {
6181                 v.setActive(false, true);
6182                 prev = v;
6183                 
6184             }
6185             
6186         });
6187
6188         item.setActive(true, true);
6189         this.fireEvent('changed', this, item, prev);
6190         
6191         
6192     },
6193     /**
6194     * gets the active Navigation item
6195     * @return {Roo.bootstrap.NavItem} the current navitem
6196     */
6197     getActive : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v){
6202             
6203             if (v.isActive()) {
6204                 prev = v;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     
6212     indexOfNav : function()
6213     {
6214         
6215         var prev = false;
6216         Roo.each(this.navItems, function(v,i){
6217             
6218             if (v.isActive()) {
6219                 prev = i;
6220                 
6221             }
6222             
6223         });
6224         return prev;
6225     },
6226     /**
6227     * adds a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     addItem : function(cfg)
6231     {
6232         if (this.form && Roo.bootstrap.version == 4) {
6233             cfg.tag = 'div';
6234         }
6235         var cn = new Roo.bootstrap.NavItem(cfg);
6236         this.register(cn);
6237         cn.parentId = this.id;
6238         cn.onRender(this.el, null);
6239         return cn;
6240     },
6241     /**
6242     * register a Navigation item
6243     * @param {Roo.bootstrap.NavItem} the navitem to add
6244     */
6245     register : function(item)
6246     {
6247         this.navItems.push( item);
6248         item.navId = this.navId;
6249     
6250     },
6251     
6252     /**
6253     * clear all the Navigation item
6254     */
6255    
6256     clearAll : function()
6257     {
6258         this.navItems = [];
6259         this.el.dom.innerHTML = '';
6260     },
6261     
6262     getNavItem: function(tabId)
6263     {
6264         var ret = false;
6265         Roo.each(this.navItems, function(e) {
6266             if (e.tabId == tabId) {
6267                ret =  e;
6268                return false;
6269             }
6270             return true;
6271             
6272         });
6273         return ret;
6274     },
6275     
6276     setActiveNext : function()
6277     {
6278         var i = this.indexOfNav(this.getActive());
6279         if (i > this.navItems.length) {
6280             return;
6281         }
6282         this.setActiveItem(this.navItems[i+1]);
6283     },
6284     setActivePrev : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i  < 1) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i-1]);
6291     },
6292     clearWasActive : function(except) {
6293         Roo.each(this.navItems, function(e) {
6294             if (e.tabId != except.tabId && e.was_active) {
6295                e.was_active = false;
6296                return false;
6297             }
6298             return true;
6299             
6300         });
6301     },
6302     getWasActive : function ()
6303     {
6304         var r = false;
6305         Roo.each(this.navItems, function(e) {
6306             if (e.was_active) {
6307                r = e;
6308                return false;
6309             }
6310             return true;
6311             
6312         });
6313         return r;
6314     }
6315     
6316     
6317 });
6318
6319  
6320 Roo.apply(Roo.bootstrap.NavGroup, {
6321     
6322     groups: {},
6323      /**
6324     * register a Navigation Group
6325     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6326     */
6327     register : function(navgrp)
6328     {
6329         this.groups[navgrp.navId] = navgrp;
6330         
6331     },
6332     /**
6333     * fetch a Navigation Group based on the navigation ID
6334     * @param {string} the navgroup to add
6335     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6336     */
6337     get: function(navId) {
6338         if (typeof(this.groups[navId]) == 'undefined') {
6339             return false;
6340             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6341         }
6342         return this.groups[navId] ;
6343     }
6344     
6345     
6346     
6347 });
6348
6349  /*
6350  * - LGPL
6351  *
6352  * row
6353  * 
6354  */
6355
6356 /**
6357  * @class Roo.bootstrap.NavItem
6358  * @extends Roo.bootstrap.Component
6359  * Bootstrap Navbar.NavItem class
6360  * @cfg {String} href  link to
6361  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6362  * @cfg {Boolean} button_outline show and outlined button
6363  * @cfg {String} html content of button
6364  * @cfg {String} badge text inside badge
6365  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6366  * @cfg {String} glyphicon DEPRICATED - use fa
6367  * @cfg {String} icon DEPRICATED - use fa
6368  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6369  * @cfg {Boolean} active Is item active
6370  * @cfg {Boolean} disabled Is item disabled
6371  * @cfg {String} linkcls  Link Class
6372  * @cfg {Boolean} preventDefault (true | false) default false
6373  * @cfg {String} tabId the tab that this item activates.
6374  * @cfg {String} tagtype (a|span) render as a href or span?
6375  * @cfg {Boolean} animateRef (true|false) link to element default false  
6376   
6377  * @constructor
6378  * Create a new Navbar Item
6379  * @param {Object} config The config object
6380  */
6381 Roo.bootstrap.NavItem = function(config){
6382     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6383     this.addEvents({
6384         // raw events
6385         /**
6386          * @event click
6387          * The raw click event for the entire grid.
6388          * @param {Roo.EventObject} e
6389          */
6390         "click" : true,
6391          /**
6392             * @event changed
6393             * Fires when the active item active state changes
6394             * @param {Roo.bootstrap.NavItem} this
6395             * @param {boolean} state the new state
6396              
6397          */
6398         'changed': true,
6399         /**
6400             * @event scrollto
6401             * Fires when scroll to element
6402             * @param {Roo.bootstrap.NavItem} this
6403             * @param {Object} options
6404             * @param {Roo.EventObject} e
6405              
6406          */
6407         'scrollto': true
6408     });
6409    
6410 };
6411
6412 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6413     
6414     href: false,
6415     html: '',
6416     badge: '',
6417     icon: false,
6418     fa : false,
6419     glyphicon: false,
6420     active: false,
6421     preventDefault : false,
6422     tabId : false,
6423     tagtype : 'a',
6424     tag: 'li',
6425     disabled : false,
6426     animateRef : false,
6427     was_active : false,
6428     button_weight : '',
6429     button_outline : false,
6430     linkcls : '',
6431     navLink: false,
6432     
6433     getAutoCreate : function(){
6434          
6435         var cfg = {
6436             tag: this.tag,
6437             cls: 'nav-item'
6438         };
6439         
6440         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6441         
6442         if (this.active) {
6443             cfg.cls +=  ' active' ;
6444         }
6445         if (this.disabled) {
6446             cfg.cls += ' disabled';
6447         }
6448         
6449         // BS4 only?
6450         if (this.button_weight.length) {
6451             cfg.tag = this.href ? 'a' : 'button';
6452             cfg.html = this.html || '';
6453             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6454             if (this.href) {
6455                 cfg.href = this.href;
6456             }
6457             if (this.fa) {
6458                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6459             } else {
6460                 cfg.cls += " nav-html";
6461             }
6462             
6463             // menu .. should add dropdown-menu class - so no need for carat..
6464             
6465             if (this.badge !== '') {
6466                  
6467                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6468             }
6469             return cfg;
6470         }
6471         
6472         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6473             cfg.cn = [
6474                 {
6475                     tag: this.tagtype,
6476                     href : this.href || "#",
6477                     html: this.html || '',
6478                     cls : ''
6479                 }
6480             ];
6481             if (this.tagtype == 'a') {
6482                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6483         
6484             }
6485             if (this.icon) {
6486                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else  if (this.fa) {
6488                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6489             } else if(this.glyphicon) {
6490                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6491             } else {
6492                 cfg.cn[0].cls += " nav-html";
6493             }
6494             
6495             if (this.menu) {
6496                 cfg.cn[0].html += " <span class='caret'></span>";
6497              
6498             }
6499             
6500             if (this.badge !== '') {
6501                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6502             }
6503         }
6504         
6505         
6506         
6507         return cfg;
6508     },
6509     onRender : function(ct, position)
6510     {
6511        // Roo.log("Call onRender: " + this.xtype);
6512         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6513             this.tag = 'div';
6514         }
6515         
6516         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6517         this.navLink = this.el.select('.nav-link',true).first();
6518         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6519         return ret;
6520     },
6521       
6522     
6523     initEvents: function() 
6524     {
6525         if (typeof (this.menu) != 'undefined') {
6526             this.menu.parentType = this.xtype;
6527             this.menu.triggerEl = this.el;
6528             this.menu = this.addxtype(Roo.apply({}, this.menu));
6529         }
6530         
6531         this.el.on('click', this.onClick, this);
6532         
6533         //if(this.tagtype == 'span'){
6534         //    this.el.select('span',true).on('click', this.onClick, this);
6535         //}
6536        
6537         // at this point parent should be available..
6538         this.parent().register(this);
6539     },
6540     
6541     onClick : function(e)
6542     {
6543         if (e.getTarget('.dropdown-menu-item')) {
6544             // did you click on a menu itemm.... - then don't trigger onclick..
6545             return;
6546         }
6547         
6548         if(
6549                 this.preventDefault || 
6550                 this.href == '#' 
6551         ){
6552             Roo.log("NavItem - prevent Default?");
6553             e.preventDefault();
6554         }
6555         
6556         if (this.disabled) {
6557             return;
6558         }
6559         
6560         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6561         if (tg && tg.transition) {
6562             Roo.log("waiting for the transitionend");
6563             return;
6564         }
6565         
6566         
6567         
6568         //Roo.log("fire event clicked");
6569         if(this.fireEvent('click', this, e) === false){
6570             return;
6571         };
6572         
6573         if(this.tagtype == 'span'){
6574             return;
6575         }
6576         
6577         //Roo.log(this.href);
6578         var ael = this.el.select('a',true).first();
6579         //Roo.log(ael);
6580         
6581         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6582             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6583             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6584                 return; // ignore... - it's a 'hash' to another page.
6585             }
6586             Roo.log("NavItem - prevent Default?");
6587             e.preventDefault();
6588             this.scrollToElement(e);
6589         }
6590         
6591         
6592         var p =  this.parent();
6593    
6594         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6595             if (typeof(p.setActiveItem) !== 'undefined') {
6596                 p.setActiveItem(this);
6597             }
6598         }
6599         
6600         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6601         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6602             // remove the collapsed menu expand...
6603             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6604         }
6605     },
6606     
6607     isActive: function () {
6608         return this.active
6609     },
6610     setActive : function(state, fire, is_was_active)
6611     {
6612         if (this.active && !state && this.navId) {
6613             this.was_active = true;
6614             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6615             if (nv) {
6616                 nv.clearWasActive(this);
6617             }
6618             
6619         }
6620         this.active = state;
6621         
6622         if (!state ) {
6623             this.el.removeClass('active');
6624             this.navLink ? this.navLink.removeClass('active') : false;
6625         } else if (!this.el.hasClass('active')) {
6626             
6627             this.el.addClass('active');
6628             if (Roo.bootstrap.version == 4 && this.navLink ) {
6629                 this.navLink.addClass('active');
6630             }
6631             
6632         }
6633         if (fire) {
6634             this.fireEvent('changed', this, state);
6635         }
6636         
6637         // show a panel if it's registered and related..
6638         
6639         if (!this.navId || !this.tabId || !state || is_was_active) {
6640             return;
6641         }
6642         
6643         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6644         if (!tg) {
6645             return;
6646         }
6647         var pan = tg.getPanelByName(this.tabId);
6648         if (!pan) {
6649             return;
6650         }
6651         // if we can not flip to new panel - go back to old nav highlight..
6652         if (false == tg.showPanel(pan)) {
6653             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6654             if (nv) {
6655                 var onav = nv.getWasActive();
6656                 if (onav) {
6657                     onav.setActive(true, false, true);
6658                 }
6659             }
6660             
6661         }
6662         
6663         
6664         
6665     },
6666      // this should not be here...
6667     setDisabled : function(state)
6668     {
6669         this.disabled = state;
6670         if (!state ) {
6671             this.el.removeClass('disabled');
6672         } else if (!this.el.hasClass('disabled')) {
6673             this.el.addClass('disabled');
6674         }
6675         
6676     },
6677     
6678     /**
6679      * Fetch the element to display the tooltip on.
6680      * @return {Roo.Element} defaults to this.el
6681      */
6682     tooltipEl : function()
6683     {
6684         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6685     },
6686     
6687     scrollToElement : function(e)
6688     {
6689         var c = document.body;
6690         
6691         /*
6692          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6693          */
6694         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6695             c = document.documentElement;
6696         }
6697         
6698         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6699         
6700         if(!target){
6701             return;
6702         }
6703
6704         var o = target.calcOffsetsTo(c);
6705         
6706         var options = {
6707             target : target,
6708             value : o[1]
6709         };
6710         
6711         this.fireEvent('scrollto', this, options, e);
6712         
6713         Roo.get(c).scrollTo('top', options.value, true);
6714         
6715         return;
6716     },
6717     /**
6718      * Set the HTML (text content) of the item
6719      * @param {string} html  content for the nav item
6720      */
6721     setHtml : function(html)
6722     {
6723         this.html = html;
6724         this.htmlEl.dom.innerHTML = html;
6725         
6726     } 
6727 });
6728  
6729
6730  /*
6731  * - LGPL
6732  *
6733  * sidebar item
6734  *
6735  *  li
6736  *    <span> icon </span>
6737  *    <span> text </span>
6738  *    <span>badge </span>
6739  */
6740
6741 /**
6742  * @class Roo.bootstrap.NavSidebarItem
6743  * @extends Roo.bootstrap.NavItem
6744  * Bootstrap Navbar.NavSidebarItem class
6745  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6746  * {Boolean} open is the menu open
6747  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6748  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6749  * {String} buttonSize (sm|md|lg)the extra classes for the button
6750  * {Boolean} showArrow show arrow next to the text (default true)
6751  * @constructor
6752  * Create a new Navbar Button
6753  * @param {Object} config The config object
6754  */
6755 Roo.bootstrap.NavSidebarItem = function(config){
6756     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6757     this.addEvents({
6758         // raw events
6759         /**
6760          * @event click
6761          * The raw click event for the entire grid.
6762          * @param {Roo.EventObject} e
6763          */
6764         "click" : true,
6765          /**
6766             * @event changed
6767             * Fires when the active item active state changes
6768             * @param {Roo.bootstrap.NavSidebarItem} this
6769             * @param {boolean} state the new state
6770              
6771          */
6772         'changed': true
6773     });
6774    
6775 };
6776
6777 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6778     
6779     badgeWeight : 'default',
6780     
6781     open: false,
6782     
6783     buttonView : false,
6784     
6785     buttonWeight : 'default',
6786     
6787     buttonSize : 'md',
6788     
6789     showArrow : true,
6790     
6791     getAutoCreate : function(){
6792         
6793         
6794         var a = {
6795                 tag: 'a',
6796                 href : this.href || '#',
6797                 cls: '',
6798                 html : '',
6799                 cn : []
6800         };
6801         
6802         if(this.buttonView){
6803             a = {
6804                 tag: 'button',
6805                 href : this.href || '#',
6806                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6807                 html : this.html,
6808                 cn : []
6809             };
6810         }
6811         
6812         var cfg = {
6813             tag: 'li',
6814             cls: '',
6815             cn: [ a ]
6816         };
6817         
6818         if (this.active) {
6819             cfg.cls += ' active';
6820         }
6821         
6822         if (this.disabled) {
6823             cfg.cls += ' disabled';
6824         }
6825         if (this.open) {
6826             cfg.cls += ' open x-open';
6827         }
6828         // left icon..
6829         if (this.glyphicon || this.icon) {
6830             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6831             a.cn.push({ tag : 'i', cls : c }) ;
6832         }
6833         
6834         if(!this.buttonView){
6835             var span = {
6836                 tag: 'span',
6837                 html : this.html || ''
6838             };
6839
6840             a.cn.push(span);
6841             
6842         }
6843         
6844         if (this.badge !== '') {
6845             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6846         }
6847         
6848         if (this.menu) {
6849             
6850             if(this.showArrow){
6851                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6852             }
6853             
6854             a.cls += ' dropdown-toggle treeview' ;
6855         }
6856         
6857         return cfg;
6858     },
6859     
6860     initEvents : function()
6861     { 
6862         if (typeof (this.menu) != 'undefined') {
6863             this.menu.parentType = this.xtype;
6864             this.menu.triggerEl = this.el;
6865             this.menu = this.addxtype(Roo.apply({}, this.menu));
6866         }
6867         
6868         this.el.on('click', this.onClick, this);
6869         
6870         if(this.badge !== ''){
6871             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6872         }
6873         
6874     },
6875     
6876     onClick : function(e)
6877     {
6878         if(this.disabled){
6879             e.preventDefault();
6880             return;
6881         }
6882         
6883         if(this.preventDefault){
6884             e.preventDefault();
6885         }
6886         
6887         this.fireEvent('click', this, e);
6888     },
6889     
6890     disable : function()
6891     {
6892         this.setDisabled(true);
6893     },
6894     
6895     enable : function()
6896     {
6897         this.setDisabled(false);
6898     },
6899     
6900     setDisabled : function(state)
6901     {
6902         if(this.disabled == state){
6903             return;
6904         }
6905         
6906         this.disabled = state;
6907         
6908         if (state) {
6909             this.el.addClass('disabled');
6910             return;
6911         }
6912         
6913         this.el.removeClass('disabled');
6914         
6915         return;
6916     },
6917     
6918     setActive : function(state)
6919     {
6920         if(this.active == state){
6921             return;
6922         }
6923         
6924         this.active = state;
6925         
6926         if (state) {
6927             this.el.addClass('active');
6928             return;
6929         }
6930         
6931         this.el.removeClass('active');
6932         
6933         return;
6934     },
6935     
6936     isActive: function () 
6937     {
6938         return this.active;
6939     },
6940     
6941     setBadge : function(str)
6942     {
6943         if(!this.badgeEl){
6944             return;
6945         }
6946         
6947         this.badgeEl.dom.innerHTML = str;
6948     }
6949     
6950    
6951      
6952  
6953 });
6954  
6955
6956  /*
6957  * - LGPL
6958  *
6959  *  Breadcrumb Nav
6960  * 
6961  */
6962 Roo.namespace('Roo.bootstrap.breadcrumb');
6963
6964
6965 /**
6966  * @class Roo.bootstrap.breadcrumb.Nav
6967  * @extends Roo.bootstrap.Component
6968  * Bootstrap Breadcrumb Nav Class
6969  *  
6970  * @children Roo.bootstrap.breadcrumb.Item
6971  * 
6972  * @constructor
6973  * Create a new breadcrumb.Nav
6974  * @param {Object} config The config object
6975  */
6976
6977
6978 Roo.bootstrap.breadcrumb.Nav = function(config){
6979     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6980     
6981     
6982 };
6983
6984 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6985     
6986     getAutoCreate : function()
6987     {
6988
6989         var cfg = {
6990             tag: 'nav',
6991             cn : [
6992                 {
6993                     tag : 'ol',
6994                     cls : 'breadcrumb'
6995                 }
6996             ]
6997             
6998         };
6999           
7000         return cfg;
7001     },
7002     
7003     initEvents: function()
7004     {
7005         this.olEl = this.el.select('ol',true).first();    
7006     },
7007     getChildContainer : function()
7008     {
7009         return this.olEl;  
7010     }
7011     
7012 });
7013
7014  /*
7015  * - LGPL
7016  *
7017  *  Breadcrumb Item
7018  * 
7019  */
7020
7021
7022 /**
7023  * @class Roo.bootstrap.breadcrumb.Nav
7024  * @extends Roo.bootstrap.Component
7025  * Bootstrap Breadcrumb Nav Class
7026  *  
7027  * @children Roo.bootstrap.breadcrumb.Component
7028  * @cfg {String} html the content of the link.
7029  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7030  * @cfg {Boolean} active is it active
7031
7032  * 
7033  * @constructor
7034  * Create a new breadcrumb.Nav
7035  * @param {Object} config The config object
7036  */
7037
7038 Roo.bootstrap.breadcrumb.Item = function(config){
7039     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7040     this.addEvents({
7041         // img events
7042         /**
7043          * @event click
7044          * The img click event for the img.
7045          * @param {Roo.EventObject} e
7046          */
7047         "click" : true
7048     });
7049     
7050 };
7051
7052 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7053     
7054     href: false,
7055     html : '',
7056     
7057     getAutoCreate : function()
7058     {
7059
7060         var cfg = {
7061             tag: 'li',
7062             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7063         };
7064         if (this.href !== false) {
7065             cfg.cn = [{
7066                 tag : 'a',
7067                 href : this.href,
7068                 html : this.html
7069             }];
7070         } else {
7071             cfg.html = this.html;
7072         }
7073         
7074         return cfg;
7075     },
7076     
7077     initEvents: function()
7078     {
7079         if (this.href) {
7080             this.el.select('a', true).first().on('click',this.onClick, this)
7081         }
7082         
7083     },
7084     onClick : function(e)
7085     {
7086         e.preventDefault();
7087         this.fireEvent('click',this,  e);
7088     }
7089     
7090 });
7091
7092  /*
7093  * - LGPL
7094  *
7095  * row
7096  * 
7097  */
7098
7099 /**
7100  * @class Roo.bootstrap.Row
7101  * @extends Roo.bootstrap.Component
7102  * @children Roo.bootstrap.Component
7103  * Bootstrap Row class (contains columns...)
7104  * 
7105  * @constructor
7106  * Create a new Row
7107  * @param {Object} config The config object
7108  */
7109
7110 Roo.bootstrap.Row = function(config){
7111     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7112 };
7113
7114 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7115     
7116     getAutoCreate : function(){
7117        return {
7118             cls: 'row clearfix'
7119        };
7120     }
7121     
7122     
7123 });
7124
7125  
7126
7127  /*
7128  * - LGPL
7129  *
7130  * pagination
7131  * 
7132  */
7133
7134 /**
7135  * @class Roo.bootstrap.Pagination
7136  * @extends Roo.bootstrap.Component
7137  * Bootstrap Pagination class
7138  * @cfg {String} size xs | sm | md | lg
7139  * @cfg {Boolean} inverse false | true
7140  * 
7141  * @constructor
7142  * Create a new Pagination
7143  * @param {Object} config The config object
7144  */
7145
7146 Roo.bootstrap.Pagination = function(config){
7147     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7148 };
7149
7150 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7151     
7152     cls: false,
7153     size: false,
7154     inverse: false,
7155     
7156     getAutoCreate : function(){
7157         var cfg = {
7158             tag: 'ul',
7159                 cls: 'pagination'
7160         };
7161         if (this.inverse) {
7162             cfg.cls += ' inverse';
7163         }
7164         if (this.html) {
7165             cfg.html=this.html;
7166         }
7167         if (this.cls) {
7168             cfg.cls += " " + this.cls;
7169         }
7170         return cfg;
7171     }
7172    
7173 });
7174
7175  
7176
7177  /*
7178  * - LGPL
7179  *
7180  * Pagination item
7181  * 
7182  */
7183
7184
7185 /**
7186  * @class Roo.bootstrap.PaginationItem
7187  * @extends Roo.bootstrap.Component
7188  * Bootstrap PaginationItem class
7189  * @cfg {String} html text
7190  * @cfg {String} href the link
7191  * @cfg {Boolean} preventDefault (true | false) default true
7192  * @cfg {Boolean} active (true | false) default false
7193  * @cfg {Boolean} disabled default false
7194  * 
7195  * 
7196  * @constructor
7197  * Create a new PaginationItem
7198  * @param {Object} config The config object
7199  */
7200
7201
7202 Roo.bootstrap.PaginationItem = function(config){
7203     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7204     this.addEvents({
7205         // raw events
7206         /**
7207          * @event click
7208          * The raw click event for the entire grid.
7209          * @param {Roo.EventObject} e
7210          */
7211         "click" : true
7212     });
7213 };
7214
7215 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7216     
7217     href : false,
7218     html : false,
7219     preventDefault: true,
7220     active : false,
7221     cls : false,
7222     disabled: false,
7223     
7224     getAutoCreate : function(){
7225         var cfg= {
7226             tag: 'li',
7227             cn: [
7228                 {
7229                     tag : 'a',
7230                     href : this.href ? this.href : '#',
7231                     html : this.html ? this.html : ''
7232                 }
7233             ]
7234         };
7235         
7236         if(this.cls){
7237             cfg.cls = this.cls;
7238         }
7239         
7240         if(this.disabled){
7241             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7242         }
7243         
7244         if(this.active){
7245             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7246         }
7247         
7248         return cfg;
7249     },
7250     
7251     initEvents: function() {
7252         
7253         this.el.on('click', this.onClick, this);
7254         
7255     },
7256     onClick : function(e)
7257     {
7258         Roo.log('PaginationItem on click ');
7259         if(this.preventDefault){
7260             e.preventDefault();
7261         }
7262         
7263         if(this.disabled){
7264             return;
7265         }
7266         
7267         this.fireEvent('click', this, e);
7268     }
7269    
7270 });
7271
7272  
7273
7274  /*
7275  * - LGPL
7276  *
7277  * slider
7278  * 
7279  */
7280
7281
7282 /**
7283  * @class Roo.bootstrap.Slider
7284  * @extends Roo.bootstrap.Component
7285  * Bootstrap Slider class
7286  *    
7287  * @constructor
7288  * Create a new Slider
7289  * @param {Object} config The config object
7290  */
7291
7292 Roo.bootstrap.Slider = function(config){
7293     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7294 };
7295
7296 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7297     
7298     getAutoCreate : function(){
7299         
7300         var cfg = {
7301             tag: 'div',
7302             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7303             cn: [
7304                 {
7305                     tag: 'a',
7306                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7307                 }
7308             ]
7309         };
7310         
7311         return cfg;
7312     }
7313    
7314 });
7315
7316  /*
7317  * Based on:
7318  * Ext JS Library 1.1.1
7319  * Copyright(c) 2006-2007, Ext JS, LLC.
7320  *
7321  * Originally Released Under LGPL - original licence link has changed is not relivant.
7322  *
7323  * Fork - LGPL
7324  * <script type="text/javascript">
7325  */
7326  /**
7327  * @extends Roo.dd.DDProxy
7328  * @class Roo.grid.SplitDragZone
7329  * Support for Column Header resizing
7330  * @constructor
7331  * @param {Object} config
7332  */
7333 // private
7334 // This is a support class used internally by the Grid components
7335 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7336     this.grid = grid;
7337     this.view = grid.getView();
7338     this.proxy = this.view.resizeProxy;
7339     Roo.grid.SplitDragZone.superclass.constructor.call(
7340         this,
7341         hd, // ID
7342         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7343         {  // CONFIG
7344             dragElId : Roo.id(this.proxy.dom),
7345             resizeFrame:false
7346         }
7347     );
7348     
7349     this.setHandleElId(Roo.id(hd));
7350     if (hd2 !== false) {
7351         this.setOuterHandleElId(Roo.id(hd2));
7352     }
7353     
7354     this.scroll = false;
7355 };
7356 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7357     fly: Roo.Element.fly,
7358
7359     b4StartDrag : function(x, y){
7360         this.view.headersDisabled = true;
7361         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7362                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7363         );
7364         this.proxy.setHeight(h);
7365         
7366         // for old system colWidth really stored the actual width?
7367         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7368         // which in reality did not work.. - it worked only for fixed sizes
7369         // for resizable we need to use actual sizes.
7370         var w = this.cm.getColumnWidth(this.cellIndex);
7371         if (!this.view.mainWrap) {
7372             // bootstrap.
7373             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7374         }
7375         
7376         
7377         
7378         // this was w-this.grid.minColumnWidth;
7379         // doesnt really make sense? - w = thie curren width or the rendered one?
7380         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7381         this.resetConstraints();
7382         this.setXConstraint(minw, 1000);
7383         this.setYConstraint(0, 0);
7384         this.minX = x - minw;
7385         this.maxX = x + 1000;
7386         this.startPos = x;
7387         if (!this.view.mainWrap) { // this is Bootstrap code..
7388             this.getDragEl().style.display='block';
7389         }
7390         
7391         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7392     },
7393
7394
7395     handleMouseDown : function(e){
7396         ev = Roo.EventObject.setEvent(e);
7397         var t = this.fly(ev.getTarget());
7398         if(t.hasClass("x-grid-split")){
7399             this.cellIndex = this.view.getCellIndex(t.dom);
7400             this.split = t.dom;
7401             this.cm = this.grid.colModel;
7402             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7403                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7404             }
7405         }
7406     },
7407
7408     endDrag : function(e){
7409         this.view.headersDisabled = false;
7410         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7411         var diff = endX - this.startPos;
7412         // 
7413         var w = this.cm.getColumnWidth(this.cellIndex);
7414         if (!this.view.mainWrap) {
7415             w = 0;
7416         }
7417         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7418     },
7419
7420     autoOffset : function(){
7421         this.setDelta(0,0);
7422     }
7423 });/*
7424  * Based on:
7425  * Ext JS Library 1.1.1
7426  * Copyright(c) 2006-2007, Ext JS, LLC.
7427  *
7428  * Originally Released Under LGPL - original licence link has changed is not relivant.
7429  *
7430  * Fork - LGPL
7431  * <script type="text/javascript">
7432  */
7433
7434 /**
7435  * @class Roo.grid.AbstractSelectionModel
7436  * @extends Roo.util.Observable
7437  * @abstract
7438  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7439  * implemented by descendant classes.  This class should not be directly instantiated.
7440  * @constructor
7441  */
7442 Roo.grid.AbstractSelectionModel = function(){
7443     this.locked = false;
7444     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7445 };
7446
7447 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7448     /** @ignore Called by the grid automatically. Do not call directly. */
7449     init : function(grid){
7450         this.grid = grid;
7451         this.initEvents();
7452     },
7453
7454     /**
7455      * Locks the selections.
7456      */
7457     lock : function(){
7458         this.locked = true;
7459     },
7460
7461     /**
7462      * Unlocks the selections.
7463      */
7464     unlock : function(){
7465         this.locked = false;
7466     },
7467
7468     /**
7469      * Returns true if the selections are locked.
7470      * @return {Boolean}
7471      */
7472     isLocked : function(){
7473         return this.locked;
7474     }
7475 });/*
7476  * Based on:
7477  * Ext JS Library 1.1.1
7478  * Copyright(c) 2006-2007, Ext JS, LLC.
7479  *
7480  * Originally Released Under LGPL - original licence link has changed is not relivant.
7481  *
7482  * Fork - LGPL
7483  * <script type="text/javascript">
7484  */
7485 /**
7486  * @extends Roo.grid.AbstractSelectionModel
7487  * @class Roo.grid.RowSelectionModel
7488  * The default SelectionModel used by {@link Roo.grid.Grid}.
7489  * It supports multiple selections and keyboard selection/navigation. 
7490  * @constructor
7491  * @param {Object} config
7492  */
7493 Roo.grid.RowSelectionModel = function(config){
7494     Roo.apply(this, config);
7495     this.selections = new Roo.util.MixedCollection(false, function(o){
7496         return o.id;
7497     });
7498
7499     this.last = false;
7500     this.lastActive = false;
7501
7502     this.addEvents({
7503         /**
7504         * @event selectionchange
7505         * Fires when the selection changes
7506         * @param {SelectionModel} this
7507         */
7508        "selectionchange" : true,
7509        /**
7510         * @event afterselectionchange
7511         * Fires after the selection changes (eg. by key press or clicking)
7512         * @param {SelectionModel} this
7513         */
7514        "afterselectionchange" : true,
7515        /**
7516         * @event beforerowselect
7517         * Fires when a row is selected being selected, return false to cancel.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         * @param {Boolean} keepExisting False if other selections will be cleared
7521         */
7522        "beforerowselect" : true,
7523        /**
7524         * @event rowselect
7525         * Fires when a row is selected.
7526         * @param {SelectionModel} this
7527         * @param {Number} rowIndex The selected index
7528         * @param {Roo.data.Record} r The record
7529         */
7530        "rowselect" : true,
7531        /**
7532         * @event rowdeselect
7533         * Fires when a row is deselected.
7534         * @param {SelectionModel} this
7535         * @param {Number} rowIndex The selected index
7536         */
7537         "rowdeselect" : true
7538     });
7539     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7540     this.locked = false;
7541 };
7542
7543 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7544     /**
7545      * @cfg {Boolean} singleSelect
7546      * True to allow selection of only one row at a time (defaults to false)
7547      */
7548     singleSelect : false,
7549
7550     // private
7551     initEvents : function(){
7552
7553         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7554             this.grid.on("mousedown", this.handleMouseDown, this);
7555         }else{ // allow click to work like normal
7556             this.grid.on("rowclick", this.handleDragableRowClick, this);
7557         }
7558         // bootstrap does not have a view..
7559         var view = this.grid.view ? this.grid.view : this.grid;
7560         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7561             "up" : function(e){
7562                 if(!e.shiftKey){
7563                     this.selectPrevious(e.shiftKey);
7564                 }else if(this.last !== false && this.lastActive !== false){
7565                     var last = this.last;
7566                     this.selectRange(this.last,  this.lastActive-1);
7567                     view.focusRow(this.lastActive);
7568                     if(last !== false){
7569                         this.last = last;
7570                     }
7571                 }else{
7572                     this.selectFirstRow();
7573                 }
7574                 this.fireEvent("afterselectionchange", this);
7575             },
7576             "down" : function(e){
7577                 if(!e.shiftKey){
7578                     this.selectNext(e.shiftKey);
7579                 }else if(this.last !== false && this.lastActive !== false){
7580                     var last = this.last;
7581                     this.selectRange(this.last,  this.lastActive+1);
7582                     view.focusRow(this.lastActive);
7583                     if(last !== false){
7584                         this.last = last;
7585                     }
7586                 }else{
7587                     this.selectFirstRow();
7588                 }
7589                 this.fireEvent("afterselectionchange", this);
7590             },
7591             scope: this
7592         });
7593
7594          
7595         view.on("refresh", this.onRefresh, this);
7596         view.on("rowupdated", this.onRowUpdated, this);
7597         view.on("rowremoved", this.onRemove, this);
7598     },
7599
7600     // private
7601     onRefresh : function(){
7602         var ds = this.grid.ds, i, v = this.grid.view;
7603         var s = this.selections;
7604         s.each(function(r){
7605             if((i = ds.indexOfId(r.id)) != -1){
7606                 v.onRowSelect(i);
7607                 s.add(ds.getAt(i)); // updating the selection relate data
7608             }else{
7609                 s.remove(r);
7610             }
7611         });
7612     },
7613
7614     // private
7615     onRemove : function(v, index, r){
7616         this.selections.remove(r);
7617     },
7618
7619     // private
7620     onRowUpdated : function(v, index, r){
7621         if(this.isSelected(r)){
7622             v.onRowSelect(index);
7623         }
7624     },
7625
7626     /**
7627      * Select records.
7628      * @param {Array} records The records to select
7629      * @param {Boolean} keepExisting (optional) True to keep existing selections
7630      */
7631     selectRecords : function(records, keepExisting){
7632         if(!keepExisting){
7633             this.clearSelections();
7634         }
7635         var ds = this.grid.ds;
7636         for(var i = 0, len = records.length; i < len; i++){
7637             this.selectRow(ds.indexOf(records[i]), true);
7638         }
7639     },
7640
7641     /**
7642      * Gets the number of selected rows.
7643      * @return {Number}
7644      */
7645     getCount : function(){
7646         return this.selections.length;
7647     },
7648
7649     /**
7650      * Selects the first row in the grid.
7651      */
7652     selectFirstRow : function(){
7653         this.selectRow(0);
7654     },
7655
7656     /**
7657      * Select the last row.
7658      * @param {Boolean} keepExisting (optional) True to keep existing selections
7659      */
7660     selectLastRow : function(keepExisting){
7661         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7662     },
7663
7664     /**
7665      * Selects the row immediately following the last selected row.
7666      * @param {Boolean} keepExisting (optional) True to keep existing selections
7667      */
7668     selectNext : function(keepExisting){
7669         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7670             this.selectRow(this.last+1, keepExisting);
7671             var view = this.grid.view ? this.grid.view : this.grid;
7672             view.focusRow(this.last);
7673         }
7674     },
7675
7676     /**
7677      * Selects the row that precedes the last selected row.
7678      * @param {Boolean} keepExisting (optional) True to keep existing selections
7679      */
7680     selectPrevious : function(keepExisting){
7681         if(this.last){
7682             this.selectRow(this.last-1, keepExisting);
7683             var view = this.grid.view ? this.grid.view : this.grid;
7684             view.focusRow(this.last);
7685         }
7686     },
7687
7688     /**
7689      * Returns the selected records
7690      * @return {Array} Array of selected records
7691      */
7692     getSelections : function(){
7693         return [].concat(this.selections.items);
7694     },
7695
7696     /**
7697      * Returns the first selected record.
7698      * @return {Record}
7699      */
7700     getSelected : function(){
7701         return this.selections.itemAt(0);
7702     },
7703
7704
7705     /**
7706      * Clears all selections.
7707      */
7708     clearSelections : function(fast){
7709         if(this.locked) {
7710             return;
7711         }
7712         if(fast !== true){
7713             var ds = this.grid.ds;
7714             var s = this.selections;
7715             s.each(function(r){
7716                 this.deselectRow(ds.indexOfId(r.id));
7717             }, this);
7718             s.clear();
7719         }else{
7720             this.selections.clear();
7721         }
7722         this.last = false;
7723     },
7724
7725
7726     /**
7727      * Selects all rows.
7728      */
7729     selectAll : function(){
7730         if(this.locked) {
7731             return;
7732         }
7733         this.selections.clear();
7734         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7735             this.selectRow(i, true);
7736         }
7737     },
7738
7739     /**
7740      * Returns True if there is a selection.
7741      * @return {Boolean}
7742      */
7743     hasSelection : function(){
7744         return this.selections.length > 0;
7745     },
7746
7747     /**
7748      * Returns True if the specified row is selected.
7749      * @param {Number/Record} record The record or index of the record to check
7750      * @return {Boolean}
7751      */
7752     isSelected : function(index){
7753         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7754         return (r && this.selections.key(r.id) ? true : false);
7755     },
7756
7757     /**
7758      * Returns True if the specified record id is selected.
7759      * @param {String} id The id of record to check
7760      * @return {Boolean}
7761      */
7762     isIdSelected : function(id){
7763         return (this.selections.key(id) ? true : false);
7764     },
7765
7766     // private
7767     handleMouseDown : function(e, t)
7768     {
7769         var view = this.grid.view ? this.grid.view : this.grid;
7770         var rowIndex;
7771         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7772             return;
7773         };
7774         if(e.shiftKey && this.last !== false){
7775             var last = this.last;
7776             this.selectRange(last, rowIndex, e.ctrlKey);
7777             this.last = last; // reset the last
7778             view.focusRow(rowIndex);
7779         }else{
7780             var isSelected = this.isSelected(rowIndex);
7781             if(e.button !== 0 && isSelected){
7782                 view.focusRow(rowIndex);
7783             }else if(e.ctrlKey && isSelected){
7784                 this.deselectRow(rowIndex);
7785             }else if(!isSelected){
7786                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7787                 view.focusRow(rowIndex);
7788             }
7789         }
7790         this.fireEvent("afterselectionchange", this);
7791     },
7792     // private
7793     handleDragableRowClick :  function(grid, rowIndex, e) 
7794     {
7795         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7796             this.selectRow(rowIndex, false);
7797             var view = this.grid.view ? this.grid.view : this.grid;
7798             view.focusRow(rowIndex);
7799              this.fireEvent("afterselectionchange", this);
7800         }
7801     },
7802     
7803     /**
7804      * Selects multiple rows.
7805      * @param {Array} rows Array of the indexes of the row to select
7806      * @param {Boolean} keepExisting (optional) True to keep existing selections
7807      */
7808     selectRows : function(rows, keepExisting){
7809         if(!keepExisting){
7810             this.clearSelections();
7811         }
7812         for(var i = 0, len = rows.length; i < len; i++){
7813             this.selectRow(rows[i], true);
7814         }
7815     },
7816
7817     /**
7818      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7819      * @param {Number} startRow The index of the first row in the range
7820      * @param {Number} endRow The index of the last row in the range
7821      * @param {Boolean} keepExisting (optional) True to retain existing selections
7822      */
7823     selectRange : function(startRow, endRow, keepExisting){
7824         if(this.locked) {
7825             return;
7826         }
7827         if(!keepExisting){
7828             this.clearSelections();
7829         }
7830         if(startRow <= endRow){
7831             for(var i = startRow; i <= endRow; i++){
7832                 this.selectRow(i, true);
7833             }
7834         }else{
7835             for(var i = startRow; i >= endRow; i--){
7836                 this.selectRow(i, true);
7837             }
7838         }
7839     },
7840
7841     /**
7842      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7843      * @param {Number} startRow The index of the first row in the range
7844      * @param {Number} endRow The index of the last row in the range
7845      */
7846     deselectRange : function(startRow, endRow, preventViewNotify){
7847         if(this.locked) {
7848             return;
7849         }
7850         for(var i = startRow; i <= endRow; i++){
7851             this.deselectRow(i, preventViewNotify);
7852         }
7853     },
7854
7855     /**
7856      * Selects a row.
7857      * @param {Number} row The index of the row to select
7858      * @param {Boolean} keepExisting (optional) True to keep existing selections
7859      */
7860     selectRow : function(index, keepExisting, preventViewNotify){
7861         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7862             return;
7863         }
7864         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7865             if(!keepExisting || this.singleSelect){
7866                 this.clearSelections();
7867             }
7868             var r = this.grid.ds.getAt(index);
7869             this.selections.add(r);
7870             this.last = this.lastActive = index;
7871             if(!preventViewNotify){
7872                 var view = this.grid.view ? this.grid.view : this.grid;
7873                 view.onRowSelect(index);
7874             }
7875             this.fireEvent("rowselect", this, index, r);
7876             this.fireEvent("selectionchange", this);
7877         }
7878     },
7879
7880     /**
7881      * Deselects a row.
7882      * @param {Number} row The index of the row to deselect
7883      */
7884     deselectRow : function(index, preventViewNotify){
7885         if(this.locked) {
7886             return;
7887         }
7888         if(this.last == index){
7889             this.last = false;
7890         }
7891         if(this.lastActive == index){
7892             this.lastActive = false;
7893         }
7894         var r = this.grid.ds.getAt(index);
7895         this.selections.remove(r);
7896         if(!preventViewNotify){
7897             var view = this.grid.view ? this.grid.view : this.grid;
7898             view.onRowDeselect(index);
7899         }
7900         this.fireEvent("rowdeselect", this, index);
7901         this.fireEvent("selectionchange", this);
7902     },
7903
7904     // private
7905     restoreLast : function(){
7906         if(this._last){
7907             this.last = this._last;
7908         }
7909     },
7910
7911     // private
7912     acceptsNav : function(row, col, cm){
7913         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7914     },
7915
7916     // private
7917     onEditorKey : function(field, e){
7918         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7919         if(k == e.TAB){
7920             e.stopEvent();
7921             ed.completeEdit();
7922             if(e.shiftKey){
7923                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7924             }else{
7925                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7926             }
7927         }else if(k == e.ENTER && !e.ctrlKey){
7928             e.stopEvent();
7929             ed.completeEdit();
7930             if(e.shiftKey){
7931                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7932             }else{
7933                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7934             }
7935         }else if(k == e.ESC){
7936             ed.cancelEdit();
7937         }
7938         if(newCell){
7939             g.startEditing(newCell[0], newCell[1]);
7940         }
7941     }
7942 });/*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952  
7953
7954 /**
7955  * @class Roo.grid.ColumnModel
7956  * @extends Roo.util.Observable
7957  * This is the default implementation of a ColumnModel used by the Grid. It defines
7958  * the columns in the grid.
7959  * <br>Usage:<br>
7960  <pre><code>
7961  var colModel = new Roo.grid.ColumnModel([
7962         {header: "Ticker", width: 60, sortable: true, locked: true},
7963         {header: "Company Name", width: 150, sortable: true},
7964         {header: "Market Cap.", width: 100, sortable: true},
7965         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7966         {header: "Employees", width: 100, sortable: true, resizable: false}
7967  ]);
7968  </code></pre>
7969  * <p>
7970  
7971  * The config options listed for this class are options which may appear in each
7972  * individual column definition.
7973  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7974  * @constructor
7975  * @param {Object} config An Array of column config objects. See this class's
7976  * config objects for details.
7977 */
7978 Roo.grid.ColumnModel = function(config){
7979         /**
7980      * The config passed into the constructor
7981      */
7982     this.config = []; //config;
7983     this.lookup = {};
7984
7985     // if no id, create one
7986     // if the column does not have a dataIndex mapping,
7987     // map it to the order it is in the config
7988     for(var i = 0, len = config.length; i < len; i++){
7989         this.addColumn(config[i]);
7990         
7991     }
7992
7993     /**
7994      * The width of columns which have no width specified (defaults to 100)
7995      * @type Number
7996      */
7997     this.defaultWidth = 100;
7998
7999     /**
8000      * Default sortable of columns which have no sortable specified (defaults to false)
8001      * @type Boolean
8002      */
8003     this.defaultSortable = false;
8004
8005     this.addEvents({
8006         /**
8007              * @event widthchange
8008              * Fires when the width of a column changes.
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Number} newWidth The new width
8012              */
8013             "widthchange": true,
8014         /**
8015              * @event headerchange
8016              * Fires when the text of a header changes.
8017              * @param {ColumnModel} this
8018              * @param {Number} columnIndex The column index
8019              * @param {Number} newText The new header text
8020              */
8021             "headerchange": true,
8022         /**
8023              * @event hiddenchange
8024              * Fires when a column is hidden or "unhidden".
8025              * @param {ColumnModel} this
8026              * @param {Number} columnIndex The column index
8027              * @param {Boolean} hidden true if hidden, false otherwise
8028              */
8029             "hiddenchange": true,
8030             /**
8031          * @event columnmoved
8032          * Fires when a column is moved.
8033          * @param {ColumnModel} this
8034          * @param {Number} oldIndex
8035          * @param {Number} newIndex
8036          */
8037         "columnmoved" : true,
8038         /**
8039          * @event columlockchange
8040          * Fires when a column's locked state is changed
8041          * @param {ColumnModel} this
8042          * @param {Number} colIndex
8043          * @param {Boolean} locked true if locked
8044          */
8045         "columnlockchange" : true
8046     });
8047     Roo.grid.ColumnModel.superclass.constructor.call(this);
8048 };
8049 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8050     /**
8051      * @cfg {String} header The header text to display in the Grid view.
8052      */
8053         /**
8054      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8055      */
8056         /**
8057      * @cfg {String} smHeader Header at Bootsrap Small width
8058      */
8059         /**
8060      * @cfg {String} mdHeader Header at Bootsrap Medium width
8061      */
8062         /**
8063      * @cfg {String} lgHeader Header at Bootsrap Large width
8064      */
8065         /**
8066      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8067      */
8068     /**
8069      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8070      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8071      * specified, the column's index is used as an index into the Record's data Array.
8072      */
8073     /**
8074      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8075      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8076      */
8077     /**
8078      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8079      * Defaults to the value of the {@link #defaultSortable} property.
8080      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8081      */
8082     /**
8083      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8084      */
8085     /**
8086      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8087      */
8088     /**
8089      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8090      */
8091     /**
8092      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8093      */
8094     /**
8095      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8096      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8097      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8098      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8099      */
8100        /**
8101      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8102      */
8103     /**
8104      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8105      */
8106     /**
8107      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8108      */
8109     /**
8110      * @cfg {String} cursor (Optional)
8111      */
8112     /**
8113      * @cfg {String} tooltip (Optional)
8114      */
8115     /**
8116      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118     /**
8119      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8120      */
8121     /**
8122      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8123      */
8124     /**
8125      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8126      */
8127         /**
8128      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8129      */
8130     /**
8131      * Returns the id of the column at the specified index.
8132      * @param {Number} index The column index
8133      * @return {String} the id
8134      */
8135     getColumnId : function(index){
8136         return this.config[index].id;
8137     },
8138
8139     /**
8140      * Returns the column for a specified id.
8141      * @param {String} id The column id
8142      * @return {Object} the column
8143      */
8144     getColumnById : function(id){
8145         return this.lookup[id];
8146     },
8147
8148     
8149     /**
8150      * Returns the column Object for a specified dataIndex.
8151      * @param {String} dataIndex The column dataIndex
8152      * @return {Object|Boolean} the column or false if not found
8153      */
8154     getColumnByDataIndex: function(dataIndex){
8155         var index = this.findColumnIndex(dataIndex);
8156         return index > -1 ? this.config[index] : false;
8157     },
8158     
8159     /**
8160      * Returns the index for a specified column id.
8161      * @param {String} id The column id
8162      * @return {Number} the index, or -1 if not found
8163      */
8164     getIndexById : function(id){
8165         for(var i = 0, len = this.config.length; i < len; i++){
8166             if(this.config[i].id == id){
8167                 return i;
8168             }
8169         }
8170         return -1;
8171     },
8172     
8173     /**
8174      * Returns the index for a specified column dataIndex.
8175      * @param {String} dataIndex The column dataIndex
8176      * @return {Number} the index, or -1 if not found
8177      */
8178     
8179     findColumnIndex : function(dataIndex){
8180         for(var i = 0, len = this.config.length; i < len; i++){
8181             if(this.config[i].dataIndex == dataIndex){
8182                 return i;
8183             }
8184         }
8185         return -1;
8186     },
8187     
8188     
8189     moveColumn : function(oldIndex, newIndex){
8190         var c = this.config[oldIndex];
8191         this.config.splice(oldIndex, 1);
8192         this.config.splice(newIndex, 0, c);
8193         this.dataMap = null;
8194         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8195     },
8196
8197     isLocked : function(colIndex){
8198         return this.config[colIndex].locked === true;
8199     },
8200
8201     setLocked : function(colIndex, value, suppressEvent){
8202         if(this.isLocked(colIndex) == value){
8203             return;
8204         }
8205         this.config[colIndex].locked = value;
8206         if(!suppressEvent){
8207             this.fireEvent("columnlockchange", this, colIndex, value);
8208         }
8209     },
8210
8211     getTotalLockedWidth : function(){
8212         var totalWidth = 0;
8213         for(var i = 0; i < this.config.length; i++){
8214             if(this.isLocked(i) && !this.isHidden(i)){
8215                 this.totalWidth += this.getColumnWidth(i);
8216             }
8217         }
8218         return totalWidth;
8219     },
8220
8221     getLockedCount : function(){
8222         for(var i = 0, len = this.config.length; i < len; i++){
8223             if(!this.isLocked(i)){
8224                 return i;
8225             }
8226         }
8227         
8228         return this.config.length;
8229     },
8230
8231     /**
8232      * Returns the number of columns.
8233      * @return {Number}
8234      */
8235     getColumnCount : function(visibleOnly){
8236         if(visibleOnly === true){
8237             var c = 0;
8238             for(var i = 0, len = this.config.length; i < len; i++){
8239                 if(!this.isHidden(i)){
8240                     c++;
8241                 }
8242             }
8243             return c;
8244         }
8245         return this.config.length;
8246     },
8247
8248     /**
8249      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8250      * @param {Function} fn
8251      * @param {Object} scope (optional)
8252      * @return {Array} result
8253      */
8254     getColumnsBy : function(fn, scope){
8255         var r = [];
8256         for(var i = 0, len = this.config.length; i < len; i++){
8257             var c = this.config[i];
8258             if(fn.call(scope||this, c, i) === true){
8259                 r[r.length] = c;
8260             }
8261         }
8262         return r;
8263     },
8264
8265     /**
8266      * Returns true if the specified column is sortable.
8267      * @param {Number} col The column index
8268      * @return {Boolean}
8269      */
8270     isSortable : function(col){
8271         if(typeof this.config[col].sortable == "undefined"){
8272             return this.defaultSortable;
8273         }
8274         return this.config[col].sortable;
8275     },
8276
8277     /**
8278      * Returns the rendering (formatting) function defined for the column.
8279      * @param {Number} col The column index.
8280      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8281      */
8282     getRenderer : function(col){
8283         if(!this.config[col].renderer){
8284             return Roo.grid.ColumnModel.defaultRenderer;
8285         }
8286         return this.config[col].renderer;
8287     },
8288
8289     /**
8290      * Sets the rendering (formatting) function for a column.
8291      * @param {Number} col The column index
8292      * @param {Function} fn The function to use to process the cell's raw data
8293      * to return HTML markup for the grid view. The render function is called with
8294      * the following parameters:<ul>
8295      * <li>Data value.</li>
8296      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8297      * <li>css A CSS style string to apply to the table cell.</li>
8298      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8299      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8300      * <li>Row index</li>
8301      * <li>Column index</li>
8302      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8303      */
8304     setRenderer : function(col, fn){
8305         this.config[col].renderer = fn;
8306     },
8307
8308     /**
8309      * Returns the width for the specified column.
8310      * @param {Number} col The column index
8311      * @param (optional) {String} gridSize bootstrap width size.
8312      * @return {Number}
8313      */
8314     getColumnWidth : function(col, gridSize)
8315         {
8316                 var cfg = this.config[col];
8317                 
8318                 if (typeof(gridSize) == 'undefined') {
8319                         return cfg.width * 1 || this.defaultWidth;
8320                 }
8321                 if (gridSize === false) { // if we set it..
8322                         return cfg.width || false;
8323                 }
8324                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8325                 
8326                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8327                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8328                                 continue;
8329                         }
8330                         return cfg[ sizes[i] ];
8331                 }
8332                 return 1;
8333                 
8334     },
8335
8336     /**
8337      * Sets the width for a column.
8338      * @param {Number} col The column index
8339      * @param {Number} width The new width
8340      */
8341     setColumnWidth : function(col, width, suppressEvent){
8342         this.config[col].width = width;
8343         this.totalWidth = null;
8344         if(!suppressEvent){
8345              this.fireEvent("widthchange", this, col, width);
8346         }
8347     },
8348
8349     /**
8350      * Returns the total width of all columns.
8351      * @param {Boolean} includeHidden True to include hidden column widths
8352      * @return {Number}
8353      */
8354     getTotalWidth : function(includeHidden){
8355         if(!this.totalWidth){
8356             this.totalWidth = 0;
8357             for(var i = 0, len = this.config.length; i < len; i++){
8358                 if(includeHidden || !this.isHidden(i)){
8359                     this.totalWidth += this.getColumnWidth(i);
8360                 }
8361             }
8362         }
8363         return this.totalWidth;
8364     },
8365
8366     /**
8367      * Returns the header for the specified column.
8368      * @param {Number} col The column index
8369      * @return {String}
8370      */
8371     getColumnHeader : function(col){
8372         return this.config[col].header;
8373     },
8374
8375     /**
8376      * Sets the header for a column.
8377      * @param {Number} col The column index
8378      * @param {String} header The new header
8379      */
8380     setColumnHeader : function(col, header){
8381         this.config[col].header = header;
8382         this.fireEvent("headerchange", this, col, header);
8383     },
8384
8385     /**
8386      * Returns the tooltip for the specified column.
8387      * @param {Number} col The column index
8388      * @return {String}
8389      */
8390     getColumnTooltip : function(col){
8391             return this.config[col].tooltip;
8392     },
8393     /**
8394      * Sets the tooltip for a column.
8395      * @param {Number} col The column index
8396      * @param {String} tooltip The new tooltip
8397      */
8398     setColumnTooltip : function(col, tooltip){
8399             this.config[col].tooltip = tooltip;
8400     },
8401
8402     /**
8403      * Returns the dataIndex for the specified column.
8404      * @param {Number} col The column index
8405      * @return {Number}
8406      */
8407     getDataIndex : function(col){
8408         return this.config[col].dataIndex;
8409     },
8410
8411     /**
8412      * Sets the dataIndex for a column.
8413      * @param {Number} col The column index
8414      * @param {Number} dataIndex The new dataIndex
8415      */
8416     setDataIndex : function(col, dataIndex){
8417         this.config[col].dataIndex = dataIndex;
8418     },
8419
8420     
8421     
8422     /**
8423      * Returns true if the cell is editable.
8424      * @param {Number} colIndex The column index
8425      * @param {Number} rowIndex The row index - this is nto actually used..?
8426      * @return {Boolean}
8427      */
8428     isCellEditable : function(colIndex, rowIndex){
8429         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8430     },
8431
8432     /**
8433      * Returns the editor defined for the cell/column.
8434      * return false or null to disable editing.
8435      * @param {Number} colIndex The column index
8436      * @param {Number} rowIndex The row index
8437      * @return {Object}
8438      */
8439     getCellEditor : function(colIndex, rowIndex){
8440         return this.config[colIndex].editor;
8441     },
8442
8443     /**
8444      * Sets if a column is editable.
8445      * @param {Number} col The column index
8446      * @param {Boolean} editable True if the column is editable
8447      */
8448     setEditable : function(col, editable){
8449         this.config[col].editable = editable;
8450     },
8451
8452
8453     /**
8454      * Returns true if the column is hidden.
8455      * @param {Number} colIndex The column index
8456      * @return {Boolean}
8457      */
8458     isHidden : function(colIndex){
8459         return this.config[colIndex].hidden;
8460     },
8461
8462
8463     /**
8464      * Returns true if the column width cannot be changed
8465      */
8466     isFixed : function(colIndex){
8467         return this.config[colIndex].fixed;
8468     },
8469
8470     /**
8471      * Returns true if the column can be resized
8472      * @return {Boolean}
8473      */
8474     isResizable : function(colIndex){
8475         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8476     },
8477     /**
8478      * Sets if a column is hidden.
8479      * @param {Number} colIndex The column index
8480      * @param {Boolean} hidden True if the column is hidden
8481      */
8482     setHidden : function(colIndex, hidden){
8483         this.config[colIndex].hidden = hidden;
8484         this.totalWidth = null;
8485         this.fireEvent("hiddenchange", this, colIndex, hidden);
8486     },
8487
8488     /**
8489      * Sets the editor for a column.
8490      * @param {Number} col The column index
8491      * @param {Object} editor The editor object
8492      */
8493     setEditor : function(col, editor){
8494         this.config[col].editor = editor;
8495     },
8496     /**
8497      * Add a column (experimental...) - defaults to adding to the end..
8498      * @param {Object} config 
8499     */
8500     addColumn : function(c)
8501     {
8502     
8503         var i = this.config.length;
8504         this.config[i] = c;
8505         
8506         if(typeof c.dataIndex == "undefined"){
8507             c.dataIndex = i;
8508         }
8509         if(typeof c.renderer == "string"){
8510             c.renderer = Roo.util.Format[c.renderer];
8511         }
8512         if(typeof c.id == "undefined"){
8513             c.id = Roo.id();
8514         }
8515         if(c.editor && c.editor.xtype){
8516             c.editor  = Roo.factory(c.editor, Roo.grid);
8517         }
8518         if(c.editor && c.editor.isFormField){
8519             c.editor = new Roo.grid.GridEditor(c.editor);
8520         }
8521         this.lookup[c.id] = c;
8522     }
8523     
8524 });
8525
8526 Roo.grid.ColumnModel.defaultRenderer = function(value)
8527 {
8528     if(typeof value == "object") {
8529         return value;
8530     }
8531         if(typeof value == "string" && value.length < 1){
8532             return "&#160;";
8533         }
8534     
8535         return String.format("{0}", value);
8536 };
8537
8538 // Alias for backwards compatibility
8539 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8540 /*
8541  * Based on:
8542  * Ext JS Library 1.1.1
8543  * Copyright(c) 2006-2007, Ext JS, LLC.
8544  *
8545  * Originally Released Under LGPL - original licence link has changed is not relivant.
8546  *
8547  * Fork - LGPL
8548  * <script type="text/javascript">
8549  */
8550  
8551 /**
8552  * @class Roo.LoadMask
8553  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8554  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8555  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8556  * element's UpdateManager load indicator and will be destroyed after the initial load.
8557  * @constructor
8558  * Create a new LoadMask
8559  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8560  * @param {Object} config The config object
8561  */
8562 Roo.LoadMask = function(el, config){
8563     this.el = Roo.get(el);
8564     Roo.apply(this, config);
8565     if(this.store){
8566         this.store.on('beforeload', this.onBeforeLoad, this);
8567         this.store.on('load', this.onLoad, this);
8568         this.store.on('loadexception', this.onLoadException, this);
8569         this.removeMask = false;
8570     }else{
8571         var um = this.el.getUpdateManager();
8572         um.showLoadIndicator = false; // disable the default indicator
8573         um.on('beforeupdate', this.onBeforeLoad, this);
8574         um.on('update', this.onLoad, this);
8575         um.on('failure', this.onLoad, this);
8576         this.removeMask = true;
8577     }
8578 };
8579
8580 Roo.LoadMask.prototype = {
8581     /**
8582      * @cfg {Boolean} removeMask
8583      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8584      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8585      */
8586     removeMask : false,
8587     /**
8588      * @cfg {String} msg
8589      * The text to display in a centered loading message box (defaults to 'Loading...')
8590      */
8591     msg : 'Loading...',
8592     /**
8593      * @cfg {String} msgCls
8594      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8595      */
8596     msgCls : 'x-mask-loading',
8597
8598     /**
8599      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8600      * @type Boolean
8601      */
8602     disabled: false,
8603
8604     /**
8605      * Disables the mask to prevent it from being displayed
8606      */
8607     disable : function(){
8608        this.disabled = true;
8609     },
8610
8611     /**
8612      * Enables the mask so that it can be displayed
8613      */
8614     enable : function(){
8615         this.disabled = false;
8616     },
8617     
8618     onLoadException : function()
8619     {
8620         Roo.log(arguments);
8621         
8622         if (typeof(arguments[3]) != 'undefined') {
8623             Roo.MessageBox.alert("Error loading",arguments[3]);
8624         } 
8625         /*
8626         try {
8627             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8628                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8629             }   
8630         } catch(e) {
8631             
8632         }
8633         */
8634     
8635         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8636     },
8637     // private
8638     onLoad : function()
8639     {
8640         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8641     },
8642
8643     // private
8644     onBeforeLoad : function(){
8645         if(!this.disabled){
8646             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8647         }
8648     },
8649
8650     // private
8651     destroy : function(){
8652         if(this.store){
8653             this.store.un('beforeload', this.onBeforeLoad, this);
8654             this.store.un('load', this.onLoad, this);
8655             this.store.un('loadexception', this.onLoadException, this);
8656         }else{
8657             var um = this.el.getUpdateManager();
8658             um.un('beforeupdate', this.onBeforeLoad, this);
8659             um.un('update', this.onLoad, this);
8660             um.un('failure', this.onLoad, this);
8661         }
8662     }
8663 };/**
8664  * @class Roo.bootstrap.Table
8665  * @licence LGBL
8666  * @extends Roo.bootstrap.Component
8667  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8668  * Similar to Roo.grid.Grid
8669  * <pre><code>
8670  var table = Roo.factory({
8671     xtype : 'Table',
8672     xns : Roo.bootstrap,
8673     autoSizeColumns: true,
8674     
8675     
8676     store : {
8677         xtype : 'Store',
8678         xns : Roo.data,
8679         remoteSort : true,
8680         sortInfo : { direction : 'ASC', field: 'name' },
8681         proxy : {
8682            xtype : 'HttpProxy',
8683            xns : Roo.data,
8684            method : 'GET',
8685            url : 'https://example.com/some.data.url.json'
8686         },
8687         reader : {
8688            xtype : 'JsonReader',
8689            xns : Roo.data,
8690            fields : [ 'id', 'name', whatever' ],
8691            id : 'id',
8692            root : 'data'
8693         }
8694     },
8695     cm : [
8696         {
8697             xtype : 'ColumnModel',
8698             xns : Roo.grid,
8699             align : 'center',
8700             cursor : 'pointer',
8701             dataIndex : 'is_in_group',
8702             header : "Name",
8703             sortable : true,
8704             renderer : function(v, x , r) {  
8705             
8706                 return String.format("{0}", v)
8707             }
8708             width : 3
8709         } // more columns..
8710     ],
8711     selModel : {
8712         xtype : 'RowSelectionModel',
8713         xns : Roo.bootstrap.Table
8714         // you can add listeners to catch selection change here....
8715     }
8716      
8717
8718  });
8719  // set any options
8720  grid.render(Roo.get("some-div"));
8721 </code></pre>
8722
8723 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8724
8725
8726
8727  *
8728  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8729  * @cfg {Roo.data.Store} store The data store to use
8730  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8731  * 
8732  * @cfg {String} cls table class
8733  *
8734  * 
8735  * @cfg {boolean} striped Should the rows be alternative striped
8736  * @cfg {boolean} bordered Add borders to the table
8737  * @cfg {boolean} hover Add hover highlighting
8738  * @cfg {boolean} condensed Format condensed
8739  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8740  *                also adds table-responsive (see bootstrap docs for details)
8741  * @cfg {Boolean} loadMask (true|false) default false
8742  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8743  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8744  * @cfg {Boolean} rowSelection (true|false) default false
8745  * @cfg {Boolean} cellSelection (true|false) default false
8746  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8747  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8748  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8749  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8750  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8751  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8752  * 
8753  * @constructor
8754  * Create a new Table
8755  * @param {Object} config The config object
8756  */
8757
8758 Roo.bootstrap.Table = function(config)
8759 {
8760     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8761      
8762     // BC...
8763     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8764     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8765     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8766     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8767     
8768     this.view = this; // compat with grid.
8769     
8770     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8771     if (this.sm) {
8772         this.sm.grid = this;
8773         this.selModel = Roo.factory(this.sm, Roo.grid);
8774         this.sm = this.selModel;
8775         this.sm.xmodule = this.xmodule || false;
8776     }
8777     
8778     if (this.cm && typeof(this.cm.config) == 'undefined') {
8779         this.colModel = new Roo.grid.ColumnModel(this.cm);
8780         this.cm = this.colModel;
8781         this.cm.xmodule = this.xmodule || false;
8782     }
8783     if (this.store) {
8784         this.store= Roo.factory(this.store, Roo.data);
8785         this.ds = this.store;
8786         this.ds.xmodule = this.xmodule || false;
8787          
8788     }
8789     if (this.footer && this.store) {
8790         this.footer.dataSource = this.ds;
8791         this.footer = Roo.factory(this.footer);
8792     }
8793     
8794     /** @private */
8795     this.addEvents({
8796         /**
8797          * @event cellclick
8798          * Fires when a cell is clicked
8799          * @param {Roo.bootstrap.Table} this
8800          * @param {Roo.Element} el
8801          * @param {Number} rowIndex
8802          * @param {Number} columnIndex
8803          * @param {Roo.EventObject} e
8804          */
8805         "cellclick" : true,
8806         /**
8807          * @event celldblclick
8808          * Fires when a cell is double clicked
8809          * @param {Roo.bootstrap.Table} this
8810          * @param {Roo.Element} el
8811          * @param {Number} rowIndex
8812          * @param {Number} columnIndex
8813          * @param {Roo.EventObject} e
8814          */
8815         "celldblclick" : true,
8816         /**
8817          * @event rowclick
8818          * Fires when a row is clicked
8819          * @param {Roo.bootstrap.Table} this
8820          * @param {Roo.Element} el
8821          * @param {Number} rowIndex
8822          * @param {Roo.EventObject} e
8823          */
8824         "rowclick" : true,
8825         /**
8826          * @event rowdblclick
8827          * Fires when a row is double clicked
8828          * @param {Roo.bootstrap.Table} this
8829          * @param {Roo.Element} el
8830          * @param {Number} rowIndex
8831          * @param {Roo.EventObject} e
8832          */
8833         "rowdblclick" : true,
8834         /**
8835          * @event mouseover
8836          * Fires when a mouseover occur
8837          * @param {Roo.bootstrap.Table} this
8838          * @param {Roo.Element} el
8839          * @param {Number} rowIndex
8840          * @param {Number} columnIndex
8841          * @param {Roo.EventObject} e
8842          */
8843         "mouseover" : true,
8844         /**
8845          * @event mouseout
8846          * Fires when a mouseout occur
8847          * @param {Roo.bootstrap.Table} this
8848          * @param {Roo.Element} el
8849          * @param {Number} rowIndex
8850          * @param {Number} columnIndex
8851          * @param {Roo.EventObject} e
8852          */
8853         "mouseout" : true,
8854         /**
8855          * @event rowclass
8856          * Fires when a row is rendered, so you can change add a style to it.
8857          * @param {Roo.bootstrap.Table} this
8858          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8859          */
8860         'rowclass' : true,
8861           /**
8862          * @event rowsrendered
8863          * Fires when all the  rows have been rendered
8864          * @param {Roo.bootstrap.Table} this
8865          */
8866         'rowsrendered' : true,
8867         /**
8868          * @event contextmenu
8869          * The raw contextmenu event for the entire grid.
8870          * @param {Roo.EventObject} e
8871          */
8872         "contextmenu" : true,
8873         /**
8874          * @event rowcontextmenu
8875          * Fires when a row is right clicked
8876          * @param {Roo.bootstrap.Table} this
8877          * @param {Number} rowIndex
8878          * @param {Roo.EventObject} e
8879          */
8880         "rowcontextmenu" : true,
8881         /**
8882          * @event cellcontextmenu
8883          * Fires when a cell is right clicked
8884          * @param {Roo.bootstrap.Table} this
8885          * @param {Number} rowIndex
8886          * @param {Number} cellIndex
8887          * @param {Roo.EventObject} e
8888          */
8889          "cellcontextmenu" : true,
8890          /**
8891          * @event headercontextmenu
8892          * Fires when a header is right clicked
8893          * @param {Roo.bootstrap.Table} this
8894          * @param {Number} columnIndex
8895          * @param {Roo.EventObject} e
8896          */
8897         "headercontextmenu" : true,
8898         /**
8899          * @event mousedown
8900          * The raw mousedown event for the entire grid.
8901          * @param {Roo.EventObject} e
8902          */
8903         "mousedown" : true
8904         
8905     });
8906 };
8907
8908 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8909     
8910     cls: false,
8911     
8912     striped : false,
8913     scrollBody : false,
8914     bordered: false,
8915     hover:  false,
8916     condensed : false,
8917     responsive : false,
8918     sm : false,
8919     cm : false,
8920     store : false,
8921     loadMask : false,
8922     footerShow : true,
8923     headerShow : true,
8924     enableColumnResize: true,
8925   
8926     rowSelection : false,
8927     cellSelection : false,
8928     layout : false,
8929
8930     minColumnWidth : 50,
8931     
8932     // Roo.Element - the tbody
8933     bodyEl: false,  // <tbody> Roo.Element - thead element    
8934     headEl: false,  // <thead> Roo.Element - thead element
8935     resizeProxy : false, // proxy element for dragging?
8936
8937
8938     
8939     container: false, // used by gridpanel...
8940     
8941     lazyLoad : false,
8942     
8943     CSS : Roo.util.CSS,
8944     
8945     auto_hide_footer : false,
8946     
8947     view: false, // actually points to this..
8948     
8949     getAutoCreate : function()
8950     {
8951         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'table',
8955             cls : 'table', 
8956             cn : []
8957         };
8958         // this get's auto added by panel.Grid
8959         if (this.scrollBody) {
8960             cfg.cls += ' table-body-fixed';
8961         }    
8962         if (this.striped) {
8963             cfg.cls += ' table-striped';
8964         }
8965         
8966         if (this.hover) {
8967             cfg.cls += ' table-hover';
8968         }
8969         if (this.bordered) {
8970             cfg.cls += ' table-bordered';
8971         }
8972         if (this.condensed) {
8973             cfg.cls += ' table-condensed';
8974         }
8975         
8976         if (this.responsive) {
8977             cfg.cls += ' table-responsive';
8978         }
8979         
8980         if (this.cls) {
8981             cfg.cls+=  ' ' +this.cls;
8982         }
8983         
8984         
8985         
8986         if (this.layout) {
8987             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8988         }
8989         
8990         if(this.store || this.cm){
8991             if(this.headerShow){
8992                 cfg.cn.push(this.renderHeader());
8993             }
8994             
8995             cfg.cn.push(this.renderBody());
8996             
8997             if(this.footerShow){
8998                 cfg.cn.push(this.renderFooter());
8999             }
9000             // where does this come from?
9001             //cfg.cls+=  ' TableGrid';
9002         }
9003         
9004         return { cn : [ cfg ] };
9005     },
9006     
9007     initEvents : function()
9008     {   
9009         if(!this.store || !this.cm){
9010             return;
9011         }
9012         if (this.selModel) {
9013             this.selModel.initEvents();
9014         }
9015         
9016         
9017         //Roo.log('initEvents with ds!!!!');
9018         
9019         this.bodyEl = this.el.select('tbody', true).first();
9020         this.headEl = this.el.select('thead', true).first();
9021         this.mainFoot = this.el.select('tfoot', true).first();
9022         
9023         
9024         
9025         
9026         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9027             e.on('click', this.sort, this);
9028         }, this);
9029         
9030         
9031         // why is this done????? = it breaks dialogs??
9032         //this.parent().el.setStyle('position', 'relative');
9033         
9034         
9035         if (this.footer) {
9036             this.footer.parentId = this.id;
9037             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9038             
9039             if(this.lazyLoad){
9040                 this.el.select('tfoot tr td').first().addClass('hide');
9041             }
9042         } 
9043         
9044         if(this.loadMask) {
9045             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9046         }
9047         
9048         this.store.on('load', this.onLoad, this);
9049         this.store.on('beforeload', this.onBeforeLoad, this);
9050         this.store.on('update', this.onUpdate, this);
9051         this.store.on('add', this.onAdd, this);
9052         this.store.on("clear", this.clear, this);
9053         
9054         this.el.on("contextmenu", this.onContextMenu, this);
9055         
9056         
9057         this.cm.on("headerchange", this.onHeaderChange, this);
9058         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9059
9060  //?? does bodyEl get replaced on render?
9061         this.bodyEl.on("click", this.onClick, this);
9062         this.bodyEl.on("dblclick", this.onDblClick, this);        
9063         this.bodyEl.on('scroll', this.onBodyScroll, this);
9064
9065         // guessing mainbody will work - this relays usually caught by selmodel at present.
9066         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9067   
9068   
9069         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9070         
9071   
9072         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9073             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9074         }
9075         
9076         this.initCSS();
9077     },
9078     // Compatibility with grid - we implement all the view features at present.
9079     getView : function()
9080     {
9081         return this;
9082     },
9083     
9084     initCSS : function()
9085     {
9086         
9087         
9088         var cm = this.cm, styles = [];
9089         this.CSS.removeStyleSheet(this.id + '-cssrules');
9090         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9091         // we can honour xs/sm/md/xl  as widths...
9092         // we first have to decide what widht we are currently at...
9093         var sz = Roo.getGridSize();
9094         
9095         var total = 0;
9096         var last = -1;
9097         var cols = []; // visable cols.
9098         var total_abs = 0;
9099         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9100             var w = cm.getColumnWidth(i, false);
9101             if(cm.isHidden(i)){
9102                 cols.push( { rel : false, abs : 0 });
9103                 continue;
9104             }
9105             if (w !== false) {
9106                 cols.push( { rel : false, abs : w });
9107                 total_abs += w;
9108                 last = i; // not really..
9109                 continue;
9110             }
9111             var w = cm.getColumnWidth(i, sz);
9112             if (w > 0) {
9113                 last = i
9114             }
9115             total += w;
9116             cols.push( { rel : w, abs : false });
9117         }
9118         
9119         var avail = this.bodyEl.dom.clientWidth - total_abs;
9120         
9121         var unitWidth = Math.floor(avail / total);
9122         var rem = avail - (unitWidth * total);
9123         
9124         var hidden, width, pos = 0 , splithide , left;
9125         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9126             
9127             hidden = 'display:none;';
9128             left = '';
9129             width  = 'width:0px;';
9130             splithide = '';
9131             if(!cm.isHidden(i)){
9132                 hidden = '';
9133                 
9134                 
9135                 // we can honour xs/sm/md/xl ?
9136                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9137                 if (w===0) {
9138                     hidden = 'display:none;';
9139                 }
9140                 // width should return a small number...
9141                 if (i == last) {
9142                     w+=rem; // add the remaining with..
9143                 }
9144                 pos += w;
9145                 left = "left:" + (pos -4) + "px;";
9146                 width = "width:" + w+ "px;";
9147                 
9148             }
9149             if (this.responsive) {
9150                 width = '';
9151                 left = '';
9152                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9153                 splithide = 'display: none;';
9154             }
9155             
9156             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9157             if (this.headEl) {
9158                 if (i == last) {
9159                     splithide = 'display:none;';
9160                 }
9161                 
9162                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9163                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9164                 );
9165             }
9166             
9167         }
9168         //Roo.log(styles.join(''));
9169         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9170         
9171     },
9172     
9173     
9174     
9175     onContextMenu : function(e, t)
9176     {
9177         this.processEvent("contextmenu", e);
9178     },
9179     
9180     processEvent : function(name, e)
9181     {
9182         if (name != 'touchstart' ) {
9183             this.fireEvent(name, e);    
9184         }
9185         
9186         var t = e.getTarget();
9187         
9188         var cell = Roo.get(t);
9189         
9190         if(!cell){
9191             return;
9192         }
9193         
9194         if(cell.findParent('tfoot', false, true)){
9195             return;
9196         }
9197         
9198         if(cell.findParent('thead', false, true)){
9199             
9200             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9201                 cell = Roo.get(t).findParent('th', false, true);
9202                 if (!cell) {
9203                     Roo.log("failed to find th in thead?");
9204                     Roo.log(e.getTarget());
9205                     return;
9206                 }
9207             }
9208             
9209             var cellIndex = cell.dom.cellIndex;
9210             
9211             var ename = name == 'touchstart' ? 'click' : name;
9212             this.fireEvent("header" + ename, this, cellIndex, e);
9213             
9214             return;
9215         }
9216         
9217         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9218             cell = Roo.get(t).findParent('td', false, true);
9219             if (!cell) {
9220                 Roo.log("failed to find th in tbody?");
9221                 Roo.log(e.getTarget());
9222                 return;
9223             }
9224         }
9225         
9226         var row = cell.findParent('tr', false, true);
9227         var cellIndex = cell.dom.cellIndex;
9228         var rowIndex = row.dom.rowIndex - 1;
9229         
9230         if(row !== false){
9231             
9232             this.fireEvent("row" + name, this, rowIndex, e);
9233             
9234             if(cell !== false){
9235             
9236                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9237             }
9238         }
9239         
9240     },
9241     
9242     onMouseover : function(e, el)
9243     {
9244         var cell = Roo.get(el);
9245         
9246         if(!cell){
9247             return;
9248         }
9249         
9250         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9251             cell = cell.findParent('td', false, true);
9252         }
9253         
9254         var row = cell.findParent('tr', false, true);
9255         var cellIndex = cell.dom.cellIndex;
9256         var rowIndex = row.dom.rowIndex - 1; // start from 0
9257         
9258         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9259         
9260     },
9261     
9262     onMouseout : function(e, el)
9263     {
9264         var cell = Roo.get(el);
9265         
9266         if(!cell){
9267             return;
9268         }
9269         
9270         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9271             cell = cell.findParent('td', false, true);
9272         }
9273         
9274         var row = cell.findParent('tr', false, true);
9275         var cellIndex = cell.dom.cellIndex;
9276         var rowIndex = row.dom.rowIndex - 1; // start from 0
9277         
9278         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9279         
9280     },
9281     
9282     onClick : function(e, el)
9283     {
9284         var cell = Roo.get(el);
9285         
9286         if(!cell || (!this.cellSelection && !this.rowSelection)){
9287             return;
9288         }
9289         
9290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9291             cell = cell.findParent('td', false, true);
9292         }
9293         
9294         if(!cell || typeof(cell) == 'undefined'){
9295             return;
9296         }
9297         
9298         var row = cell.findParent('tr', false, true);
9299         
9300         if(!row || typeof(row) == 'undefined'){
9301             return;
9302         }
9303         
9304         var cellIndex = cell.dom.cellIndex;
9305         var rowIndex = this.getRowIndex(row);
9306         
9307         // why??? - should these not be based on SelectionModel?
9308         //if(this.cellSelection){
9309             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9310         //}
9311         
9312         //if(this.rowSelection){
9313             this.fireEvent('rowclick', this, row, rowIndex, e);
9314         //}
9315          
9316     },
9317         
9318     onDblClick : function(e,el)
9319     {
9320         var cell = Roo.get(el);
9321         
9322         if(!cell || (!this.cellSelection && !this.rowSelection)){
9323             return;
9324         }
9325         
9326         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9327             cell = cell.findParent('td', false, true);
9328         }
9329         
9330         if(!cell || typeof(cell) == 'undefined'){
9331             return;
9332         }
9333         
9334         var row = cell.findParent('tr', false, true);
9335         
9336         if(!row || typeof(row) == 'undefined'){
9337             return;
9338         }
9339         
9340         var cellIndex = cell.dom.cellIndex;
9341         var rowIndex = this.getRowIndex(row);
9342         
9343         if(this.cellSelection){
9344             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9345         }
9346         
9347         if(this.rowSelection){
9348             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9349         }
9350     },
9351     findRowIndex : function(el)
9352     {
9353         var cell = Roo.get(el);
9354         if(!cell) {
9355             return false;
9356         }
9357         var row = cell.findParent('tr', false, true);
9358         
9359         if(!row || typeof(row) == 'undefined'){
9360             return false;
9361         }
9362         return this.getRowIndex(row);
9363     },
9364     sort : function(e,el)
9365     {
9366         var col = Roo.get(el);
9367         
9368         if(!col.hasClass('sortable')){
9369             return;
9370         }
9371         
9372         var sort = col.attr('sort');
9373         var dir = 'ASC';
9374         
9375         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9376             dir = 'DESC';
9377         }
9378         
9379         this.store.sortInfo = {field : sort, direction : dir};
9380         
9381         if (this.footer) {
9382             Roo.log("calling footer first");
9383             this.footer.onClick('first');
9384         } else {
9385         
9386             this.store.load({ params : { start : 0 } });
9387         }
9388     },
9389     
9390     renderHeader : function()
9391     {
9392         var header = {
9393             tag: 'thead',
9394             cn : []
9395         };
9396         
9397         var cm = this.cm;
9398         this.totalWidth = 0;
9399         
9400         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9401             
9402             var config = cm.config[i];
9403             
9404             var c = {
9405                 tag: 'th',
9406                 cls : 'x-hcol-' + i,
9407                 style : '',
9408                 
9409                 html: cm.getColumnHeader(i)
9410             };
9411             
9412             var tooltip = cm.getColumnTooltip(i);
9413             if (tooltip) {
9414                 c.tooltip = tooltip;
9415             }
9416             
9417             
9418             var hh = '';
9419             
9420             if(typeof(config.sortable) != 'undefined' && config.sortable){
9421                 c.cls += ' sortable';
9422                 c.html = '<i class="fa"></i>' + c.html;
9423             }
9424             
9425             // could use BS4 hidden-..-down 
9426             
9427             if(typeof(config.lgHeader) != 'undefined'){
9428                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9429             }
9430             
9431             if(typeof(config.mdHeader) != 'undefined'){
9432                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9433             }
9434             
9435             if(typeof(config.smHeader) != 'undefined'){
9436                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9437             }
9438             
9439             if(typeof(config.xsHeader) != 'undefined'){
9440                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9441             }
9442             
9443             if(hh.length){
9444                 c.html = hh;
9445             }
9446             
9447             if(typeof(config.tooltip) != 'undefined'){
9448                 c.tooltip = config.tooltip;
9449             }
9450             
9451             if(typeof(config.colspan) != 'undefined'){
9452                 c.colspan = config.colspan;
9453             }
9454             
9455             // hidden is handled by CSS now
9456             
9457             if(typeof(config.dataIndex) != 'undefined'){
9458                 c.sort = config.dataIndex;
9459             }
9460             
9461            
9462             
9463             if(typeof(config.align) != 'undefined' && config.align.length){
9464                 c.style += ' text-align:' + config.align + ';';
9465             }
9466             
9467             /* width is done in CSS
9468              *if(typeof(config.width) != 'undefined'){
9469                 c.style += ' width:' + config.width + 'px;';
9470                 this.totalWidth += config.width;
9471             } else {
9472                 this.totalWidth += 100; // assume minimum of 100 per column?
9473             }
9474             */
9475             
9476             if(typeof(config.cls) != 'undefined'){
9477                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9478             }
9479             // this is the bit that doesnt reall work at all...
9480             
9481             if (this.responsive) {
9482                  
9483             
9484                 ['xs','sm','md','lg'].map(function(size){
9485                     
9486                     if(typeof(config[size]) == 'undefined'){
9487                         return;
9488                     }
9489                      
9490                     if (!config[size]) { // 0 = hidden
9491                         // BS 4 '0' is treated as hide that column and below.
9492                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9493                         return;
9494                     }
9495                     
9496                     c.cls += ' col-' + size + '-' + config[size] + (
9497                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9498                     );
9499                     
9500                     
9501                 });
9502             }
9503             // at the end?
9504             
9505             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9506             
9507             
9508             
9509             
9510             header.cn.push(c)
9511         }
9512         
9513         return header;
9514     },
9515     
9516     renderBody : function()
9517     {
9518         var body = {
9519             tag: 'tbody',
9520             cn : [
9521                 {
9522                     tag: 'tr',
9523                     cn : [
9524                         {
9525                             tag : 'td',
9526                             colspan :  this.cm.getColumnCount()
9527                         }
9528                     ]
9529                 }
9530             ]
9531         };
9532         
9533         return body;
9534     },
9535     
9536     renderFooter : function()
9537     {
9538         var footer = {
9539             tag: 'tfoot',
9540             cn : [
9541                 {
9542                     tag: 'tr',
9543                     cn : [
9544                         {
9545                             tag : 'td',
9546                             colspan :  this.cm.getColumnCount()
9547                         }
9548                     ]
9549                 }
9550             ]
9551         };
9552         
9553         return footer;
9554     },
9555     
9556     
9557     
9558     onLoad : function()
9559     {
9560 //        Roo.log('ds onload');
9561         this.clear();
9562         
9563         var _this = this;
9564         var cm = this.cm;
9565         var ds = this.store;
9566         
9567         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9568             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9569             if (_this.store.sortInfo) {
9570                     
9571                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9572                     e.select('i', true).addClass(['fa-arrow-up']);
9573                 }
9574                 
9575                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9576                     e.select('i', true).addClass(['fa-arrow-down']);
9577                 }
9578             }
9579         });
9580         
9581         var tbody =  this.bodyEl;
9582               
9583         if(ds.getCount() > 0){
9584             ds.data.each(function(d,rowIndex){
9585                 var row =  this.renderRow(cm, ds, rowIndex);
9586                 
9587                 tbody.createChild(row);
9588                 
9589                 var _this = this;
9590                 
9591                 if(row.cellObjects.length){
9592                     Roo.each(row.cellObjects, function(r){
9593                         _this.renderCellObject(r);
9594                     })
9595                 }
9596                 
9597             }, this);
9598         }
9599         
9600         var tfoot = this.el.select('tfoot', true).first();
9601         
9602         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9603             
9604             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9605             
9606             var total = this.ds.getTotalCount();
9607             
9608             if(this.footer.pageSize < total){
9609                 this.mainFoot.show();
9610             }
9611         }
9612         
9613         Roo.each(this.el.select('tbody td', true).elements, function(e){
9614             e.on('mouseover', _this.onMouseover, _this);
9615         });
9616         
9617         Roo.each(this.el.select('tbody td', true).elements, function(e){
9618             e.on('mouseout', _this.onMouseout, _this);
9619         });
9620         this.fireEvent('rowsrendered', this);
9621         
9622         this.autoSize();
9623         
9624         this.initCSS(); /// resize cols
9625
9626         
9627     },
9628     
9629     
9630     onUpdate : function(ds,record)
9631     {
9632         this.refreshRow(record);
9633         this.autoSize();
9634     },
9635     
9636     onRemove : function(ds, record, index, isUpdate){
9637         if(isUpdate !== true){
9638             this.fireEvent("beforerowremoved", this, index, record);
9639         }
9640         var bt = this.bodyEl.dom;
9641         
9642         var rows = this.el.select('tbody > tr', true).elements;
9643         
9644         if(typeof(rows[index]) != 'undefined'){
9645             bt.removeChild(rows[index].dom);
9646         }
9647         
9648 //        if(bt.rows[index]){
9649 //            bt.removeChild(bt.rows[index]);
9650 //        }
9651         
9652         if(isUpdate !== true){
9653             //this.stripeRows(index);
9654             //this.syncRowHeights(index, index);
9655             //this.layout();
9656             this.fireEvent("rowremoved", this, index, record);
9657         }
9658     },
9659     
9660     onAdd : function(ds, records, rowIndex)
9661     {
9662         //Roo.log('on Add called');
9663         // - note this does not handle multiple adding very well..
9664         var bt = this.bodyEl.dom;
9665         for (var i =0 ; i < records.length;i++) {
9666             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9667             //Roo.log(records[i]);
9668             //Roo.log(this.store.getAt(rowIndex+i));
9669             this.insertRow(this.store, rowIndex + i, false);
9670             return;
9671         }
9672         
9673     },
9674     
9675     
9676     refreshRow : function(record){
9677         var ds = this.store, index;
9678         if(typeof record == 'number'){
9679             index = record;
9680             record = ds.getAt(index);
9681         }else{
9682             index = ds.indexOf(record);
9683             if (index < 0) {
9684                 return; // should not happen - but seems to 
9685             }
9686         }
9687         this.insertRow(ds, index, true);
9688         this.autoSize();
9689         this.onRemove(ds, record, index+1, true);
9690         this.autoSize();
9691         //this.syncRowHeights(index, index);
9692         //this.layout();
9693         this.fireEvent("rowupdated", this, index, record);
9694     },
9695     // private - called by RowSelection
9696     onRowSelect : function(rowIndex){
9697         var row = this.getRowDom(rowIndex);
9698         row.addClass(['bg-info','info']);
9699     },
9700     // private - called by RowSelection
9701     onRowDeselect : function(rowIndex)
9702     {
9703         if (rowIndex < 0) {
9704             return;
9705         }
9706         var row = this.getRowDom(rowIndex);
9707         row.removeClass(['bg-info','info']);
9708     },
9709       /**
9710      * Focuses the specified row.
9711      * @param {Number} row The row index
9712      */
9713     focusRow : function(row)
9714     {
9715         //Roo.log('GridView.focusRow');
9716         var x = this.bodyEl.dom.scrollLeft;
9717         this.focusCell(row, 0, false);
9718         this.bodyEl.dom.scrollLeft = x;
9719
9720     },
9721      /**
9722      * Focuses the specified cell.
9723      * @param {Number} row The row index
9724      * @param {Number} col The column index
9725      * @param {Boolean} hscroll false to disable horizontal scrolling
9726      */
9727     focusCell : function(row, col, hscroll)
9728     {
9729         //Roo.log('GridView.focusCell');
9730         var el = this.ensureVisible(row, col, hscroll);
9731         // not sure what focusEL achives = it's a <a> pos relative 
9732         //this.focusEl.alignTo(el, "tl-tl");
9733         //if(Roo.isGecko){
9734         //    this.focusEl.focus();
9735         //}else{
9736         //    this.focusEl.focus.defer(1, this.focusEl);
9737         //}
9738     },
9739     
9740      /**
9741      * Scrolls the specified cell into view
9742      * @param {Number} row The row index
9743      * @param {Number} col The column index
9744      * @param {Boolean} hscroll false to disable horizontal scrolling
9745      */
9746     ensureVisible : function(row, col, hscroll)
9747     {
9748         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9749         //return null; //disable for testing.
9750         if(typeof row != "number"){
9751             row = row.rowIndex;
9752         }
9753         if(row < 0 && row >= this.ds.getCount()){
9754             return  null;
9755         }
9756         col = (col !== undefined ? col : 0);
9757         var cm = this.cm;
9758         while(cm.isHidden(col)){
9759             col++;
9760         }
9761
9762         var el = this.getCellDom(row, col);
9763         if(!el){
9764             return null;
9765         }
9766         var c = this.bodyEl.dom;
9767
9768         var ctop = parseInt(el.offsetTop, 10);
9769         var cleft = parseInt(el.offsetLeft, 10);
9770         var cbot = ctop + el.offsetHeight;
9771         var cright = cleft + el.offsetWidth;
9772
9773         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9774         var ch = 0; //?? header is not withing the area?
9775         var stop = parseInt(c.scrollTop, 10);
9776         var sleft = parseInt(c.scrollLeft, 10);
9777         var sbot = stop + ch;
9778         var sright = sleft + c.clientWidth;
9779         /*
9780         Roo.log('GridView.ensureVisible:' +
9781                 ' ctop:' + ctop +
9782                 ' c.clientHeight:' + c.clientHeight +
9783                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9784                 ' stop:' + stop +
9785                 ' cbot:' + cbot +
9786                 ' sbot:' + sbot +
9787                 ' ch:' + ch  
9788                 );
9789         */
9790         if(ctop < stop){
9791             c.scrollTop = ctop;
9792             //Roo.log("set scrolltop to ctop DISABLE?");
9793         }else if(cbot > sbot){
9794             //Roo.log("set scrolltop to cbot-ch");
9795             c.scrollTop = cbot-ch;
9796         }
9797
9798         if(hscroll !== false){
9799             if(cleft < sleft){
9800                 c.scrollLeft = cleft;
9801             }else if(cright > sright){
9802                 c.scrollLeft = cright-c.clientWidth;
9803             }
9804         }
9805
9806         return el;
9807     },
9808     
9809     
9810     insertRow : function(dm, rowIndex, isUpdate){
9811         
9812         if(!isUpdate){
9813             this.fireEvent("beforerowsinserted", this, rowIndex);
9814         }
9815             //var s = this.getScrollState();
9816         var row = this.renderRow(this.cm, this.store, rowIndex);
9817         // insert before rowIndex..
9818         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9819         
9820         var _this = this;
9821                 
9822         if(row.cellObjects.length){
9823             Roo.each(row.cellObjects, function(r){
9824                 _this.renderCellObject(r);
9825             })
9826         }
9827             
9828         if(!isUpdate){
9829             this.fireEvent("rowsinserted", this, rowIndex);
9830             //this.syncRowHeights(firstRow, lastRow);
9831             //this.stripeRows(firstRow);
9832             //this.layout();
9833         }
9834         
9835     },
9836     
9837     
9838     getRowDom : function(rowIndex)
9839     {
9840         var rows = this.el.select('tbody > tr', true).elements;
9841         
9842         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9843         
9844     },
9845     getCellDom : function(rowIndex, colIndex)
9846     {
9847         var row = this.getRowDom(rowIndex);
9848         if (row === false) {
9849             return false;
9850         }
9851         var cols = row.select('td', true).elements;
9852         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9853         
9854     },
9855     
9856     // returns the object tree for a tr..
9857   
9858     
9859     renderRow : function(cm, ds, rowIndex) 
9860     {
9861         var d = ds.getAt(rowIndex);
9862         
9863         var row = {
9864             tag : 'tr',
9865             cls : 'x-row-' + rowIndex,
9866             cn : []
9867         };
9868             
9869         var cellObjects = [];
9870         
9871         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9872             var config = cm.config[i];
9873             
9874             var renderer = cm.getRenderer(i);
9875             var value = '';
9876             var id = false;
9877             
9878             if(typeof(renderer) !== 'undefined'){
9879                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9880             }
9881             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9882             // and are rendered into the cells after the row is rendered - using the id for the element.
9883             
9884             if(typeof(value) === 'object'){
9885                 id = Roo.id();
9886                 cellObjects.push({
9887                     container : id,
9888                     cfg : value 
9889                 })
9890             }
9891             
9892             var rowcfg = {
9893                 record: d,
9894                 rowIndex : rowIndex,
9895                 colIndex : i,
9896                 rowClass : ''
9897             };
9898
9899             this.fireEvent('rowclass', this, rowcfg);
9900             
9901             var td = {
9902                 tag: 'td',
9903                 // this might end up displaying HTML?
9904                 // this is too messy... - better to only do it on columsn you know are going to be too long
9905                 //tooltip : (typeof(value) === 'object') ? '' : value,
9906                 cls : rowcfg.rowClass + ' x-col-' + i,
9907                 style: '',
9908                 html: (typeof(value) === 'object') ? '' : value
9909             };
9910             
9911             if (id) {
9912                 td.id = id;
9913             }
9914             
9915             if(typeof(config.colspan) != 'undefined'){
9916                 td.colspan = config.colspan;
9917             }
9918             
9919             
9920             
9921             if(typeof(config.align) != 'undefined' && config.align.length){
9922                 td.style += ' text-align:' + config.align + ';';
9923             }
9924             if(typeof(config.valign) != 'undefined' && config.valign.length){
9925                 td.style += ' vertical-align:' + config.valign + ';';
9926             }
9927             /*
9928             if(typeof(config.width) != 'undefined'){
9929                 td.style += ' width:' +  config.width + 'px;';
9930             }
9931             */
9932             
9933             if(typeof(config.cursor) != 'undefined'){
9934                 td.style += ' cursor:' +  config.cursor + ';';
9935             }
9936             
9937             if(typeof(config.cls) != 'undefined'){
9938                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9939             }
9940             if (this.responsive) {
9941                 ['xs','sm','md','lg'].map(function(size){
9942                     
9943                     if(typeof(config[size]) == 'undefined'){
9944                         return;
9945                     }
9946                     
9947                     
9948                       
9949                     if (!config[size]) { // 0 = hidden
9950                         // BS 4 '0' is treated as hide that column and below.
9951                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9952                         return;
9953                     }
9954                     
9955                     td.cls += ' col-' + size + '-' + config[size] + (
9956                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9957                     );
9958                      
9959     
9960                 });
9961             }
9962             row.cn.push(td);
9963            
9964         }
9965         
9966         row.cellObjects = cellObjects;
9967         
9968         return row;
9969           
9970     },
9971     
9972     
9973     
9974     onBeforeLoad : function()
9975     {
9976         
9977     },
9978      /**
9979      * Remove all rows
9980      */
9981     clear : function()
9982     {
9983         this.el.select('tbody', true).first().dom.innerHTML = '';
9984     },
9985     /**
9986      * Show or hide a row.
9987      * @param {Number} rowIndex to show or hide
9988      * @param {Boolean} state hide
9989      */
9990     setRowVisibility : function(rowIndex, state)
9991     {
9992         var bt = this.bodyEl.dom;
9993         
9994         var rows = this.el.select('tbody > tr', true).elements;
9995         
9996         if(typeof(rows[rowIndex]) == 'undefined'){
9997             return;
9998         }
9999         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10000         
10001     },
10002     
10003     
10004     getSelectionModel : function(){
10005         if(!this.selModel){
10006             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10007         }
10008         return this.selModel;
10009     },
10010     /*
10011      * Render the Roo.bootstrap object from renderder
10012      */
10013     renderCellObject : function(r)
10014     {
10015         var _this = this;
10016         
10017         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10018         
10019         var t = r.cfg.render(r.container);
10020         
10021         if(r.cfg.cn){
10022             Roo.each(r.cfg.cn, function(c){
10023                 var child = {
10024                     container: t.getChildContainer(),
10025                     cfg: c
10026                 };
10027                 _this.renderCellObject(child);
10028             })
10029         }
10030     },
10031     /**
10032      * get the Row Index from a dom element.
10033      * @param {Roo.Element} row The row to look for
10034      * @returns {Number} the row
10035      */
10036     getRowIndex : function(row)
10037     {
10038         var rowIndex = -1;
10039         
10040         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10041             if(el != row){
10042                 return;
10043             }
10044             
10045             rowIndex = index;
10046         });
10047         
10048         return rowIndex;
10049     },
10050     /**
10051      * get the header TH element for columnIndex
10052      * @param {Number} columnIndex
10053      * @returns {Roo.Element}
10054      */
10055     getHeaderIndex: function(colIndex)
10056     {
10057         var cols = this.headEl.select('th', true).elements;
10058         return cols[colIndex]; 
10059     },
10060     /**
10061      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10062      * @param {domElement} cell to look for
10063      * @returns {Number} the column
10064      */
10065     getCellIndex : function(cell)
10066     {
10067         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10068         if(id){
10069             return parseInt(id[1], 10);
10070         }
10071         return 0;
10072     },
10073      /**
10074      * Returns the grid's underlying element = used by panel.Grid
10075      * @return {Element} The element
10076      */
10077     getGridEl : function(){
10078         return this.el;
10079     },
10080      /**
10081      * Forces a resize - used by panel.Grid
10082      * @return {Element} The element
10083      */
10084     autoSize : function()
10085     {
10086         //var ctr = Roo.get(this.container.dom.parentElement);
10087         var ctr = Roo.get(this.el.dom);
10088         
10089         var thd = this.getGridEl().select('thead',true).first();
10090         var tbd = this.getGridEl().select('tbody', true).first();
10091         var tfd = this.getGridEl().select('tfoot', true).first();
10092         
10093         var cw = ctr.getWidth();
10094         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10095         
10096         if (tbd) {
10097             
10098             tbd.setWidth(ctr.getWidth());
10099             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10100             // this needs fixing for various usage - currently only hydra job advers I think..
10101             //tdb.setHeight(
10102             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10103             //); 
10104             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10105             cw -= barsize;
10106         }
10107         cw = Math.max(cw, this.totalWidth);
10108         this.getGridEl().select('tbody tr',true).setWidth(cw);
10109         this.initCSS();
10110         
10111         // resize 'expandable coloumn?
10112         
10113         return; // we doe not have a view in this design..
10114         
10115     },
10116     onBodyScroll: function()
10117     {
10118         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10119         if(this.headEl){
10120             this.headEl.setStyle({
10121                 'position' : 'relative',
10122                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10123             });
10124         }
10125         
10126         if(this.lazyLoad){
10127             
10128             var scrollHeight = this.bodyEl.dom.scrollHeight;
10129             
10130             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10131             
10132             var height = this.bodyEl.getHeight();
10133             
10134             if(scrollHeight - height == scrollTop) {
10135                 
10136                 var total = this.ds.getTotalCount();
10137                 
10138                 if(this.footer.cursor + this.footer.pageSize < total){
10139                     
10140                     this.footer.ds.load({
10141                         params : {
10142                             start : this.footer.cursor + this.footer.pageSize,
10143                             limit : this.footer.pageSize
10144                         },
10145                         add : true
10146                     });
10147                 }
10148             }
10149             
10150         }
10151     },
10152     onColumnSplitterMoved : function(i, diff)
10153     {
10154         this.userResized = true;
10155         
10156         var cm = this.colModel;
10157         
10158         var w = this.getHeaderIndex(i).getWidth() + diff;
10159         
10160         
10161         cm.setColumnWidth(i, w, true);
10162         this.initCSS();
10163         //var cid = cm.getColumnId(i); << not used in this version?
10164        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10165         
10166         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10167         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10168         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10169 */
10170         //this.updateSplitters();
10171         //this.layout(); << ??
10172         this.fireEvent("columnresize", i, w);
10173     },
10174     onHeaderChange : function()
10175     {
10176         var header = this.renderHeader();
10177         var table = this.el.select('table', true).first();
10178         
10179         this.headEl.remove();
10180         this.headEl = table.createChild(header, this.bodyEl, false);
10181         
10182         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10183             e.on('click', this.sort, this);
10184         }, this);
10185         
10186         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10187             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10188         }
10189         
10190     },
10191     
10192     onHiddenChange : function(colModel, colIndex, hidden)
10193     {
10194         /*
10195         this.cm.setHidden()
10196         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10197         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10198         
10199         this.CSS.updateRule(thSelector, "display", "");
10200         this.CSS.updateRule(tdSelector, "display", "");
10201         
10202         if(hidden){
10203             this.CSS.updateRule(thSelector, "display", "none");
10204             this.CSS.updateRule(tdSelector, "display", "none");
10205         }
10206         */
10207         // onload calls initCSS()
10208         this.onHeaderChange();
10209         this.onLoad();
10210     },
10211     
10212     setColumnWidth: function(col_index, width)
10213     {
10214         // width = "md-2 xs-2..."
10215         if(!this.colModel.config[col_index]) {
10216             return;
10217         }
10218         
10219         var w = width.split(" ");
10220         
10221         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10222         
10223         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10224         
10225         
10226         for(var j = 0; j < w.length; j++) {
10227             
10228             if(!w[j]) {
10229                 continue;
10230             }
10231             
10232             var size_cls = w[j].split("-");
10233             
10234             if(!Number.isInteger(size_cls[1] * 1)) {
10235                 continue;
10236             }
10237             
10238             if(!this.colModel.config[col_index][size_cls[0]]) {
10239                 continue;
10240             }
10241             
10242             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10243                 continue;
10244             }
10245             
10246             h_row[0].classList.replace(
10247                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10248                 "col-"+size_cls[0]+"-"+size_cls[1]
10249             );
10250             
10251             for(var i = 0; i < rows.length; i++) {
10252                 
10253                 var size_cls = w[j].split("-");
10254                 
10255                 if(!Number.isInteger(size_cls[1] * 1)) {
10256                     continue;
10257                 }
10258                 
10259                 if(!this.colModel.config[col_index][size_cls[0]]) {
10260                     continue;
10261                 }
10262                 
10263                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10264                     continue;
10265                 }
10266                 
10267                 rows[i].classList.replace(
10268                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10269                     "col-"+size_cls[0]+"-"+size_cls[1]
10270                 );
10271             }
10272             
10273             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10274         }
10275     }
10276 });
10277
10278 // currently only used to find the split on drag.. 
10279 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10280
10281 /**
10282  * @depricated
10283 */
10284 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10285 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10286 /*
10287  * - LGPL
10288  *
10289  * table cell
10290  * 
10291  */
10292
10293 /**
10294  * @class Roo.bootstrap.TableCell
10295  * @extends Roo.bootstrap.Component
10296  * Bootstrap TableCell class
10297  * @cfg {String} html cell contain text
10298  * @cfg {String} cls cell class
10299  * @cfg {String} tag cell tag (td|th) default td
10300  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10301  * @cfg {String} align Aligns the content in a cell
10302  * @cfg {String} axis Categorizes cells
10303  * @cfg {String} bgcolor Specifies the background color of a cell
10304  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10305  * @cfg {Number} colspan Specifies the number of columns a cell should span
10306  * @cfg {String} headers Specifies one or more header cells a cell is related to
10307  * @cfg {Number} height Sets the height of a cell
10308  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10309  * @cfg {Number} rowspan Sets the number of rows a cell should span
10310  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10311  * @cfg {String} valign Vertical aligns the content in a cell
10312  * @cfg {Number} width Specifies the width of a cell
10313  * 
10314  * @constructor
10315  * Create a new TableCell
10316  * @param {Object} config The config object
10317  */
10318
10319 Roo.bootstrap.TableCell = function(config){
10320     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10321 };
10322
10323 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10324     
10325     html: false,
10326     cls: false,
10327     tag: false,
10328     abbr: false,
10329     align: false,
10330     axis: false,
10331     bgcolor: false,
10332     charoff: false,
10333     colspan: false,
10334     headers: false,
10335     height: false,
10336     nowrap: false,
10337     rowspan: false,
10338     scope: false,
10339     valign: false,
10340     width: false,
10341     
10342     
10343     getAutoCreate : function(){
10344         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10345         
10346         cfg = {
10347             tag: 'td'
10348         };
10349         
10350         if(this.tag){
10351             cfg.tag = this.tag;
10352         }
10353         
10354         if (this.html) {
10355             cfg.html=this.html
10356         }
10357         if (this.cls) {
10358             cfg.cls=this.cls
10359         }
10360         if (this.abbr) {
10361             cfg.abbr=this.abbr
10362         }
10363         if (this.align) {
10364             cfg.align=this.align
10365         }
10366         if (this.axis) {
10367             cfg.axis=this.axis
10368         }
10369         if (this.bgcolor) {
10370             cfg.bgcolor=this.bgcolor
10371         }
10372         if (this.charoff) {
10373             cfg.charoff=this.charoff
10374         }
10375         if (this.colspan) {
10376             cfg.colspan=this.colspan
10377         }
10378         if (this.headers) {
10379             cfg.headers=this.headers
10380         }
10381         if (this.height) {
10382             cfg.height=this.height
10383         }
10384         if (this.nowrap) {
10385             cfg.nowrap=this.nowrap
10386         }
10387         if (this.rowspan) {
10388             cfg.rowspan=this.rowspan
10389         }
10390         if (this.scope) {
10391             cfg.scope=this.scope
10392         }
10393         if (this.valign) {
10394             cfg.valign=this.valign
10395         }
10396         if (this.width) {
10397             cfg.width=this.width
10398         }
10399         
10400         
10401         return cfg;
10402     }
10403    
10404 });
10405
10406  
10407
10408  /*
10409  * - LGPL
10410  *
10411  * table row
10412  * 
10413  */
10414
10415 /**
10416  * @class Roo.bootstrap.TableRow
10417  * @extends Roo.bootstrap.Component
10418  * Bootstrap TableRow class
10419  * @cfg {String} cls row class
10420  * @cfg {String} align Aligns the content in a table row
10421  * @cfg {String} bgcolor Specifies a background color for a table row
10422  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10423  * @cfg {String} valign Vertical aligns the content in a table row
10424  * 
10425  * @constructor
10426  * Create a new TableRow
10427  * @param {Object} config The config object
10428  */
10429
10430 Roo.bootstrap.TableRow = function(config){
10431     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10432 };
10433
10434 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10435     
10436     cls: false,
10437     align: false,
10438     bgcolor: false,
10439     charoff: false,
10440     valign: false,
10441     
10442     getAutoCreate : function(){
10443         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10444         
10445         cfg = {
10446             tag: 'tr'
10447         };
10448             
10449         if(this.cls){
10450             cfg.cls = this.cls;
10451         }
10452         if(this.align){
10453             cfg.align = this.align;
10454         }
10455         if(this.bgcolor){
10456             cfg.bgcolor = this.bgcolor;
10457         }
10458         if(this.charoff){
10459             cfg.charoff = this.charoff;
10460         }
10461         if(this.valign){
10462             cfg.valign = this.valign;
10463         }
10464         
10465         return cfg;
10466     }
10467    
10468 });
10469
10470  
10471
10472  /*
10473  * - LGPL
10474  *
10475  * table body
10476  * 
10477  */
10478
10479 /**
10480  * @class Roo.bootstrap.TableBody
10481  * @extends Roo.bootstrap.Component
10482  * Bootstrap TableBody class
10483  * @cfg {String} cls element class
10484  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10485  * @cfg {String} align Aligns the content inside the element
10486  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10487  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10488  * 
10489  * @constructor
10490  * Create a new TableBody
10491  * @param {Object} config The config object
10492  */
10493
10494 Roo.bootstrap.TableBody = function(config){
10495     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10496 };
10497
10498 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10499     
10500     cls: false,
10501     tag: false,
10502     align: false,
10503     charoff: false,
10504     valign: false,
10505     
10506     getAutoCreate : function(){
10507         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10508         
10509         cfg = {
10510             tag: 'tbody'
10511         };
10512             
10513         if (this.cls) {
10514             cfg.cls=this.cls
10515         }
10516         if(this.tag){
10517             cfg.tag = this.tag;
10518         }
10519         
10520         if(this.align){
10521             cfg.align = this.align;
10522         }
10523         if(this.charoff){
10524             cfg.charoff = this.charoff;
10525         }
10526         if(this.valign){
10527             cfg.valign = this.valign;
10528         }
10529         
10530         return cfg;
10531     }
10532     
10533     
10534 //    initEvents : function()
10535 //    {
10536 //        
10537 //        if(!this.store){
10538 //            return;
10539 //        }
10540 //        
10541 //        this.store = Roo.factory(this.store, Roo.data);
10542 //        this.store.on('load', this.onLoad, this);
10543 //        
10544 //        this.store.load();
10545 //        
10546 //    },
10547 //    
10548 //    onLoad: function () 
10549 //    {   
10550 //        this.fireEvent('load', this);
10551 //    }
10552 //    
10553 //   
10554 });
10555
10556  
10557
10558  /*
10559  * Based on:
10560  * Ext JS Library 1.1.1
10561  * Copyright(c) 2006-2007, Ext JS, LLC.
10562  *
10563  * Originally Released Under LGPL - original licence link has changed is not relivant.
10564  *
10565  * Fork - LGPL
10566  * <script type="text/javascript">
10567  */
10568
10569 // as we use this in bootstrap.
10570 Roo.namespace('Roo.form');
10571  /**
10572  * @class Roo.form.Action
10573  * Internal Class used to handle form actions
10574  * @constructor
10575  * @param {Roo.form.BasicForm} el The form element or its id
10576  * @param {Object} config Configuration options
10577  */
10578
10579  
10580  
10581 // define the action interface
10582 Roo.form.Action = function(form, options){
10583     this.form = form;
10584     this.options = options || {};
10585 };
10586 /**
10587  * Client Validation Failed
10588  * @const 
10589  */
10590 Roo.form.Action.CLIENT_INVALID = 'client';
10591 /**
10592  * Server Validation Failed
10593  * @const 
10594  */
10595 Roo.form.Action.SERVER_INVALID = 'server';
10596  /**
10597  * Connect to Server Failed
10598  * @const 
10599  */
10600 Roo.form.Action.CONNECT_FAILURE = 'connect';
10601 /**
10602  * Reading Data from Server Failed
10603  * @const 
10604  */
10605 Roo.form.Action.LOAD_FAILURE = 'load';
10606
10607 Roo.form.Action.prototype = {
10608     type : 'default',
10609     failureType : undefined,
10610     response : undefined,
10611     result : undefined,
10612
10613     // interface method
10614     run : function(options){
10615
10616     },
10617
10618     // interface method
10619     success : function(response){
10620
10621     },
10622
10623     // interface method
10624     handleResponse : function(response){
10625
10626     },
10627
10628     // default connection failure
10629     failure : function(response){
10630         
10631         this.response = response;
10632         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10633         this.form.afterAction(this, false);
10634     },
10635
10636     processResponse : function(response){
10637         this.response = response;
10638         if(!response.responseText){
10639             return true;
10640         }
10641         this.result = this.handleResponse(response);
10642         return this.result;
10643     },
10644
10645     // utility functions used internally
10646     getUrl : function(appendParams){
10647         var url = this.options.url || this.form.url || this.form.el.dom.action;
10648         if(appendParams){
10649             var p = this.getParams();
10650             if(p){
10651                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10652             }
10653         }
10654         return url;
10655     },
10656
10657     getMethod : function(){
10658         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10659     },
10660
10661     getParams : function(){
10662         var bp = this.form.baseParams;
10663         var p = this.options.params;
10664         if(p){
10665             if(typeof p == "object"){
10666                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10667             }else if(typeof p == 'string' && bp){
10668                 p += '&' + Roo.urlEncode(bp);
10669             }
10670         }else if(bp){
10671             p = Roo.urlEncode(bp);
10672         }
10673         return p;
10674     },
10675
10676     createCallback : function(){
10677         return {
10678             success: this.success,
10679             failure: this.failure,
10680             scope: this,
10681             timeout: (this.form.timeout*1000),
10682             upload: this.form.fileUpload ? this.success : undefined
10683         };
10684     }
10685 };
10686
10687 Roo.form.Action.Submit = function(form, options){
10688     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10689 };
10690
10691 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10692     type : 'submit',
10693
10694     haveProgress : false,
10695     uploadComplete : false,
10696     
10697     // uploadProgress indicator.
10698     uploadProgress : function()
10699     {
10700         if (!this.form.progressUrl) {
10701             return;
10702         }
10703         
10704         if (!this.haveProgress) {
10705             Roo.MessageBox.progress("Uploading", "Uploading");
10706         }
10707         if (this.uploadComplete) {
10708            Roo.MessageBox.hide();
10709            return;
10710         }
10711         
10712         this.haveProgress = true;
10713    
10714         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10715         
10716         var c = new Roo.data.Connection();
10717         c.request({
10718             url : this.form.progressUrl,
10719             params: {
10720                 id : uid
10721             },
10722             method: 'GET',
10723             success : function(req){
10724                //console.log(data);
10725                 var rdata = false;
10726                 var edata;
10727                 try  {
10728                    rdata = Roo.decode(req.responseText)
10729                 } catch (e) {
10730                     Roo.log("Invalid data from server..");
10731                     Roo.log(edata);
10732                     return;
10733                 }
10734                 if (!rdata || !rdata.success) {
10735                     Roo.log(rdata);
10736                     Roo.MessageBox.alert(Roo.encode(rdata));
10737                     return;
10738                 }
10739                 var data = rdata.data;
10740                 
10741                 if (this.uploadComplete) {
10742                    Roo.MessageBox.hide();
10743                    return;
10744                 }
10745                    
10746                 if (data){
10747                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10748                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10749                     );
10750                 }
10751                 this.uploadProgress.defer(2000,this);
10752             },
10753        
10754             failure: function(data) {
10755                 Roo.log('progress url failed ');
10756                 Roo.log(data);
10757             },
10758             scope : this
10759         });
10760            
10761     },
10762     
10763     
10764     run : function()
10765     {
10766         // run get Values on the form, so it syncs any secondary forms.
10767         this.form.getValues();
10768         
10769         var o = this.options;
10770         var method = this.getMethod();
10771         var isPost = method == 'POST';
10772         if(o.clientValidation === false || this.form.isValid()){
10773             
10774             if (this.form.progressUrl) {
10775                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10776                     (new Date() * 1) + '' + Math.random());
10777                     
10778             } 
10779             
10780             
10781             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10782                 form:this.form.el.dom,
10783                 url:this.getUrl(!isPost),
10784                 method: method,
10785                 params:isPost ? this.getParams() : null,
10786                 isUpload: this.form.fileUpload,
10787                 formData : this.form.formData
10788             }));
10789             
10790             this.uploadProgress();
10791
10792         }else if (o.clientValidation !== false){ // client validation failed
10793             this.failureType = Roo.form.Action.CLIENT_INVALID;
10794             this.form.afterAction(this, false);
10795         }
10796     },
10797
10798     success : function(response)
10799     {
10800         this.uploadComplete= true;
10801         if (this.haveProgress) {
10802             Roo.MessageBox.hide();
10803         }
10804         
10805         
10806         var result = this.processResponse(response);
10807         if(result === true || result.success){
10808             this.form.afterAction(this, true);
10809             return;
10810         }
10811         if(result.errors){
10812             this.form.markInvalid(result.errors);
10813             this.failureType = Roo.form.Action.SERVER_INVALID;
10814         }
10815         this.form.afterAction(this, false);
10816     },
10817     failure : function(response)
10818     {
10819         this.uploadComplete= true;
10820         if (this.haveProgress) {
10821             Roo.MessageBox.hide();
10822         }
10823         
10824         this.response = response;
10825         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10826         this.form.afterAction(this, false);
10827     },
10828     
10829     handleResponse : function(response){
10830         if(this.form.errorReader){
10831             var rs = this.form.errorReader.read(response);
10832             var errors = [];
10833             if(rs.records){
10834                 for(var i = 0, len = rs.records.length; i < len; i++) {
10835                     var r = rs.records[i];
10836                     errors[i] = r.data;
10837                 }
10838             }
10839             if(errors.length < 1){
10840                 errors = null;
10841             }
10842             return {
10843                 success : rs.success,
10844                 errors : errors
10845             };
10846         }
10847         var ret = false;
10848         try {
10849             ret = Roo.decode(response.responseText);
10850         } catch (e) {
10851             ret = {
10852                 success: false,
10853                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10854                 errors : []
10855             };
10856         }
10857         return ret;
10858         
10859     }
10860 });
10861
10862
10863 Roo.form.Action.Load = function(form, options){
10864     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10865     this.reader = this.form.reader;
10866 };
10867
10868 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10869     type : 'load',
10870
10871     run : function(){
10872         
10873         Roo.Ajax.request(Roo.apply(
10874                 this.createCallback(), {
10875                     method:this.getMethod(),
10876                     url:this.getUrl(false),
10877                     params:this.getParams()
10878         }));
10879     },
10880
10881     success : function(response){
10882         
10883         var result = this.processResponse(response);
10884         if(result === true || !result.success || !result.data){
10885             this.failureType = Roo.form.Action.LOAD_FAILURE;
10886             this.form.afterAction(this, false);
10887             return;
10888         }
10889         this.form.clearInvalid();
10890         this.form.setValues(result.data);
10891         this.form.afterAction(this, true);
10892     },
10893
10894     handleResponse : function(response){
10895         if(this.form.reader){
10896             var rs = this.form.reader.read(response);
10897             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10898             return {
10899                 success : rs.success,
10900                 data : data
10901             };
10902         }
10903         return Roo.decode(response.responseText);
10904     }
10905 });
10906
10907 Roo.form.Action.ACTION_TYPES = {
10908     'load' : Roo.form.Action.Load,
10909     'submit' : Roo.form.Action.Submit
10910 };/*
10911  * - LGPL
10912  *
10913  * form
10914  *
10915  */
10916
10917 /**
10918  * @class Roo.bootstrap.Form
10919  * @extends Roo.bootstrap.Component
10920  * @children Roo.bootstrap.Component
10921  * Bootstrap Form class
10922  * @cfg {String} method  GET | POST (default POST)
10923  * @cfg {String} labelAlign top | left (default top)
10924  * @cfg {String} align left  | right - for navbars
10925  * @cfg {Boolean} loadMask load mask when submit (default true)
10926
10927  *
10928  * @constructor
10929  * Create a new Form
10930  * @param {Object} config The config object
10931  */
10932
10933
10934 Roo.bootstrap.Form = function(config){
10935     
10936     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10937     
10938     Roo.bootstrap.Form.popover.apply();
10939     
10940     this.addEvents({
10941         /**
10942          * @event clientvalidation
10943          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10944          * @param {Form} this
10945          * @param {Boolean} valid true if the form has passed client-side validation
10946          */
10947         clientvalidation: true,
10948         /**
10949          * @event beforeaction
10950          * Fires before any action is performed. Return false to cancel the action.
10951          * @param {Form} this
10952          * @param {Action} action The action to be performed
10953          */
10954         beforeaction: true,
10955         /**
10956          * @event actionfailed
10957          * Fires when an action fails.
10958          * @param {Form} this
10959          * @param {Action} action The action that failed
10960          */
10961         actionfailed : true,
10962         /**
10963          * @event actioncomplete
10964          * Fires when an action is completed.
10965          * @param {Form} this
10966          * @param {Action} action The action that completed
10967          */
10968         actioncomplete : true
10969     });
10970 };
10971
10972 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10973
10974      /**
10975      * @cfg {String} method
10976      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10977      */
10978     method : 'POST',
10979     /**
10980      * @cfg {String} url
10981      * The URL to use for form actions if one isn't supplied in the action options.
10982      */
10983     /**
10984      * @cfg {Boolean} fileUpload
10985      * Set to true if this form is a file upload.
10986      */
10987
10988     /**
10989      * @cfg {Object} baseParams
10990      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10991      */
10992
10993     /**
10994      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10995      */
10996     timeout: 30,
10997     /**
10998      * @cfg {Sting} align (left|right) for navbar forms
10999      */
11000     align : 'left',
11001
11002     // private
11003     activeAction : null,
11004
11005     /**
11006      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11007      * element by passing it or its id or mask the form itself by passing in true.
11008      * @type Mixed
11009      */
11010     waitMsgTarget : false,
11011
11012     loadMask : true,
11013     
11014     /**
11015      * @cfg {Boolean} errorMask (true|false) default false
11016      */
11017     errorMask : false,
11018     
11019     /**
11020      * @cfg {Number} maskOffset Default 100
11021      */
11022     maskOffset : 100,
11023     
11024     /**
11025      * @cfg {Boolean} maskBody
11026      */
11027     maskBody : false,
11028
11029     getAutoCreate : function(){
11030
11031         var cfg = {
11032             tag: 'form',
11033             method : this.method || 'POST',
11034             id : this.id || Roo.id(),
11035             cls : ''
11036         };
11037         if (this.parent().xtype.match(/^Nav/)) {
11038             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11039
11040         }
11041
11042         if (this.labelAlign == 'left' ) {
11043             cfg.cls += ' form-horizontal';
11044         }
11045
11046
11047         return cfg;
11048     },
11049     initEvents : function()
11050     {
11051         this.el.on('submit', this.onSubmit, this);
11052         // this was added as random key presses on the form where triggering form submit.
11053         this.el.on('keypress', function(e) {
11054             if (e.getCharCode() != 13) {
11055                 return true;
11056             }
11057             // we might need to allow it for textareas.. and some other items.
11058             // check e.getTarget().
11059
11060             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11061                 return true;
11062             }
11063
11064             Roo.log("keypress blocked");
11065
11066             e.preventDefault();
11067             return false;
11068         });
11069         
11070     },
11071     // private
11072     onSubmit : function(e){
11073         e.stopEvent();
11074     },
11075
11076      /**
11077      * Returns true if client-side validation on the form is successful.
11078      * @return Boolean
11079      */
11080     isValid : function(){
11081         var items = this.getItems();
11082         var valid = true;
11083         var target = false;
11084         
11085         items.each(function(f){
11086             
11087             if(f.validate()){
11088                 return;
11089             }
11090             
11091             Roo.log('invalid field: ' + f.name);
11092             
11093             valid = false;
11094
11095             if(!target && f.el.isVisible(true)){
11096                 target = f;
11097             }
11098            
11099         });
11100         
11101         if(this.errorMask && !valid){
11102             Roo.bootstrap.Form.popover.mask(this, target);
11103         }
11104         
11105         return valid;
11106     },
11107     
11108     /**
11109      * Returns true if any fields in this form have changed since their original load.
11110      * @return Boolean
11111      */
11112     isDirty : function(){
11113         var dirty = false;
11114         var items = this.getItems();
11115         items.each(function(f){
11116            if(f.isDirty()){
11117                dirty = true;
11118                return false;
11119            }
11120            return true;
11121         });
11122         return dirty;
11123     },
11124      /**
11125      * Performs a predefined action (submit or load) or custom actions you define on this form.
11126      * @param {String} actionName The name of the action type
11127      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11128      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11129      * accept other config options):
11130      * <pre>
11131 Property          Type             Description
11132 ----------------  ---------------  ----------------------------------------------------------------------------------
11133 url               String           The url for the action (defaults to the form's url)
11134 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11135 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11136 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11137                                    validate the form on the client (defaults to false)
11138      * </pre>
11139      * @return {BasicForm} this
11140      */
11141     doAction : function(action, options){
11142         if(typeof action == 'string'){
11143             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11144         }
11145         if(this.fireEvent('beforeaction', this, action) !== false){
11146             this.beforeAction(action);
11147             action.run.defer(100, action);
11148         }
11149         return this;
11150     },
11151
11152     // private
11153     beforeAction : function(action){
11154         var o = action.options;
11155         
11156         if(this.loadMask){
11157             
11158             if(this.maskBody){
11159                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11160             } else {
11161                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11162             }
11163         }
11164         // not really supported yet.. ??
11165
11166         //if(this.waitMsgTarget === true){
11167         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11168         //}else if(this.waitMsgTarget){
11169         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11170         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11171         //}else {
11172         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11173        // }
11174
11175     },
11176
11177     // private
11178     afterAction : function(action, success){
11179         this.activeAction = null;
11180         var o = action.options;
11181
11182         if(this.loadMask){
11183             
11184             if(this.maskBody){
11185                 Roo.get(document.body).unmask();
11186             } else {
11187                 this.el.unmask();
11188             }
11189         }
11190         
11191         //if(this.waitMsgTarget === true){
11192 //            this.el.unmask();
11193         //}else if(this.waitMsgTarget){
11194         //    this.waitMsgTarget.unmask();
11195         //}else{
11196         //    Roo.MessageBox.updateProgress(1);
11197         //    Roo.MessageBox.hide();
11198        // }
11199         //
11200         if(success){
11201             if(o.reset){
11202                 this.reset();
11203             }
11204             Roo.callback(o.success, o.scope, [this, action]);
11205             this.fireEvent('actioncomplete', this, action);
11206
11207         }else{
11208
11209             // failure condition..
11210             // we have a scenario where updates need confirming.
11211             // eg. if a locking scenario exists..
11212             // we look for { errors : { needs_confirm : true }} in the response.
11213             if (
11214                 (typeof(action.result) != 'undefined')  &&
11215                 (typeof(action.result.errors) != 'undefined')  &&
11216                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11217            ){
11218                 var _t = this;
11219                 Roo.log("not supported yet");
11220                  /*
11221
11222                 Roo.MessageBox.confirm(
11223                     "Change requires confirmation",
11224                     action.result.errorMsg,
11225                     function(r) {
11226                         if (r != 'yes') {
11227                             return;
11228                         }
11229                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11230                     }
11231
11232                 );
11233                 */
11234
11235
11236                 return;
11237             }
11238
11239             Roo.callback(o.failure, o.scope, [this, action]);
11240             // show an error message if no failed handler is set..
11241             if (!this.hasListener('actionfailed')) {
11242                 Roo.log("need to add dialog support");
11243                 /*
11244                 Roo.MessageBox.alert("Error",
11245                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11246                         action.result.errorMsg :
11247                         "Saving Failed, please check your entries or try again"
11248                 );
11249                 */
11250             }
11251
11252             this.fireEvent('actionfailed', this, action);
11253         }
11254
11255     },
11256     /**
11257      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11258      * @param {String} id The value to search for
11259      * @return Field
11260      */
11261     findField : function(id){
11262         var items = this.getItems();
11263         var field = items.get(id);
11264         if(!field){
11265              items.each(function(f){
11266                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11267                     field = f;
11268                     return false;
11269                 }
11270                 return true;
11271             });
11272         }
11273         return field || null;
11274     },
11275      /**
11276      * Mark fields in this form invalid in bulk.
11277      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11278      * @return {BasicForm} this
11279      */
11280     markInvalid : function(errors){
11281         if(errors instanceof Array){
11282             for(var i = 0, len = errors.length; i < len; i++){
11283                 var fieldError = errors[i];
11284                 var f = this.findField(fieldError.id);
11285                 if(f){
11286                     f.markInvalid(fieldError.msg);
11287                 }
11288             }
11289         }else{
11290             var field, id;
11291             for(id in errors){
11292                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11293                     field.markInvalid(errors[id]);
11294                 }
11295             }
11296         }
11297         //Roo.each(this.childForms || [], function (f) {
11298         //    f.markInvalid(errors);
11299         //});
11300
11301         return this;
11302     },
11303
11304     /**
11305      * Set values for fields in this form in bulk.
11306      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11307      * @return {BasicForm} this
11308      */
11309     setValues : function(values){
11310         if(values instanceof Array){ // array of objects
11311             for(var i = 0, len = values.length; i < len; i++){
11312                 var v = values[i];
11313                 var f = this.findField(v.id);
11314                 if(f){
11315                     f.setValue(v.value);
11316                     if(this.trackResetOnLoad){
11317                         f.originalValue = f.getValue();
11318                     }
11319                 }
11320             }
11321         }else{ // object hash
11322             var field, id;
11323             for(id in values){
11324                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11325
11326                     if (field.setFromData &&
11327                         field.valueField &&
11328                         field.displayField &&
11329                         // combos' with local stores can
11330                         // be queried via setValue()
11331                         // to set their value..
11332                         (field.store && !field.store.isLocal)
11333                         ) {
11334                         // it's a combo
11335                         var sd = { };
11336                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11337                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11338                         field.setFromData(sd);
11339
11340                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11341                         
11342                         field.setFromData(values);
11343                         
11344                     } else {
11345                         field.setValue(values[id]);
11346                     }
11347
11348
11349                     if(this.trackResetOnLoad){
11350                         field.originalValue = field.getValue();
11351                     }
11352                 }
11353             }
11354         }
11355
11356         //Roo.each(this.childForms || [], function (f) {
11357         //    f.setValues(values);
11358         //});
11359
11360         return this;
11361     },
11362
11363     /**
11364      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11365      * they are returned as an array.
11366      * @param {Boolean} asString
11367      * @return {Object}
11368      */
11369     getValues : function(asString){
11370         //if (this.childForms) {
11371             // copy values from the child forms
11372         //    Roo.each(this.childForms, function (f) {
11373         //        this.setValues(f.getValues());
11374         //    }, this);
11375         //}
11376
11377
11378
11379         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11380         if(asString === true){
11381             return fs;
11382         }
11383         return Roo.urlDecode(fs);
11384     },
11385
11386     /**
11387      * Returns the fields in this form as an object with key/value pairs.
11388      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11389      * @return {Object}
11390      */
11391     getFieldValues : function(with_hidden)
11392     {
11393         var items = this.getItems();
11394         var ret = {};
11395         items.each(function(f){
11396             
11397             if (!f.getName()) {
11398                 return;
11399             }
11400             
11401             var v = f.getValue();
11402             
11403             if (f.inputType =='radio') {
11404                 if (typeof(ret[f.getName()]) == 'undefined') {
11405                     ret[f.getName()] = ''; // empty..
11406                 }
11407
11408                 if (!f.el.dom.checked) {
11409                     return;
11410
11411                 }
11412                 v = f.el.dom.value;
11413
11414             }
11415             
11416             if(f.xtype == 'MoneyField'){
11417                 ret[f.currencyName] = f.getCurrency();
11418             }
11419
11420             // not sure if this supported any more..
11421             if ((typeof(v) == 'object') && f.getRawValue) {
11422                 v = f.getRawValue() ; // dates..
11423             }
11424             // combo boxes where name != hiddenName...
11425             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11426                 ret[f.name] = f.getRawValue();
11427             }
11428             ret[f.getName()] = v;
11429         });
11430
11431         return ret;
11432     },
11433
11434     /**
11435      * Clears all invalid messages in this form.
11436      * @return {BasicForm} this
11437      */
11438     clearInvalid : function(){
11439         var items = this.getItems();
11440
11441         items.each(function(f){
11442            f.clearInvalid();
11443         });
11444
11445         return this;
11446     },
11447
11448     /**
11449      * Resets this form.
11450      * @return {BasicForm} this
11451      */
11452     reset : function(){
11453         var items = this.getItems();
11454         items.each(function(f){
11455             f.reset();
11456         });
11457
11458         Roo.each(this.childForms || [], function (f) {
11459             f.reset();
11460         });
11461
11462
11463         return this;
11464     },
11465     
11466     getItems : function()
11467     {
11468         var r=new Roo.util.MixedCollection(false, function(o){
11469             return o.id || (o.id = Roo.id());
11470         });
11471         var iter = function(el) {
11472             if (el.inputEl) {
11473                 r.add(el);
11474             }
11475             if (!el.items) {
11476                 return;
11477             }
11478             Roo.each(el.items,function(e) {
11479                 iter(e);
11480             });
11481         };
11482
11483         iter(this);
11484         return r;
11485     },
11486     
11487     hideFields : function(items)
11488     {
11489         Roo.each(items, function(i){
11490             
11491             var f = this.findField(i);
11492             
11493             if(!f){
11494                 return;
11495             }
11496             
11497             f.hide();
11498             
11499         }, this);
11500     },
11501     
11502     showFields : function(items)
11503     {
11504         Roo.each(items, function(i){
11505             
11506             var f = this.findField(i);
11507             
11508             if(!f){
11509                 return;
11510             }
11511             
11512             f.show();
11513             
11514         }, this);
11515     }
11516
11517 });
11518
11519 Roo.apply(Roo.bootstrap.Form, {
11520     
11521     popover : {
11522         
11523         padding : 5,
11524         
11525         isApplied : false,
11526         
11527         isMasked : false,
11528         
11529         form : false,
11530         
11531         target : false,
11532         
11533         toolTip : false,
11534         
11535         intervalID : false,
11536         
11537         maskEl : false,
11538         
11539         apply : function()
11540         {
11541             if(this.isApplied){
11542                 return;
11543             }
11544             
11545             this.maskEl = {
11546                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11547                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11548                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11549                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11550             };
11551             
11552             this.maskEl.top.enableDisplayMode("block");
11553             this.maskEl.left.enableDisplayMode("block");
11554             this.maskEl.bottom.enableDisplayMode("block");
11555             this.maskEl.right.enableDisplayMode("block");
11556             
11557             this.toolTip = new Roo.bootstrap.Tooltip({
11558                 cls : 'roo-form-error-popover',
11559                 alignment : {
11560                     'left' : ['r-l', [-2,0], 'right'],
11561                     'right' : ['l-r', [2,0], 'left'],
11562                     'bottom' : ['tl-bl', [0,2], 'top'],
11563                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11564                 }
11565             });
11566             
11567             this.toolTip.render(Roo.get(document.body));
11568
11569             this.toolTip.el.enableDisplayMode("block");
11570             
11571             Roo.get(document.body).on('click', function(){
11572                 this.unmask();
11573             }, this);
11574             
11575             Roo.get(document.body).on('touchstart', function(){
11576                 this.unmask();
11577             }, this);
11578             
11579             this.isApplied = true
11580         },
11581         
11582         mask : function(form, target)
11583         {
11584             this.form = form;
11585             
11586             this.target = target;
11587             
11588             if(!this.form.errorMask || !target.el){
11589                 return;
11590             }
11591             
11592             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11593             
11594             Roo.log(scrollable);
11595             
11596             var ot = this.target.el.calcOffsetsTo(scrollable);
11597             
11598             var scrollTo = ot[1] - this.form.maskOffset;
11599             
11600             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11601             
11602             scrollable.scrollTo('top', scrollTo);
11603             
11604             var box = this.target.el.getBox();
11605             Roo.log(box);
11606             var zIndex = Roo.bootstrap.Modal.zIndex++;
11607
11608             
11609             this.maskEl.top.setStyle('position', 'absolute');
11610             this.maskEl.top.setStyle('z-index', zIndex);
11611             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11612             this.maskEl.top.setLeft(0);
11613             this.maskEl.top.setTop(0);
11614             this.maskEl.top.show();
11615             
11616             this.maskEl.left.setStyle('position', 'absolute');
11617             this.maskEl.left.setStyle('z-index', zIndex);
11618             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11619             this.maskEl.left.setLeft(0);
11620             this.maskEl.left.setTop(box.y - this.padding);
11621             this.maskEl.left.show();
11622
11623             this.maskEl.bottom.setStyle('position', 'absolute');
11624             this.maskEl.bottom.setStyle('z-index', zIndex);
11625             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11626             this.maskEl.bottom.setLeft(0);
11627             this.maskEl.bottom.setTop(box.bottom + this.padding);
11628             this.maskEl.bottom.show();
11629
11630             this.maskEl.right.setStyle('position', 'absolute');
11631             this.maskEl.right.setStyle('z-index', zIndex);
11632             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11633             this.maskEl.right.setLeft(box.right + this.padding);
11634             this.maskEl.right.setTop(box.y - this.padding);
11635             this.maskEl.right.show();
11636
11637             this.toolTip.bindEl = this.target.el;
11638
11639             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11640
11641             var tip = this.target.blankText;
11642
11643             if(this.target.getValue() !== '' ) {
11644                 
11645                 if (this.target.invalidText.length) {
11646                     tip = this.target.invalidText;
11647                 } else if (this.target.regexText.length){
11648                     tip = this.target.regexText;
11649                 }
11650             }
11651
11652             this.toolTip.show(tip);
11653
11654             this.intervalID = window.setInterval(function() {
11655                 Roo.bootstrap.Form.popover.unmask();
11656             }, 10000);
11657
11658             window.onwheel = function(){ return false;};
11659             
11660             (function(){ this.isMasked = true; }).defer(500, this);
11661             
11662         },
11663         
11664         unmask : function()
11665         {
11666             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11667                 return;
11668             }
11669             
11670             this.maskEl.top.setStyle('position', 'absolute');
11671             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11672             this.maskEl.top.hide();
11673
11674             this.maskEl.left.setStyle('position', 'absolute');
11675             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11676             this.maskEl.left.hide();
11677
11678             this.maskEl.bottom.setStyle('position', 'absolute');
11679             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11680             this.maskEl.bottom.hide();
11681
11682             this.maskEl.right.setStyle('position', 'absolute');
11683             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11684             this.maskEl.right.hide();
11685             
11686             this.toolTip.hide();
11687             
11688             this.toolTip.el.hide();
11689             
11690             window.onwheel = function(){ return true;};
11691             
11692             if(this.intervalID){
11693                 window.clearInterval(this.intervalID);
11694                 this.intervalID = false;
11695             }
11696             
11697             this.isMasked = false;
11698             
11699         }
11700         
11701     }
11702     
11703 });
11704
11705 /*
11706  * Based on:
11707  * Ext JS Library 1.1.1
11708  * Copyright(c) 2006-2007, Ext JS, LLC.
11709  *
11710  * Originally Released Under LGPL - original licence link has changed is not relivant.
11711  *
11712  * Fork - LGPL
11713  * <script type="text/javascript">
11714  */
11715 /**
11716  * @class Roo.form.VTypes
11717  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11718  * @singleton
11719  */
11720 Roo.form.VTypes = function(){
11721     // closure these in so they are only created once.
11722     var alpha = /^[a-zA-Z_]+$/;
11723     var alphanum = /^[a-zA-Z0-9_]+$/;
11724     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11725     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11726
11727     // All these messages and functions are configurable
11728     return {
11729         /**
11730          * The function used to validate email addresses
11731          * @param {String} value The email address
11732          */
11733         'email' : function(v){
11734             return email.test(v);
11735         },
11736         /**
11737          * The error text to display when the email validation function returns false
11738          * @type String
11739          */
11740         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11741         /**
11742          * The keystroke filter mask to be applied on email input
11743          * @type RegExp
11744          */
11745         'emailMask' : /[a-z0-9_\.\-@]/i,
11746
11747         /**
11748          * The function used to validate URLs
11749          * @param {String} value The URL
11750          */
11751         'url' : function(v){
11752             return url.test(v);
11753         },
11754         /**
11755          * The error text to display when the url validation function returns false
11756          * @type String
11757          */
11758         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11759         
11760         /**
11761          * The function used to validate alpha values
11762          * @param {String} value The value
11763          */
11764         'alpha' : function(v){
11765             return alpha.test(v);
11766         },
11767         /**
11768          * The error text to display when the alpha validation function returns false
11769          * @type String
11770          */
11771         'alphaText' : 'This field should only contain letters and _',
11772         /**
11773          * The keystroke filter mask to be applied on alpha input
11774          * @type RegExp
11775          */
11776         'alphaMask' : /[a-z_]/i,
11777
11778         /**
11779          * The function used to validate alphanumeric values
11780          * @param {String} value The value
11781          */
11782         'alphanum' : function(v){
11783             return alphanum.test(v);
11784         },
11785         /**
11786          * The error text to display when the alphanumeric validation function returns false
11787          * @type String
11788          */
11789         'alphanumText' : 'This field should only contain letters, numbers and _',
11790         /**
11791          * The keystroke filter mask to be applied on alphanumeric input
11792          * @type RegExp
11793          */
11794         'alphanumMask' : /[a-z0-9_]/i
11795     };
11796 }();/*
11797  * - LGPL
11798  *
11799  * Input
11800  * 
11801  */
11802
11803 /**
11804  * @class Roo.bootstrap.Input
11805  * @extends Roo.bootstrap.Component
11806  * Bootstrap Input class
11807  * @cfg {Boolean} disabled is it disabled
11808  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11809  * @cfg {String} name name of the input
11810  * @cfg {string} fieldLabel - the label associated
11811  * @cfg {string} placeholder - placeholder to put in text.
11812  * @cfg {string}  before - input group add on before
11813  * @cfg {string} after - input group add on after
11814  * @cfg {string} size - (lg|sm) or leave empty..
11815  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11816  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11817  * @cfg {Number} md colspan out of 12 for computer-sized screens
11818  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11819  * @cfg {string} value default value of the input
11820  * @cfg {Number} labelWidth set the width of label 
11821  * @cfg {Number} labellg set the width of label (1-12)
11822  * @cfg {Number} labelmd set the width of label (1-12)
11823  * @cfg {Number} labelsm set the width of label (1-12)
11824  * @cfg {Number} labelxs set the width of label (1-12)
11825  * @cfg {String} labelAlign (top|left)
11826  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11827  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11828  * @cfg {String} indicatorpos (left|right) default left
11829  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11830  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11831  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11832
11833  * @cfg {String} align (left|center|right) Default left
11834  * @cfg {Boolean} forceFeedback (true|false) Default false
11835  * 
11836  * @constructor
11837  * Create a new Input
11838  * @param {Object} config The config object
11839  */
11840
11841 Roo.bootstrap.Input = function(config){
11842     
11843     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11844     
11845     this.addEvents({
11846         /**
11847          * @event focus
11848          * Fires when this field receives input focus.
11849          * @param {Roo.form.Field} this
11850          */
11851         focus : true,
11852         /**
11853          * @event blur
11854          * Fires when this field loses input focus.
11855          * @param {Roo.form.Field} this
11856          */
11857         blur : true,
11858         /**
11859          * @event specialkey
11860          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11861          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11862          * @param {Roo.form.Field} this
11863          * @param {Roo.EventObject} e The event object
11864          */
11865         specialkey : true,
11866         /**
11867          * @event change
11868          * Fires just before the field blurs if the field value has changed.
11869          * @param {Roo.form.Field} this
11870          * @param {Mixed} newValue The new value
11871          * @param {Mixed} oldValue The original value
11872          */
11873         change : true,
11874         /**
11875          * @event invalid
11876          * Fires after the field has been marked as invalid.
11877          * @param {Roo.form.Field} this
11878          * @param {String} msg The validation message
11879          */
11880         invalid : true,
11881         /**
11882          * @event valid
11883          * Fires after the field has been validated with no errors.
11884          * @param {Roo.form.Field} this
11885          */
11886         valid : true,
11887          /**
11888          * @event keyup
11889          * Fires after the key up
11890          * @param {Roo.form.Field} this
11891          * @param {Roo.EventObject}  e The event Object
11892          */
11893         keyup : true,
11894         /**
11895          * @event paste
11896          * Fires after the user pastes into input
11897          * @param {Roo.form.Field} this
11898          * @param {Roo.EventObject}  e The event Object
11899          */
11900         paste : true
11901     });
11902 };
11903
11904 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11905      /**
11906      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11907       automatic validation (defaults to "keyup").
11908      */
11909     validationEvent : "keyup",
11910      /**
11911      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11912      */
11913     validateOnBlur : true,
11914     /**
11915      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11916      */
11917     validationDelay : 250,
11918      /**
11919      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11920      */
11921     focusClass : "x-form-focus",  // not needed???
11922     
11923        
11924     /**
11925      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11926      */
11927     invalidClass : "has-warning",
11928     
11929     /**
11930      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11931      */
11932     validClass : "has-success",
11933     
11934     /**
11935      * @cfg {Boolean} hasFeedback (true|false) default true
11936      */
11937     hasFeedback : true,
11938     
11939     /**
11940      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11941      */
11942     invalidFeedbackClass : "glyphicon-warning-sign",
11943     
11944     /**
11945      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11946      */
11947     validFeedbackClass : "glyphicon-ok",
11948     
11949     /**
11950      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11951      */
11952     selectOnFocus : false,
11953     
11954      /**
11955      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11956      */
11957     maskRe : null,
11958        /**
11959      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11960      */
11961     vtype : null,
11962     
11963       /**
11964      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11965      */
11966     disableKeyFilter : false,
11967     
11968        /**
11969      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11970      */
11971     disabled : false,
11972      /**
11973      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11974      */
11975     allowBlank : true,
11976     /**
11977      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11978      */
11979     blankText : "Please complete this mandatory field",
11980     
11981      /**
11982      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11983      */
11984     minLength : 0,
11985     /**
11986      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11987      */
11988     maxLength : Number.MAX_VALUE,
11989     /**
11990      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11991      */
11992     minLengthText : "The minimum length for this field is {0}",
11993     /**
11994      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11995      */
11996     maxLengthText : "The maximum length for this field is {0}",
11997   
11998     
11999     /**
12000      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12001      * If available, this function will be called only after the basic validators all return true, and will be passed the
12002      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12003      */
12004     validator : null,
12005     /**
12006      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12007      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12008      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12009      */
12010     regex : null,
12011     /**
12012      * @cfg {String} regexText -- Depricated - use Invalid Text
12013      */
12014     regexText : "",
12015     
12016     /**
12017      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12018      */
12019     invalidText : "",
12020     
12021     
12022     
12023     autocomplete: false,
12024     
12025     
12026     fieldLabel : '',
12027     inputType : 'text',
12028     
12029     name : false,
12030     placeholder: false,
12031     before : false,
12032     after : false,
12033     size : false,
12034     hasFocus : false,
12035     preventMark: false,
12036     isFormField : true,
12037     value : '',
12038     labelWidth : 2,
12039     labelAlign : false,
12040     readOnly : false,
12041     align : false,
12042     formatedValue : false,
12043     forceFeedback : false,
12044     
12045     indicatorpos : 'left',
12046     
12047     labellg : 0,
12048     labelmd : 0,
12049     labelsm : 0,
12050     labelxs : 0,
12051     
12052     capture : '',
12053     accept : '',
12054     
12055     parentLabelAlign : function()
12056     {
12057         var parent = this;
12058         while (parent.parent()) {
12059             parent = parent.parent();
12060             if (typeof(parent.labelAlign) !='undefined') {
12061                 return parent.labelAlign;
12062             }
12063         }
12064         return 'left';
12065         
12066     },
12067     
12068     getAutoCreate : function()
12069     {
12070         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12071         
12072         var id = Roo.id();
12073         
12074         var cfg = {};
12075         
12076         if(this.inputType != 'hidden'){
12077             cfg.cls = 'form-group' //input-group
12078         }
12079         
12080         var input =  {
12081             tag: 'input',
12082             id : id,
12083             type : this.inputType,
12084             value : this.value,
12085             cls : 'form-control',
12086             placeholder : this.placeholder || '',
12087             autocomplete : this.autocomplete || 'new-password'
12088         };
12089         if (this.inputType == 'file') {
12090             input.style = 'overflow:hidden'; // why not in CSS?
12091         }
12092         
12093         if(this.capture.length){
12094             input.capture = this.capture;
12095         }
12096         
12097         if(this.accept.length){
12098             input.accept = this.accept + "/*";
12099         }
12100         
12101         if(this.align){
12102             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12103         }
12104         
12105         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12106             input.maxLength = this.maxLength;
12107         }
12108         
12109         if (this.disabled) {
12110             input.disabled=true;
12111         }
12112         
12113         if (this.readOnly) {
12114             input.readonly=true;
12115         }
12116         
12117         if (this.name) {
12118             input.name = this.name;
12119         }
12120         
12121         if (this.size) {
12122             input.cls += ' input-' + this.size;
12123         }
12124         
12125         var settings=this;
12126         ['xs','sm','md','lg'].map(function(size){
12127             if (settings[size]) {
12128                 cfg.cls += ' col-' + size + '-' + settings[size];
12129             }
12130         });
12131         
12132         var inputblock = input;
12133         
12134         var feedback = {
12135             tag: 'span',
12136             cls: 'glyphicon form-control-feedback'
12137         };
12138             
12139         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12140             
12141             inputblock = {
12142                 cls : 'has-feedback',
12143                 cn :  [
12144                     input,
12145                     feedback
12146                 ] 
12147             };  
12148         }
12149         
12150         if (this.before || this.after) {
12151             
12152             inputblock = {
12153                 cls : 'input-group',
12154                 cn :  [] 
12155             };
12156             
12157             if (this.before && typeof(this.before) == 'string') {
12158                 
12159                 inputblock.cn.push({
12160                     tag :'span',
12161                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12162                     html : this.before
12163                 });
12164             }
12165             if (this.before && typeof(this.before) == 'object') {
12166                 this.before = Roo.factory(this.before);
12167                 
12168                 inputblock.cn.push({
12169                     tag :'span',
12170                     cls : 'roo-input-before input-group-prepend   input-group-' +
12171                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12172                 });
12173             }
12174             
12175             inputblock.cn.push(input);
12176             
12177             if (this.after && typeof(this.after) == 'string') {
12178                 inputblock.cn.push({
12179                     tag :'span',
12180                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12181                     html : this.after
12182                 });
12183             }
12184             if (this.after && typeof(this.after) == 'object') {
12185                 this.after = Roo.factory(this.after);
12186                 
12187                 inputblock.cn.push({
12188                     tag :'span',
12189                     cls : 'roo-input-after input-group-append  input-group-' +
12190                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12191                 });
12192             }
12193             
12194             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12195                 inputblock.cls += ' has-feedback';
12196                 inputblock.cn.push(feedback);
12197             }
12198         };
12199         var indicator = {
12200             tag : 'i',
12201             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12202             tooltip : 'This field is required'
12203         };
12204         if (this.allowBlank ) {
12205             indicator.style = this.allowBlank ? ' display:none' : '';
12206         }
12207         if (align ==='left' && this.fieldLabel.length) {
12208             
12209             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12210             
12211             cfg.cn = [
12212                 indicator,
12213                 {
12214                     tag: 'label',
12215                     'for' :  id,
12216                     cls : 'control-label col-form-label',
12217                     html : this.fieldLabel
12218
12219                 },
12220                 {
12221                     cls : "", 
12222                     cn: [
12223                         inputblock
12224                     ]
12225                 }
12226             ];
12227             
12228             var labelCfg = cfg.cn[1];
12229             var contentCfg = cfg.cn[2];
12230             
12231             if(this.indicatorpos == 'right'){
12232                 cfg.cn = [
12233                     {
12234                         tag: 'label',
12235                         'for' :  id,
12236                         cls : 'control-label col-form-label',
12237                         cn : [
12238                             {
12239                                 tag : 'span',
12240                                 html : this.fieldLabel
12241                             },
12242                             indicator
12243                         ]
12244                     },
12245                     {
12246                         cls : "",
12247                         cn: [
12248                             inputblock
12249                         ]
12250                     }
12251
12252                 ];
12253                 
12254                 labelCfg = cfg.cn[0];
12255                 contentCfg = cfg.cn[1];
12256             
12257             }
12258             
12259             if(this.labelWidth > 12){
12260                 labelCfg.style = "width: " + this.labelWidth + 'px';
12261             }
12262             
12263             if(this.labelWidth < 13 && this.labelmd == 0){
12264                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12265             }
12266             
12267             if(this.labellg > 0){
12268                 labelCfg.cls += ' col-lg-' + this.labellg;
12269                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12270             }
12271             
12272             if(this.labelmd > 0){
12273                 labelCfg.cls += ' col-md-' + this.labelmd;
12274                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12275             }
12276             
12277             if(this.labelsm > 0){
12278                 labelCfg.cls += ' col-sm-' + this.labelsm;
12279                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12280             }
12281             
12282             if(this.labelxs > 0){
12283                 labelCfg.cls += ' col-xs-' + this.labelxs;
12284                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12285             }
12286             
12287             
12288         } else if ( this.fieldLabel.length) {
12289                 
12290             
12291             
12292             cfg.cn = [
12293                 {
12294                     tag : 'i',
12295                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12296                     tooltip : 'This field is required',
12297                     style : this.allowBlank ? ' display:none' : '' 
12298                 },
12299                 {
12300                     tag: 'label',
12301                    //cls : 'input-group-addon',
12302                     html : this.fieldLabel
12303
12304                 },
12305
12306                inputblock
12307
12308            ];
12309            
12310            if(this.indicatorpos == 'right'){
12311        
12312                 cfg.cn = [
12313                     {
12314                         tag: 'label',
12315                        //cls : 'input-group-addon',
12316                         html : this.fieldLabel
12317
12318                     },
12319                     {
12320                         tag : 'i',
12321                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12322                         tooltip : 'This field is required',
12323                         style : this.allowBlank ? ' display:none' : '' 
12324                     },
12325
12326                    inputblock
12327
12328                ];
12329
12330             }
12331
12332         } else {
12333             
12334             cfg.cn = [
12335
12336                     inputblock
12337
12338             ];
12339                 
12340                 
12341         };
12342         
12343         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12344            cfg.cls += ' navbar-form';
12345         }
12346         
12347         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12348             // on BS4 we do this only if not form 
12349             cfg.cls += ' navbar-form';
12350             cfg.tag = 'li';
12351         }
12352         
12353         return cfg;
12354         
12355     },
12356     /**
12357      * return the real input element.
12358      */
12359     inputEl: function ()
12360     {
12361         return this.el.select('input.form-control',true).first();
12362     },
12363     
12364     tooltipEl : function()
12365     {
12366         return this.inputEl();
12367     },
12368     
12369     indicatorEl : function()
12370     {
12371         if (Roo.bootstrap.version == 4) {
12372             return false; // not enabled in v4 yet.
12373         }
12374         
12375         var indicator = this.el.select('i.roo-required-indicator',true).first();
12376         
12377         if(!indicator){
12378             return false;
12379         }
12380         
12381         return indicator;
12382         
12383     },
12384     
12385     setDisabled : function(v)
12386     {
12387         var i  = this.inputEl().dom;
12388         if (!v) {
12389             i.removeAttribute('disabled');
12390             return;
12391             
12392         }
12393         i.setAttribute('disabled','true');
12394     },
12395     initEvents : function()
12396     {
12397           
12398         this.inputEl().on("keydown" , this.fireKey,  this);
12399         this.inputEl().on("focus", this.onFocus,  this);
12400         this.inputEl().on("blur", this.onBlur,  this);
12401         
12402         this.inputEl().relayEvent('keyup', this);
12403         this.inputEl().relayEvent('paste', this);
12404         
12405         this.indicator = this.indicatorEl();
12406         
12407         if(this.indicator){
12408             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12409         }
12410  
12411         // reference to original value for reset
12412         this.originalValue = this.getValue();
12413         //Roo.form.TextField.superclass.initEvents.call(this);
12414         if(this.validationEvent == 'keyup'){
12415             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12416             this.inputEl().on('keyup', this.filterValidation, this);
12417         }
12418         else if(this.validationEvent !== false){
12419             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12420         }
12421         
12422         if(this.selectOnFocus){
12423             this.on("focus", this.preFocus, this);
12424             
12425         }
12426         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12427             this.inputEl().on("keypress", this.filterKeys, this);
12428         } else {
12429             this.inputEl().relayEvent('keypress', this);
12430         }
12431        /* if(this.grow){
12432             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12433             this.el.on("click", this.autoSize,  this);
12434         }
12435         */
12436         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12437             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12438         }
12439         
12440         if (typeof(this.before) == 'object') {
12441             this.before.render(this.el.select('.roo-input-before',true).first());
12442         }
12443         if (typeof(this.after) == 'object') {
12444             this.after.render(this.el.select('.roo-input-after',true).first());
12445         }
12446         
12447         this.inputEl().on('change', this.onChange, this);
12448         
12449     },
12450     filterValidation : function(e){
12451         if(!e.isNavKeyPress()){
12452             this.validationTask.delay(this.validationDelay);
12453         }
12454     },
12455      /**
12456      * Validates the field value
12457      * @return {Boolean} True if the value is valid, else false
12458      */
12459     validate : function(){
12460         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12461         if(this.disabled || this.validateValue(this.getRawValue())){
12462             this.markValid();
12463             return true;
12464         }
12465         
12466         this.markInvalid();
12467         return false;
12468     },
12469     
12470     
12471     /**
12472      * Validates a value according to the field's validation rules and marks the field as invalid
12473      * if the validation fails
12474      * @param {Mixed} value The value to validate
12475      * @return {Boolean} True if the value is valid, else false
12476      */
12477     validateValue : function(value)
12478     {
12479         if(this.getVisibilityEl().hasClass('hidden')){
12480             return true;
12481         }
12482         
12483         if(value.length < 1)  { // if it's blank
12484             if(this.allowBlank){
12485                 return true;
12486             }
12487             return false;
12488         }
12489         
12490         if(value.length < this.minLength){
12491             return false;
12492         }
12493         if(value.length > this.maxLength){
12494             return false;
12495         }
12496         if(this.vtype){
12497             var vt = Roo.form.VTypes;
12498             if(!vt[this.vtype](value, this)){
12499                 return false;
12500             }
12501         }
12502         if(typeof this.validator == "function"){
12503             var msg = this.validator(value);
12504             if(msg !== true){
12505                 return false;
12506             }
12507             if (typeof(msg) == 'string') {
12508                 this.invalidText = msg;
12509             }
12510         }
12511         
12512         if(this.regex && !this.regex.test(value)){
12513             return false;
12514         }
12515         
12516         return true;
12517     },
12518     
12519      // private
12520     fireKey : function(e){
12521         //Roo.log('field ' + e.getKey());
12522         if(e.isNavKeyPress()){
12523             this.fireEvent("specialkey", this, e);
12524         }
12525     },
12526     focus : function (selectText){
12527         if(this.rendered){
12528             this.inputEl().focus();
12529             if(selectText === true){
12530                 this.inputEl().dom.select();
12531             }
12532         }
12533         return this;
12534     } ,
12535     
12536     onFocus : function(){
12537         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12538            // this.el.addClass(this.focusClass);
12539         }
12540         if(!this.hasFocus){
12541             this.hasFocus = true;
12542             this.startValue = this.getValue();
12543             this.fireEvent("focus", this);
12544         }
12545     },
12546     
12547     beforeBlur : Roo.emptyFn,
12548
12549     
12550     // private
12551     onBlur : function(){
12552         this.beforeBlur();
12553         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12554             //this.el.removeClass(this.focusClass);
12555         }
12556         this.hasFocus = false;
12557         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12558             this.validate();
12559         }
12560         var v = this.getValue();
12561         if(String(v) !== String(this.startValue)){
12562             this.fireEvent('change', this, v, this.startValue);
12563         }
12564         this.fireEvent("blur", this);
12565     },
12566     
12567     onChange : function(e)
12568     {
12569         var v = this.getValue();
12570         if(String(v) !== String(this.startValue)){
12571             this.fireEvent('change', this, v, this.startValue);
12572         }
12573         
12574     },
12575     
12576     /**
12577      * Resets the current field value to the originally loaded value and clears any validation messages
12578      */
12579     reset : function(){
12580         this.setValue(this.originalValue);
12581         this.validate();
12582     },
12583      /**
12584      * Returns the name of the field
12585      * @return {Mixed} name The name field
12586      */
12587     getName: function(){
12588         return this.name;
12589     },
12590      /**
12591      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12592      * @return {Mixed} value The field value
12593      */
12594     getValue : function(){
12595         
12596         var v = this.inputEl().getValue();
12597         
12598         return v;
12599     },
12600     /**
12601      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12602      * @return {Mixed} value The field value
12603      */
12604     getRawValue : function(){
12605         var v = this.inputEl().getValue();
12606         
12607         return v;
12608     },
12609     
12610     /**
12611      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12612      * @param {Mixed} value The value to set
12613      */
12614     setRawValue : function(v){
12615         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12616     },
12617     
12618     selectText : function(start, end){
12619         var v = this.getRawValue();
12620         if(v.length > 0){
12621             start = start === undefined ? 0 : start;
12622             end = end === undefined ? v.length : end;
12623             var d = this.inputEl().dom;
12624             if(d.setSelectionRange){
12625                 d.setSelectionRange(start, end);
12626             }else if(d.createTextRange){
12627                 var range = d.createTextRange();
12628                 range.moveStart("character", start);
12629                 range.moveEnd("character", v.length-end);
12630                 range.select();
12631             }
12632         }
12633     },
12634     
12635     /**
12636      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12637      * @param {Mixed} value The value to set
12638      */
12639     setValue : function(v){
12640         this.value = v;
12641         if(this.rendered){
12642             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12643             this.validate();
12644         }
12645     },
12646     
12647     /*
12648     processValue : function(value){
12649         if(this.stripCharsRe){
12650             var newValue = value.replace(this.stripCharsRe, '');
12651             if(newValue !== value){
12652                 this.setRawValue(newValue);
12653                 return newValue;
12654             }
12655         }
12656         return value;
12657     },
12658   */
12659     preFocus : function(){
12660         
12661         if(this.selectOnFocus){
12662             this.inputEl().dom.select();
12663         }
12664     },
12665     filterKeys : function(e){
12666         var k = e.getKey();
12667         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12668             return;
12669         }
12670         var c = e.getCharCode(), cc = String.fromCharCode(c);
12671         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12672             return;
12673         }
12674         if(!this.maskRe.test(cc)){
12675             e.stopEvent();
12676         }
12677     },
12678      /**
12679      * Clear any invalid styles/messages for this field
12680      */
12681     clearInvalid : function(){
12682         
12683         if(!this.el || this.preventMark){ // not rendered
12684             return;
12685         }
12686         
12687         
12688         this.el.removeClass([this.invalidClass, 'is-invalid']);
12689         
12690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12691             
12692             var feedback = this.el.select('.form-control-feedback', true).first();
12693             
12694             if(feedback){
12695                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12696             }
12697             
12698         }
12699         
12700         if(this.indicator){
12701             this.indicator.removeClass('visible');
12702             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12703         }
12704         
12705         this.fireEvent('valid', this);
12706     },
12707     
12708      /**
12709      * Mark this field as valid
12710      */
12711     markValid : function()
12712     {
12713         if(!this.el  || this.preventMark){ // not rendered...
12714             return;
12715         }
12716         
12717         this.el.removeClass([this.invalidClass, this.validClass]);
12718         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12719
12720         var feedback = this.el.select('.form-control-feedback', true).first();
12721             
12722         if(feedback){
12723             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12724         }
12725         
12726         if(this.indicator){
12727             this.indicator.removeClass('visible');
12728             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12729         }
12730         
12731         if(this.disabled){
12732             return;
12733         }
12734         
12735            
12736         if(this.allowBlank && !this.getRawValue().length){
12737             return;
12738         }
12739         if (Roo.bootstrap.version == 3) {
12740             this.el.addClass(this.validClass);
12741         } else {
12742             this.inputEl().addClass('is-valid');
12743         }
12744
12745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12746             
12747             var feedback = this.el.select('.form-control-feedback', true).first();
12748             
12749             if(feedback){
12750                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12751                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12752             }
12753             
12754         }
12755         
12756         this.fireEvent('valid', this);
12757     },
12758     
12759      /**
12760      * Mark this field as invalid
12761      * @param {String} msg The validation message
12762      */
12763     markInvalid : function(msg)
12764     {
12765         if(!this.el  || this.preventMark){ // not rendered
12766             return;
12767         }
12768         
12769         this.el.removeClass([this.invalidClass, this.validClass]);
12770         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12771         
12772         var feedback = this.el.select('.form-control-feedback', true).first();
12773             
12774         if(feedback){
12775             this.el.select('.form-control-feedback', true).first().removeClass(
12776                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12777         }
12778
12779         if(this.disabled){
12780             return;
12781         }
12782         
12783         if(this.allowBlank && !this.getRawValue().length){
12784             return;
12785         }
12786         
12787         if(this.indicator){
12788             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12789             this.indicator.addClass('visible');
12790         }
12791         if (Roo.bootstrap.version == 3) {
12792             this.el.addClass(this.invalidClass);
12793         } else {
12794             this.inputEl().addClass('is-invalid');
12795         }
12796         
12797         
12798         
12799         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12800             
12801             var feedback = this.el.select('.form-control-feedback', true).first();
12802             
12803             if(feedback){
12804                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12805                 
12806                 if(this.getValue().length || this.forceFeedback){
12807                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12808                 }
12809                 
12810             }
12811             
12812         }
12813         
12814         this.fireEvent('invalid', this, msg);
12815     },
12816     // private
12817     SafariOnKeyDown : function(event)
12818     {
12819         // this is a workaround for a password hang bug on chrome/ webkit.
12820         if (this.inputEl().dom.type != 'password') {
12821             return;
12822         }
12823         
12824         var isSelectAll = false;
12825         
12826         if(this.inputEl().dom.selectionEnd > 0){
12827             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12828         }
12829         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12830             event.preventDefault();
12831             this.setValue('');
12832             return;
12833         }
12834         
12835         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12836             
12837             event.preventDefault();
12838             // this is very hacky as keydown always get's upper case.
12839             //
12840             var cc = String.fromCharCode(event.getCharCode());
12841             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12842             
12843         }
12844     },
12845     adjustWidth : function(tag, w){
12846         tag = tag.toLowerCase();
12847         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12848             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12849                 if(tag == 'input'){
12850                     return w + 2;
12851                 }
12852                 if(tag == 'textarea'){
12853                     return w-2;
12854                 }
12855             }else if(Roo.isOpera){
12856                 if(tag == 'input'){
12857                     return w + 2;
12858                 }
12859                 if(tag == 'textarea'){
12860                     return w-2;
12861                 }
12862             }
12863         }
12864         return w;
12865     },
12866     
12867     setFieldLabel : function(v)
12868     {
12869         if(!this.rendered){
12870             return;
12871         }
12872         
12873         if(this.indicatorEl()){
12874             var ar = this.el.select('label > span',true);
12875             
12876             if (ar.elements.length) {
12877                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878                 this.fieldLabel = v;
12879                 return;
12880             }
12881             
12882             var br = this.el.select('label',true);
12883             
12884             if(br.elements.length) {
12885                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12886                 this.fieldLabel = v;
12887                 return;
12888             }
12889             
12890             Roo.log('Cannot Found any of label > span || label in input');
12891             return;
12892         }
12893         
12894         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12895         this.fieldLabel = v;
12896         
12897         
12898     }
12899 });
12900
12901  
12902 /*
12903  * - LGPL
12904  *
12905  * Input
12906  * 
12907  */
12908
12909 /**
12910  * @class Roo.bootstrap.TextArea
12911  * @extends Roo.bootstrap.Input
12912  * Bootstrap TextArea class
12913  * @cfg {Number} cols Specifies the visible width of a text area
12914  * @cfg {Number} rows Specifies the visible number of lines in a text area
12915  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12916  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12917  * @cfg {string} html text
12918  * 
12919  * @constructor
12920  * Create a new TextArea
12921  * @param {Object} config The config object
12922  */
12923
12924 Roo.bootstrap.TextArea = function(config){
12925     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12926    
12927 };
12928
12929 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12930      
12931     cols : false,
12932     rows : 5,
12933     readOnly : false,
12934     warp : 'soft',
12935     resize : false,
12936     value: false,
12937     html: false,
12938     
12939     getAutoCreate : function(){
12940         
12941         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12942         
12943         var id = Roo.id();
12944         
12945         var cfg = {};
12946         
12947         if(this.inputType != 'hidden'){
12948             cfg.cls = 'form-group' //input-group
12949         }
12950         
12951         var input =  {
12952             tag: 'textarea',
12953             id : id,
12954             warp : this.warp,
12955             rows : this.rows,
12956             value : this.value || '',
12957             html: this.html || '',
12958             cls : 'form-control',
12959             placeholder : this.placeholder || '' 
12960             
12961         };
12962         
12963         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12964             input.maxLength = this.maxLength;
12965         }
12966         
12967         if(this.resize){
12968             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12969         }
12970         
12971         if(this.cols){
12972             input.cols = this.cols;
12973         }
12974         
12975         if (this.readOnly) {
12976             input.readonly = true;
12977         }
12978         
12979         if (this.name) {
12980             input.name = this.name;
12981         }
12982         
12983         if (this.size) {
12984             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12985         }
12986         
12987         var settings=this;
12988         ['xs','sm','md','lg'].map(function(size){
12989             if (settings[size]) {
12990                 cfg.cls += ' col-' + size + '-' + settings[size];
12991             }
12992         });
12993         
12994         var inputblock = input;
12995         
12996         if(this.hasFeedback && !this.allowBlank){
12997             
12998             var feedback = {
12999                 tag: 'span',
13000                 cls: 'glyphicon form-control-feedback'
13001             };
13002
13003             inputblock = {
13004                 cls : 'has-feedback',
13005                 cn :  [
13006                     input,
13007                     feedback
13008                 ] 
13009             };  
13010         }
13011         
13012         
13013         if (this.before || this.after) {
13014             
13015             inputblock = {
13016                 cls : 'input-group',
13017                 cn :  [] 
13018             };
13019             if (this.before) {
13020                 inputblock.cn.push({
13021                     tag :'span',
13022                     cls : 'input-group-addon',
13023                     html : this.before
13024                 });
13025             }
13026             
13027             inputblock.cn.push(input);
13028             
13029             if(this.hasFeedback && !this.allowBlank){
13030                 inputblock.cls += ' has-feedback';
13031                 inputblock.cn.push(feedback);
13032             }
13033             
13034             if (this.after) {
13035                 inputblock.cn.push({
13036                     tag :'span',
13037                     cls : 'input-group-addon',
13038                     html : this.after
13039                 });
13040             }
13041             
13042         }
13043         
13044         if (align ==='left' && this.fieldLabel.length) {
13045             cfg.cn = [
13046                 {
13047                     tag: 'label',
13048                     'for' :  id,
13049                     cls : 'control-label',
13050                     html : this.fieldLabel
13051                 },
13052                 {
13053                     cls : "",
13054                     cn: [
13055                         inputblock
13056                     ]
13057                 }
13058
13059             ];
13060             
13061             if(this.labelWidth > 12){
13062                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13063             }
13064
13065             if(this.labelWidth < 13 && this.labelmd == 0){
13066                 this.labelmd = this.labelWidth;
13067             }
13068
13069             if(this.labellg > 0){
13070                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13071                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13072             }
13073
13074             if(this.labelmd > 0){
13075                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13076                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13077             }
13078
13079             if(this.labelsm > 0){
13080                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13081                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13082             }
13083
13084             if(this.labelxs > 0){
13085                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13086                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13087             }
13088             
13089         } else if ( this.fieldLabel.length) {
13090             cfg.cn = [
13091
13092                {
13093                    tag: 'label',
13094                    //cls : 'input-group-addon',
13095                    html : this.fieldLabel
13096
13097                },
13098
13099                inputblock
13100
13101            ];
13102
13103         } else {
13104
13105             cfg.cn = [
13106
13107                 inputblock
13108
13109             ];
13110                 
13111         }
13112         
13113         if (this.disabled) {
13114             input.disabled=true;
13115         }
13116         
13117         return cfg;
13118         
13119     },
13120     /**
13121      * return the real textarea element.
13122      */
13123     inputEl: function ()
13124     {
13125         return this.el.select('textarea.form-control',true).first();
13126     },
13127     
13128     /**
13129      * Clear any invalid styles/messages for this field
13130      */
13131     clearInvalid : function()
13132     {
13133         
13134         if(!this.el || this.preventMark){ // not rendered
13135             return;
13136         }
13137         
13138         var label = this.el.select('label', true).first();
13139         var icon = this.el.select('i.fa-star', true).first();
13140         
13141         if(label && icon){
13142             icon.remove();
13143         }
13144         this.el.removeClass( this.validClass);
13145         this.inputEl().removeClass('is-invalid');
13146          
13147         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13148             
13149             var feedback = this.el.select('.form-control-feedback', true).first();
13150             
13151             if(feedback){
13152                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13153             }
13154             
13155         }
13156         
13157         this.fireEvent('valid', this);
13158     },
13159     
13160      /**
13161      * Mark this field as valid
13162      */
13163     markValid : function()
13164     {
13165         if(!this.el  || this.preventMark){ // not rendered
13166             return;
13167         }
13168         
13169         this.el.removeClass([this.invalidClass, this.validClass]);
13170         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13171         
13172         var feedback = this.el.select('.form-control-feedback', true).first();
13173             
13174         if(feedback){
13175             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13176         }
13177
13178         if(this.disabled || this.allowBlank){
13179             return;
13180         }
13181         
13182         var label = this.el.select('label', true).first();
13183         var icon = this.el.select('i.fa-star', true).first();
13184         
13185         if(label && icon){
13186             icon.remove();
13187         }
13188         if (Roo.bootstrap.version == 3) {
13189             this.el.addClass(this.validClass);
13190         } else {
13191             this.inputEl().addClass('is-valid');
13192         }
13193         
13194         
13195         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13196             
13197             var feedback = this.el.select('.form-control-feedback', true).first();
13198             
13199             if(feedback){
13200                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13201                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13202             }
13203             
13204         }
13205         
13206         this.fireEvent('valid', this);
13207     },
13208     
13209      /**
13210      * Mark this field as invalid
13211      * @param {String} msg The validation message
13212      */
13213     markInvalid : function(msg)
13214     {
13215         if(!this.el  || this.preventMark){ // not rendered
13216             return;
13217         }
13218         
13219         this.el.removeClass([this.invalidClass, this.validClass]);
13220         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13221         
13222         var feedback = this.el.select('.form-control-feedback', true).first();
13223             
13224         if(feedback){
13225             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226         }
13227
13228         if(this.disabled || this.allowBlank){
13229             return;
13230         }
13231         
13232         var label = this.el.select('label', true).first();
13233         var icon = this.el.select('i.fa-star', true).first();
13234         
13235         if(!this.getValue().length && label && !icon){
13236             this.el.createChild({
13237                 tag : 'i',
13238                 cls : 'text-danger fa fa-lg fa-star',
13239                 tooltip : 'This field is required',
13240                 style : 'margin-right:5px;'
13241             }, label, true);
13242         }
13243         
13244         if (Roo.bootstrap.version == 3) {
13245             this.el.addClass(this.invalidClass);
13246         } else {
13247             this.inputEl().addClass('is-invalid');
13248         }
13249         
13250         // fixme ... this may be depricated need to test..
13251         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13252             
13253             var feedback = this.el.select('.form-control-feedback', true).first();
13254             
13255             if(feedback){
13256                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13257                 
13258                 if(this.getValue().length || this.forceFeedback){
13259                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13260                 }
13261                 
13262             }
13263             
13264         }
13265         
13266         this.fireEvent('invalid', this, msg);
13267     }
13268 });
13269
13270  
13271 /*
13272  * - LGPL
13273  *
13274  * trigger field - base class for combo..
13275  * 
13276  */
13277  
13278 /**
13279  * @class Roo.bootstrap.TriggerField
13280  * @extends Roo.bootstrap.Input
13281  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13282  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13283  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13284  * for which you can provide a custom implementation.  For example:
13285  * <pre><code>
13286 var trigger = new Roo.bootstrap.TriggerField();
13287 trigger.onTriggerClick = myTriggerFn;
13288 trigger.applyTo('my-field');
13289 </code></pre>
13290  *
13291  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13292  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13293  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13294  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13295  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13296
13297  * @constructor
13298  * Create a new TriggerField.
13299  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13300  * to the base TextField)
13301  */
13302 Roo.bootstrap.TriggerField = function(config){
13303     this.mimicing = false;
13304     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13305 };
13306
13307 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13308     /**
13309      * @cfg {String} triggerClass A CSS class to apply to the trigger
13310      */
13311      /**
13312      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13313      */
13314     hideTrigger:false,
13315
13316     /**
13317      * @cfg {Boolean} removable (true|false) special filter default false
13318      */
13319     removable : false,
13320     
13321     /** @cfg {Boolean} grow @hide */
13322     /** @cfg {Number} growMin @hide */
13323     /** @cfg {Number} growMax @hide */
13324
13325     /**
13326      * @hide 
13327      * @method
13328      */
13329     autoSize: Roo.emptyFn,
13330     // private
13331     monitorTab : true,
13332     // private
13333     deferHeight : true,
13334
13335     
13336     actionMode : 'wrap',
13337     
13338     caret : false,
13339     
13340     
13341     getAutoCreate : function(){
13342        
13343         var align = this.labelAlign || this.parentLabelAlign();
13344         
13345         var id = Roo.id();
13346         
13347         var cfg = {
13348             cls: 'form-group' //input-group
13349         };
13350         
13351         
13352         var input =  {
13353             tag: 'input',
13354             id : id,
13355             type : this.inputType,
13356             cls : 'form-control',
13357             autocomplete: 'new-password',
13358             placeholder : this.placeholder || '' 
13359             
13360         };
13361         if (this.name) {
13362             input.name = this.name;
13363         }
13364         if (this.size) {
13365             input.cls += ' input-' + this.size;
13366         }
13367         
13368         if (this.disabled) {
13369             input.disabled=true;
13370         }
13371         
13372         var inputblock = input;
13373         
13374         if(this.hasFeedback && !this.allowBlank){
13375             
13376             var feedback = {
13377                 tag: 'span',
13378                 cls: 'glyphicon form-control-feedback'
13379             };
13380             
13381             if(this.removable && !this.editable  ){
13382                 inputblock = {
13383                     cls : 'has-feedback',
13384                     cn :  [
13385                         inputblock,
13386                         {
13387                             tag: 'button',
13388                             html : 'x',
13389                             cls : 'roo-combo-removable-btn close'
13390                         },
13391                         feedback
13392                     ] 
13393                 };
13394             } else {
13395                 inputblock = {
13396                     cls : 'has-feedback',
13397                     cn :  [
13398                         inputblock,
13399                         feedback
13400                     ] 
13401                 };
13402             }
13403
13404         } else {
13405             if(this.removable && !this.editable ){
13406                 inputblock = {
13407                     cls : 'roo-removable',
13408                     cn :  [
13409                         inputblock,
13410                         {
13411                             tag: 'button',
13412                             html : 'x',
13413                             cls : 'roo-combo-removable-btn close'
13414                         }
13415                     ] 
13416                 };
13417             }
13418         }
13419         
13420         if (this.before || this.after) {
13421             
13422             inputblock = {
13423                 cls : 'input-group',
13424                 cn :  [] 
13425             };
13426             if (this.before) {
13427                 inputblock.cn.push({
13428                     tag :'span',
13429                     cls : 'input-group-addon input-group-prepend input-group-text',
13430                     html : this.before
13431                 });
13432             }
13433             
13434             inputblock.cn.push(input);
13435             
13436             if(this.hasFeedback && !this.allowBlank){
13437                 inputblock.cls += ' has-feedback';
13438                 inputblock.cn.push(feedback);
13439             }
13440             
13441             if (this.after) {
13442                 inputblock.cn.push({
13443                     tag :'span',
13444                     cls : 'input-group-addon input-group-append input-group-text',
13445                     html : this.after
13446                 });
13447             }
13448             
13449         };
13450         
13451       
13452         
13453         var ibwrap = inputblock;
13454         
13455         if(this.multiple){
13456             ibwrap = {
13457                 tag: 'ul',
13458                 cls: 'roo-select2-choices',
13459                 cn:[
13460                     {
13461                         tag: 'li',
13462                         cls: 'roo-select2-search-field',
13463                         cn: [
13464
13465                             inputblock
13466                         ]
13467                     }
13468                 ]
13469             };
13470                 
13471         }
13472         
13473         var combobox = {
13474             cls: 'roo-select2-container input-group',
13475             cn: [
13476                  {
13477                     tag: 'input',
13478                     type : 'hidden',
13479                     cls: 'form-hidden-field'
13480                 },
13481                 ibwrap
13482             ]
13483         };
13484         
13485         if(!this.multiple && this.showToggleBtn){
13486             
13487             var caret = {
13488                         tag: 'span',
13489                         cls: 'caret'
13490              };
13491             if (this.caret != false) {
13492                 caret = {
13493                      tag: 'i',
13494                      cls: 'fa fa-' + this.caret
13495                 };
13496                 
13497             }
13498             
13499             combobox.cn.push({
13500                 tag :'span',
13501                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13502                 cn : [
13503                     Roo.bootstrap.version == 3 ? caret : '',
13504                     {
13505                         tag: 'span',
13506                         cls: 'combobox-clear',
13507                         cn  : [
13508                             {
13509                                 tag : 'i',
13510                                 cls: 'icon-remove'
13511                             }
13512                         ]
13513                     }
13514                 ]
13515
13516             })
13517         }
13518         
13519         if(this.multiple){
13520             combobox.cls += ' roo-select2-container-multi';
13521         }
13522          var indicator = {
13523             tag : 'i',
13524             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13525             tooltip : 'This field is required'
13526         };
13527         if (Roo.bootstrap.version == 4) {
13528             indicator = {
13529                 tag : 'i',
13530                 style : 'display:none'
13531             };
13532         }
13533         
13534         
13535         if (align ==='left' && this.fieldLabel.length) {
13536             
13537             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13538
13539             cfg.cn = [
13540                 indicator,
13541                 {
13542                     tag: 'label',
13543                     'for' :  id,
13544                     cls : 'control-label',
13545                     html : this.fieldLabel
13546
13547                 },
13548                 {
13549                     cls : "", 
13550                     cn: [
13551                         combobox
13552                     ]
13553                 }
13554
13555             ];
13556             
13557             var labelCfg = cfg.cn[1];
13558             var contentCfg = cfg.cn[2];
13559             
13560             if(this.indicatorpos == 'right'){
13561                 cfg.cn = [
13562                     {
13563                         tag: 'label',
13564                         'for' :  id,
13565                         cls : 'control-label',
13566                         cn : [
13567                             {
13568                                 tag : 'span',
13569                                 html : this.fieldLabel
13570                             },
13571                             indicator
13572                         ]
13573                     },
13574                     {
13575                         cls : "", 
13576                         cn: [
13577                             combobox
13578                         ]
13579                     }
13580
13581                 ];
13582                 
13583                 labelCfg = cfg.cn[0];
13584                 contentCfg = cfg.cn[1];
13585             }
13586             
13587             if(this.labelWidth > 12){
13588                 labelCfg.style = "width: " + this.labelWidth + 'px';
13589             }
13590             
13591             if(this.labelWidth < 13 && this.labelmd == 0){
13592                 this.labelmd = this.labelWidth;
13593             }
13594             
13595             if(this.labellg > 0){
13596                 labelCfg.cls += ' col-lg-' + this.labellg;
13597                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13598             }
13599             
13600             if(this.labelmd > 0){
13601                 labelCfg.cls += ' col-md-' + this.labelmd;
13602                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13603             }
13604             
13605             if(this.labelsm > 0){
13606                 labelCfg.cls += ' col-sm-' + this.labelsm;
13607                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13608             }
13609             
13610             if(this.labelxs > 0){
13611                 labelCfg.cls += ' col-xs-' + this.labelxs;
13612                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13613             }
13614             
13615         } else if ( this.fieldLabel.length) {
13616 //                Roo.log(" label");
13617             cfg.cn = [
13618                 indicator,
13619                {
13620                    tag: 'label',
13621                    //cls : 'input-group-addon',
13622                    html : this.fieldLabel
13623
13624                },
13625
13626                combobox
13627
13628             ];
13629             
13630             if(this.indicatorpos == 'right'){
13631                 
13632                 cfg.cn = [
13633                     {
13634                        tag: 'label',
13635                        cn : [
13636                            {
13637                                tag : 'span',
13638                                html : this.fieldLabel
13639                            },
13640                            indicator
13641                        ]
13642
13643                     },
13644                     combobox
13645
13646                 ];
13647
13648             }
13649
13650         } else {
13651             
13652 //                Roo.log(" no label && no align");
13653                 cfg = combobox
13654                      
13655                 
13656         }
13657         
13658         var settings=this;
13659         ['xs','sm','md','lg'].map(function(size){
13660             if (settings[size]) {
13661                 cfg.cls += ' col-' + size + '-' + settings[size];
13662             }
13663         });
13664         
13665         return cfg;
13666         
13667     },
13668     
13669     
13670     
13671     // private
13672     onResize : function(w, h){
13673 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13674 //        if(typeof w == 'number'){
13675 //            var x = w - this.trigger.getWidth();
13676 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13677 //            this.trigger.setStyle('left', x+'px');
13678 //        }
13679     },
13680
13681     // private
13682     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13683
13684     // private
13685     getResizeEl : function(){
13686         return this.inputEl();
13687     },
13688
13689     // private
13690     getPositionEl : function(){
13691         return this.inputEl();
13692     },
13693
13694     // private
13695     alignErrorIcon : function(){
13696         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13697     },
13698
13699     // private
13700     initEvents : function(){
13701         
13702         this.createList();
13703         
13704         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13705         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13706         if(!this.multiple && this.showToggleBtn){
13707             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13708             if(this.hideTrigger){
13709                 this.trigger.setDisplayed(false);
13710             }
13711             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13712         }
13713         
13714         if(this.multiple){
13715             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13716         }
13717         
13718         if(this.removable && !this.editable && !this.tickable){
13719             var close = this.closeTriggerEl();
13720             
13721             if(close){
13722                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13723                 close.on('click', this.removeBtnClick, this, close);
13724             }
13725         }
13726         
13727         //this.trigger.addClassOnOver('x-form-trigger-over');
13728         //this.trigger.addClassOnClick('x-form-trigger-click');
13729         
13730         //if(!this.width){
13731         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13732         //}
13733     },
13734     
13735     closeTriggerEl : function()
13736     {
13737         var close = this.el.select('.roo-combo-removable-btn', true).first();
13738         return close ? close : false;
13739     },
13740     
13741     removeBtnClick : function(e, h, el)
13742     {
13743         e.preventDefault();
13744         
13745         if(this.fireEvent("remove", this) !== false){
13746             this.reset();
13747             this.fireEvent("afterremove", this)
13748         }
13749     },
13750     
13751     createList : function()
13752     {
13753         this.list = Roo.get(document.body).createChild({
13754             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13755             cls: 'typeahead typeahead-long dropdown-menu shadow',
13756             style: 'display:none'
13757         });
13758         
13759         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13760         
13761     },
13762
13763     // private
13764     initTrigger : function(){
13765        
13766     },
13767
13768     // private
13769     onDestroy : function(){
13770         if(this.trigger){
13771             this.trigger.removeAllListeners();
13772           //  this.trigger.remove();
13773         }
13774         //if(this.wrap){
13775         //    this.wrap.remove();
13776         //}
13777         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13778     },
13779
13780     // private
13781     onFocus : function(){
13782         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13783         /*
13784         if(!this.mimicing){
13785             this.wrap.addClass('x-trigger-wrap-focus');
13786             this.mimicing = true;
13787             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13788             if(this.monitorTab){
13789                 this.el.on("keydown", this.checkTab, this);
13790             }
13791         }
13792         */
13793     },
13794
13795     // private
13796     checkTab : function(e){
13797         if(e.getKey() == e.TAB){
13798             this.triggerBlur();
13799         }
13800     },
13801
13802     // private
13803     onBlur : function(){
13804         // do nothing
13805     },
13806
13807     // private
13808     mimicBlur : function(e, t){
13809         /*
13810         if(!this.wrap.contains(t) && this.validateBlur()){
13811             this.triggerBlur();
13812         }
13813         */
13814     },
13815
13816     // private
13817     triggerBlur : function(){
13818         this.mimicing = false;
13819         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13820         if(this.monitorTab){
13821             this.el.un("keydown", this.checkTab, this);
13822         }
13823         //this.wrap.removeClass('x-trigger-wrap-focus');
13824         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13825     },
13826
13827     // private
13828     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13829     validateBlur : function(e, t){
13830         return true;
13831     },
13832
13833     // private
13834     onDisable : function(){
13835         this.inputEl().dom.disabled = true;
13836         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13837         //if(this.wrap){
13838         //    this.wrap.addClass('x-item-disabled');
13839         //}
13840     },
13841
13842     // private
13843     onEnable : function(){
13844         this.inputEl().dom.disabled = false;
13845         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13846         //if(this.wrap){
13847         //    this.el.removeClass('x-item-disabled');
13848         //}
13849     },
13850
13851     // private
13852     onShow : function(){
13853         var ae = this.getActionEl();
13854         
13855         if(ae){
13856             ae.dom.style.display = '';
13857             ae.dom.style.visibility = 'visible';
13858         }
13859     },
13860
13861     // private
13862     
13863     onHide : function(){
13864         var ae = this.getActionEl();
13865         ae.dom.style.display = 'none';
13866     },
13867
13868     /**
13869      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13870      * by an implementing function.
13871      * @method
13872      * @param {EventObject} e
13873      */
13874     onTriggerClick : Roo.emptyFn
13875 });
13876  
13877 /*
13878 * Licence: LGPL
13879 */
13880
13881 /**
13882  * @class Roo.bootstrap.CardUploader
13883  * @extends Roo.bootstrap.Button
13884  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13885  * @cfg {Number} errorTimeout default 3000
13886  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13887  * @cfg {Array}  html The button text.
13888
13889  *
13890  * @constructor
13891  * Create a new CardUploader
13892  * @param {Object} config The config object
13893  */
13894
13895 Roo.bootstrap.CardUploader = function(config){
13896     
13897  
13898     
13899     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13900     
13901     
13902     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13903         return r.data.id
13904      });
13905     
13906      this.addEvents({
13907          // raw events
13908         /**
13909          * @event preview
13910          * When a image is clicked on - and needs to display a slideshow or similar..
13911          * @param {Roo.bootstrap.Card} this
13912          * @param {Object} The image information data 
13913          *
13914          */
13915         'preview' : true,
13916          /**
13917          * @event download
13918          * When a the download link is clicked
13919          * @param {Roo.bootstrap.Card} this
13920          * @param {Object} The image information data  contains 
13921          */
13922         'download' : true
13923         
13924     });
13925 };
13926  
13927 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13928     
13929      
13930     errorTimeout : 3000,
13931      
13932     images : false,
13933    
13934     fileCollection : false,
13935     allowBlank : true,
13936     
13937     getAutoCreate : function()
13938     {
13939         
13940         var cfg =  {
13941             cls :'form-group' ,
13942             cn : [
13943                
13944                 {
13945                     tag: 'label',
13946                    //cls : 'input-group-addon',
13947                     html : this.fieldLabel
13948
13949                 },
13950
13951                 {
13952                     tag: 'input',
13953                     type : 'hidden',
13954                     name : this.name,
13955                     value : this.value,
13956                     cls : 'd-none  form-control'
13957                 },
13958                 
13959                 {
13960                     tag: 'input',
13961                     multiple : 'multiple',
13962                     type : 'file',
13963                     cls : 'd-none  roo-card-upload-selector'
13964                 },
13965                 
13966                 {
13967                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13968                 },
13969                 {
13970                     cls : 'card-columns roo-card-uploader-container'
13971                 }
13972
13973             ]
13974         };
13975            
13976          
13977         return cfg;
13978     },
13979     
13980     getChildContainer : function() /// what children are added to.
13981     {
13982         return this.containerEl;
13983     },
13984    
13985     getButtonContainer : function() /// what children are added to.
13986     {
13987         return this.el.select(".roo-card-uploader-button-container").first();
13988     },
13989    
13990     initEvents : function()
13991     {
13992         
13993         Roo.bootstrap.Input.prototype.initEvents.call(this);
13994         
13995         var t = this;
13996         this.addxtype({
13997             xns: Roo.bootstrap,
13998
13999             xtype : 'Button',
14000             container_method : 'getButtonContainer' ,            
14001             html :  this.html, // fix changable?
14002             cls : 'w-100 ',
14003             listeners : {
14004                 'click' : function(btn, e) {
14005                     t.onClick(e);
14006                 }
14007             }
14008         });
14009         
14010         
14011         
14012         
14013         this.urlAPI = (window.createObjectURL && window) || 
14014                                 (window.URL && URL.revokeObjectURL && URL) || 
14015                                 (window.webkitURL && webkitURL);
14016                         
14017          
14018          
14019          
14020         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14021         
14022         this.selectorEl.on('change', this.onFileSelected, this);
14023         if (this.images) {
14024             var t = this;
14025             this.images.forEach(function(img) {
14026                 t.addCard(img)
14027             });
14028             this.images = false;
14029         }
14030         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14031          
14032        
14033     },
14034     
14035    
14036     onClick : function(e)
14037     {
14038         e.preventDefault();
14039          
14040         this.selectorEl.dom.click();
14041          
14042     },
14043     
14044     onFileSelected : function(e)
14045     {
14046         e.preventDefault();
14047         
14048         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14049             return;
14050         }
14051         
14052         Roo.each(this.selectorEl.dom.files, function(file){    
14053             this.addFile(file);
14054         }, this);
14055          
14056     },
14057     
14058       
14059     
14060       
14061     
14062     addFile : function(file)
14063     {
14064            
14065         if(typeof(file) === 'string'){
14066             throw "Add file by name?"; // should not happen
14067             return;
14068         }
14069         
14070         if(!file || !this.urlAPI){
14071             return;
14072         }
14073         
14074         // file;
14075         // file.type;
14076         
14077         var _this = this;
14078         
14079         
14080         var url = _this.urlAPI.createObjectURL( file);
14081            
14082         this.addCard({
14083             id : Roo.bootstrap.CardUploader.ID--,
14084             is_uploaded : false,
14085             src : url,
14086             srcfile : file,
14087             title : file.name,
14088             mimetype : file.type,
14089             preview : false,
14090             is_deleted : 0
14091         });
14092         
14093     },
14094     
14095     /**
14096      * addCard - add an Attachment to the uploader
14097      * @param data - the data about the image to upload
14098      *
14099      * {
14100           id : 123
14101           title : "Title of file",
14102           is_uploaded : false,
14103           src : "http://.....",
14104           srcfile : { the File upload object },
14105           mimetype : file.type,
14106           preview : false,
14107           is_deleted : 0
14108           .. any other data...
14109         }
14110      *
14111      * 
14112     */
14113     
14114     addCard : function (data)
14115     {
14116         // hidden input element?
14117         // if the file is not an image...
14118         //then we need to use something other that and header_image
14119         var t = this;
14120         //   remove.....
14121         var footer = [
14122             {
14123                 xns : Roo.bootstrap,
14124                 xtype : 'CardFooter',
14125                  items: [
14126                     {
14127                         xns : Roo.bootstrap,
14128                         xtype : 'Element',
14129                         cls : 'd-flex',
14130                         items : [
14131                             
14132                             {
14133                                 xns : Roo.bootstrap,
14134                                 xtype : 'Button',
14135                                 html : String.format("<small>{0}</small>", data.title),
14136                                 cls : 'col-10 text-left',
14137                                 size: 'sm',
14138                                 weight: 'link',
14139                                 fa : 'download',
14140                                 listeners : {
14141                                     click : function() {
14142                                      
14143                                         t.fireEvent( "download", t, data );
14144                                     }
14145                                 }
14146                             },
14147                           
14148                             {
14149                                 xns : Roo.bootstrap,
14150                                 xtype : 'Button',
14151                                 style: 'max-height: 28px; ',
14152                                 size : 'sm',
14153                                 weight: 'danger',
14154                                 cls : 'col-2',
14155                                 fa : 'times',
14156                                 listeners : {
14157                                     click : function() {
14158                                         t.removeCard(data.id)
14159                                     }
14160                                 }
14161                             }
14162                         ]
14163                     }
14164                     
14165                 ] 
14166             }
14167             
14168         ];
14169         
14170         var cn = this.addxtype(
14171             {
14172                  
14173                 xns : Roo.bootstrap,
14174                 xtype : 'Card',
14175                 closeable : true,
14176                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14177                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14178                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14179                 data : data,
14180                 html : false,
14181                  
14182                 items : footer,
14183                 initEvents : function() {
14184                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14185                     var card = this;
14186                     this.imgEl = this.el.select('.card-img-top').first();
14187                     if (this.imgEl) {
14188                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14189                         this.imgEl.set({ 'pointer' : 'cursor' });
14190                                   
14191                     }
14192                     this.getCardFooter().addClass('p-1');
14193                     
14194                   
14195                 }
14196                 
14197             }
14198         );
14199         // dont' really need ot update items.
14200         // this.items.push(cn);
14201         this.fileCollection.add(cn);
14202         
14203         if (!data.srcfile) {
14204             this.updateInput();
14205             return;
14206         }
14207             
14208         var _t = this;
14209         var reader = new FileReader();
14210         reader.addEventListener("load", function() {  
14211             data.srcdata =  reader.result;
14212             _t.updateInput();
14213         });
14214         reader.readAsDataURL(data.srcfile);
14215         
14216         
14217         
14218     },
14219     removeCard : function(id)
14220     {
14221         
14222         var card  = this.fileCollection.get(id);
14223         card.data.is_deleted = 1;
14224         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14225         //this.fileCollection.remove(card);
14226         //this.items = this.items.filter(function(e) { return e != card });
14227         // dont' really need ot update items.
14228         card.el.dom.parentNode.removeChild(card.el.dom);
14229         this.updateInput();
14230
14231         
14232     },
14233     reset: function()
14234     {
14235         this.fileCollection.each(function(card) {
14236             if (card.el.dom && card.el.dom.parentNode) {
14237                 card.el.dom.parentNode.removeChild(card.el.dom);
14238             }
14239         });
14240         this.fileCollection.clear();
14241         this.updateInput();
14242     },
14243     
14244     updateInput : function()
14245     {
14246          var data = [];
14247         this.fileCollection.each(function(e) {
14248             data.push(e.data);
14249             
14250         });
14251         this.inputEl().dom.value = JSON.stringify(data);
14252         
14253         
14254         
14255     }
14256     
14257     
14258 });
14259
14260
14261 Roo.bootstrap.CardUploader.ID = -1;/*
14262  * Based on:
14263  * Ext JS Library 1.1.1
14264  * Copyright(c) 2006-2007, Ext JS, LLC.
14265  *
14266  * Originally Released Under LGPL - original licence link has changed is not relivant.
14267  *
14268  * Fork - LGPL
14269  * <script type="text/javascript">
14270  */
14271
14272
14273 /**
14274  * @class Roo.data.SortTypes
14275  * @singleton
14276  * Defines the default sorting (casting?) comparison functions used when sorting data.
14277  */
14278 Roo.data.SortTypes = {
14279     /**
14280      * Default sort that does nothing
14281      * @param {Mixed} s The value being converted
14282      * @return {Mixed} The comparison value
14283      */
14284     none : function(s){
14285         return s;
14286     },
14287     
14288     /**
14289      * The regular expression used to strip tags
14290      * @type {RegExp}
14291      * @property
14292      */
14293     stripTagsRE : /<\/?[^>]+>/gi,
14294     
14295     /**
14296      * Strips all HTML tags to sort on text only
14297      * @param {Mixed} s The value being converted
14298      * @return {String} The comparison value
14299      */
14300     asText : function(s){
14301         return String(s).replace(this.stripTagsRE, "");
14302     },
14303     
14304     /**
14305      * Strips all HTML tags to sort on text only - Case insensitive
14306      * @param {Mixed} s The value being converted
14307      * @return {String} The comparison value
14308      */
14309     asUCText : function(s){
14310         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14311     },
14312     
14313     /**
14314      * Case insensitive string
14315      * @param {Mixed} s The value being converted
14316      * @return {String} The comparison value
14317      */
14318     asUCString : function(s) {
14319         return String(s).toUpperCase();
14320     },
14321     
14322     /**
14323      * Date sorting
14324      * @param {Mixed} s The value being converted
14325      * @return {Number} The comparison value
14326      */
14327     asDate : function(s) {
14328         if(!s){
14329             return 0;
14330         }
14331         if(s instanceof Date){
14332             return s.getTime();
14333         }
14334         return Date.parse(String(s));
14335     },
14336     
14337     /**
14338      * Float sorting
14339      * @param {Mixed} s The value being converted
14340      * @return {Float} The comparison value
14341      */
14342     asFloat : function(s) {
14343         var val = parseFloat(String(s).replace(/,/g, ""));
14344         if(isNaN(val)) {
14345             val = 0;
14346         }
14347         return val;
14348     },
14349     
14350     /**
14351      * Integer sorting
14352      * @param {Mixed} s The value being converted
14353      * @return {Number} The comparison value
14354      */
14355     asInt : function(s) {
14356         var val = parseInt(String(s).replace(/,/g, ""));
14357         if(isNaN(val)) {
14358             val = 0;
14359         }
14360         return val;
14361     }
14362 };/*
14363  * Based on:
14364  * Ext JS Library 1.1.1
14365  * Copyright(c) 2006-2007, Ext JS, LLC.
14366  *
14367  * Originally Released Under LGPL - original licence link has changed is not relivant.
14368  *
14369  * Fork - LGPL
14370  * <script type="text/javascript">
14371  */
14372
14373 /**
14374 * @class Roo.data.Record
14375  * Instances of this class encapsulate both record <em>definition</em> information, and record
14376  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14377  * to access Records cached in an {@link Roo.data.Store} object.<br>
14378  * <p>
14379  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14380  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14381  * objects.<br>
14382  * <p>
14383  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14384  * @constructor
14385  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14386  * {@link #create}. The parameters are the same.
14387  * @param {Array} data An associative Array of data values keyed by the field name.
14388  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14389  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14390  * not specified an integer id is generated.
14391  */
14392 Roo.data.Record = function(data, id){
14393     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14394     this.data = data;
14395 };
14396
14397 /**
14398  * Generate a constructor for a specific record layout.
14399  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14400  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14401  * Each field definition object may contain the following properties: <ul>
14402  * <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,
14403  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14404  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14405  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14406  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14407  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14408  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14409  * this may be omitted.</p></li>
14410  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14411  * <ul><li>auto (Default, implies no conversion)</li>
14412  * <li>string</li>
14413  * <li>int</li>
14414  * <li>float</li>
14415  * <li>boolean</li>
14416  * <li>date</li></ul></p></li>
14417  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14418  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14419  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14420  * by the Reader into an object that will be stored in the Record. It is passed the
14421  * following parameters:<ul>
14422  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14423  * </ul></p></li>
14424  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14425  * </ul>
14426  * <br>usage:<br><pre><code>
14427 var TopicRecord = Roo.data.Record.create(
14428     {name: 'title', mapping: 'topic_title'},
14429     {name: 'author', mapping: 'username'},
14430     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14431     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14432     {name: 'lastPoster', mapping: 'user2'},
14433     {name: 'excerpt', mapping: 'post_text'}
14434 );
14435
14436 var myNewRecord = new TopicRecord({
14437     title: 'Do my job please',
14438     author: 'noobie',
14439     totalPosts: 1,
14440     lastPost: new Date(),
14441     lastPoster: 'Animal',
14442     excerpt: 'No way dude!'
14443 });
14444 myStore.add(myNewRecord);
14445 </code></pre>
14446  * @method create
14447  * @static
14448  */
14449 Roo.data.Record.create = function(o){
14450     var f = function(){
14451         f.superclass.constructor.apply(this, arguments);
14452     };
14453     Roo.extend(f, Roo.data.Record);
14454     var p = f.prototype;
14455     p.fields = new Roo.util.MixedCollection(false, function(field){
14456         return field.name;
14457     });
14458     for(var i = 0, len = o.length; i < len; i++){
14459         p.fields.add(new Roo.data.Field(o[i]));
14460     }
14461     f.getField = function(name){
14462         return p.fields.get(name);  
14463     };
14464     return f;
14465 };
14466
14467 Roo.data.Record.AUTO_ID = 1000;
14468 Roo.data.Record.EDIT = 'edit';
14469 Roo.data.Record.REJECT = 'reject';
14470 Roo.data.Record.COMMIT = 'commit';
14471
14472 Roo.data.Record.prototype = {
14473     /**
14474      * Readonly flag - true if this record has been modified.
14475      * @type Boolean
14476      */
14477     dirty : false,
14478     editing : false,
14479     error: null,
14480     modified: null,
14481
14482     // private
14483     join : function(store){
14484         this.store = store;
14485     },
14486
14487     /**
14488      * Set the named field to the specified value.
14489      * @param {String} name The name of the field to set.
14490      * @param {Object} value The value to set the field to.
14491      */
14492     set : function(name, value){
14493         if(this.data[name] == value){
14494             return;
14495         }
14496         this.dirty = true;
14497         if(!this.modified){
14498             this.modified = {};
14499         }
14500         if(typeof this.modified[name] == 'undefined'){
14501             this.modified[name] = this.data[name];
14502         }
14503         this.data[name] = value;
14504         if(!this.editing && this.store){
14505             this.store.afterEdit(this);
14506         }       
14507     },
14508
14509     /**
14510      * Get the value of the named field.
14511      * @param {String} name The name of the field to get the value of.
14512      * @return {Object} The value of the field.
14513      */
14514     get : function(name){
14515         return this.data[name]; 
14516     },
14517
14518     // private
14519     beginEdit : function(){
14520         this.editing = true;
14521         this.modified = {}; 
14522     },
14523
14524     // private
14525     cancelEdit : function(){
14526         this.editing = false;
14527         delete this.modified;
14528     },
14529
14530     // private
14531     endEdit : function(){
14532         this.editing = false;
14533         if(this.dirty && this.store){
14534             this.store.afterEdit(this);
14535         }
14536     },
14537
14538     /**
14539      * Usually called by the {@link Roo.data.Store} which owns the Record.
14540      * Rejects all changes made to the Record since either creation, or the last commit operation.
14541      * Modified fields are reverted to their original values.
14542      * <p>
14543      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14544      * of reject operations.
14545      */
14546     reject : function(){
14547         var m = this.modified;
14548         for(var n in m){
14549             if(typeof m[n] != "function"){
14550                 this.data[n] = m[n];
14551             }
14552         }
14553         this.dirty = false;
14554         delete this.modified;
14555         this.editing = false;
14556         if(this.store){
14557             this.store.afterReject(this);
14558         }
14559     },
14560
14561     /**
14562      * Usually called by the {@link Roo.data.Store} which owns the Record.
14563      * Commits all changes made to the Record since either creation, or the last commit operation.
14564      * <p>
14565      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14566      * of commit operations.
14567      */
14568     commit : function(){
14569         this.dirty = false;
14570         delete this.modified;
14571         this.editing = false;
14572         if(this.store){
14573             this.store.afterCommit(this);
14574         }
14575     },
14576
14577     // private
14578     hasError : function(){
14579         return this.error != null;
14580     },
14581
14582     // private
14583     clearError : function(){
14584         this.error = null;
14585     },
14586
14587     /**
14588      * Creates a copy of this record.
14589      * @param {String} id (optional) A new record id if you don't want to use this record's id
14590      * @return {Record}
14591      */
14592     copy : function(newId) {
14593         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14594     }
14595 };/*
14596  * Based on:
14597  * Ext JS Library 1.1.1
14598  * Copyright(c) 2006-2007, Ext JS, LLC.
14599  *
14600  * Originally Released Under LGPL - original licence link has changed is not relivant.
14601  *
14602  * Fork - LGPL
14603  * <script type="text/javascript">
14604  */
14605
14606
14607
14608 /**
14609  * @class Roo.data.Store
14610  * @extends Roo.util.Observable
14611  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14612  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14613  * <p>
14614  * 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
14615  * has no knowledge of the format of the data returned by the Proxy.<br>
14616  * <p>
14617  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14618  * instances from the data object. These records are cached and made available through accessor functions.
14619  * @constructor
14620  * Creates a new Store.
14621  * @param {Object} config A config object containing the objects needed for the Store to access data,
14622  * and read the data into Records.
14623  */
14624 Roo.data.Store = function(config){
14625     this.data = new Roo.util.MixedCollection(false);
14626     this.data.getKey = function(o){
14627         return o.id;
14628     };
14629     this.baseParams = {};
14630     // private
14631     this.paramNames = {
14632         "start" : "start",
14633         "limit" : "limit",
14634         "sort" : "sort",
14635         "dir" : "dir",
14636         "multisort" : "_multisort"
14637     };
14638
14639     if(config && config.data){
14640         this.inlineData = config.data;
14641         delete config.data;
14642     }
14643
14644     Roo.apply(this, config);
14645     
14646     if(this.reader){ // reader passed
14647         this.reader = Roo.factory(this.reader, Roo.data);
14648         this.reader.xmodule = this.xmodule || false;
14649         if(!this.recordType){
14650             this.recordType = this.reader.recordType;
14651         }
14652         if(this.reader.onMetaChange){
14653             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14654         }
14655     }
14656
14657     if(this.recordType){
14658         this.fields = this.recordType.prototype.fields;
14659     }
14660     this.modified = [];
14661
14662     this.addEvents({
14663         /**
14664          * @event datachanged
14665          * Fires when the data cache has changed, and a widget which is using this Store
14666          * as a Record cache should refresh its view.
14667          * @param {Store} this
14668          */
14669         datachanged : true,
14670         /**
14671          * @event metachange
14672          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14673          * @param {Store} this
14674          * @param {Object} meta The JSON metadata
14675          */
14676         metachange : true,
14677         /**
14678          * @event add
14679          * Fires when Records have been added to the Store
14680          * @param {Store} this
14681          * @param {Roo.data.Record[]} records The array of Records added
14682          * @param {Number} index The index at which the record(s) were added
14683          */
14684         add : true,
14685         /**
14686          * @event remove
14687          * Fires when a Record has been removed from the Store
14688          * @param {Store} this
14689          * @param {Roo.data.Record} record The Record that was removed
14690          * @param {Number} index The index at which the record was removed
14691          */
14692         remove : true,
14693         /**
14694          * @event update
14695          * Fires when a Record has been updated
14696          * @param {Store} this
14697          * @param {Roo.data.Record} record The Record that was updated
14698          * @param {String} operation The update operation being performed.  Value may be one of:
14699          * <pre><code>
14700  Roo.data.Record.EDIT
14701  Roo.data.Record.REJECT
14702  Roo.data.Record.COMMIT
14703          * </code></pre>
14704          */
14705         update : true,
14706         /**
14707          * @event clear
14708          * Fires when the data cache has been cleared.
14709          * @param {Store} this
14710          */
14711         clear : true,
14712         /**
14713          * @event beforeload
14714          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14715          * the load action will be canceled.
14716          * @param {Store} this
14717          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14718          */
14719         beforeload : true,
14720         /**
14721          * @event beforeloadadd
14722          * Fires after a new set of Records has been loaded.
14723          * @param {Store} this
14724          * @param {Roo.data.Record[]} records The Records that were loaded
14725          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14726          */
14727         beforeloadadd : true,
14728         /**
14729          * @event load
14730          * Fires after a new set of Records has been loaded, before they are added to the store.
14731          * @param {Store} this
14732          * @param {Roo.data.Record[]} records The Records that were loaded
14733          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14734          * @params {Object} return from reader
14735          */
14736         load : true,
14737         /**
14738          * @event loadexception
14739          * Fires if an exception occurs in the Proxy during loading.
14740          * Called with the signature of the Proxy's "loadexception" event.
14741          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14742          * 
14743          * @param {Proxy} 
14744          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14745          * @param {Object} load options 
14746          * @param {Object} jsonData from your request (normally this contains the Exception)
14747          */
14748         loadexception : true
14749     });
14750     
14751     if(this.proxy){
14752         this.proxy = Roo.factory(this.proxy, Roo.data);
14753         this.proxy.xmodule = this.xmodule || false;
14754         this.relayEvents(this.proxy,  ["loadexception"]);
14755     }
14756     this.sortToggle = {};
14757     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14758
14759     Roo.data.Store.superclass.constructor.call(this);
14760
14761     if(this.inlineData){
14762         this.loadData(this.inlineData);
14763         delete this.inlineData;
14764     }
14765 };
14766
14767 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14768      /**
14769     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14770     * without a remote query - used by combo/forms at present.
14771     */
14772     
14773     /**
14774     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14775     */
14776     /**
14777     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14778     */
14779     /**
14780     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14781     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14782     */
14783     /**
14784     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14785     * on any HTTP request
14786     */
14787     /**
14788     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14789     */
14790     /**
14791     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14792     */
14793     multiSort: false,
14794     /**
14795     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14796     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14797     */
14798     remoteSort : false,
14799
14800     /**
14801     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14802      * loaded or when a record is removed. (defaults to false).
14803     */
14804     pruneModifiedRecords : false,
14805
14806     // private
14807     lastOptions : null,
14808
14809     /**
14810      * Add Records to the Store and fires the add event.
14811      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14812      */
14813     add : function(records){
14814         records = [].concat(records);
14815         for(var i = 0, len = records.length; i < len; i++){
14816             records[i].join(this);
14817         }
14818         var index = this.data.length;
14819         this.data.addAll(records);
14820         this.fireEvent("add", this, records, index);
14821     },
14822
14823     /**
14824      * Remove a Record from the Store and fires the remove event.
14825      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14826      */
14827     remove : function(record){
14828         var index = this.data.indexOf(record);
14829         this.data.removeAt(index);
14830  
14831         if(this.pruneModifiedRecords){
14832             this.modified.remove(record);
14833         }
14834         this.fireEvent("remove", this, record, index);
14835     },
14836
14837     /**
14838      * Remove all Records from the Store and fires the clear event.
14839      */
14840     removeAll : function(){
14841         this.data.clear();
14842         if(this.pruneModifiedRecords){
14843             this.modified = [];
14844         }
14845         this.fireEvent("clear", this);
14846     },
14847
14848     /**
14849      * Inserts Records to the Store at the given index and fires the add event.
14850      * @param {Number} index The start index at which to insert the passed Records.
14851      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14852      */
14853     insert : function(index, records){
14854         records = [].concat(records);
14855         for(var i = 0, len = records.length; i < len; i++){
14856             this.data.insert(index, records[i]);
14857             records[i].join(this);
14858         }
14859         this.fireEvent("add", this, records, index);
14860     },
14861
14862     /**
14863      * Get the index within the cache of the passed Record.
14864      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14865      * @return {Number} The index of the passed Record. Returns -1 if not found.
14866      */
14867     indexOf : function(record){
14868         return this.data.indexOf(record);
14869     },
14870
14871     /**
14872      * Get the index within the cache of the Record with the passed id.
14873      * @param {String} id The id of the Record to find.
14874      * @return {Number} The index of the Record. Returns -1 if not found.
14875      */
14876     indexOfId : function(id){
14877         return this.data.indexOfKey(id);
14878     },
14879
14880     /**
14881      * Get the Record with the specified id.
14882      * @param {String} id The id of the Record to find.
14883      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14884      */
14885     getById : function(id){
14886         return this.data.key(id);
14887     },
14888
14889     /**
14890      * Get the Record at the specified index.
14891      * @param {Number} index The index of the Record to find.
14892      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14893      */
14894     getAt : function(index){
14895         return this.data.itemAt(index);
14896     },
14897
14898     /**
14899      * Returns a range of Records between specified indices.
14900      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14901      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14902      * @return {Roo.data.Record[]} An array of Records
14903      */
14904     getRange : function(start, end){
14905         return this.data.getRange(start, end);
14906     },
14907
14908     // private
14909     storeOptions : function(o){
14910         o = Roo.apply({}, o);
14911         delete o.callback;
14912         delete o.scope;
14913         this.lastOptions = o;
14914     },
14915
14916     /**
14917      * Loads the Record cache from the configured Proxy using the configured Reader.
14918      * <p>
14919      * If using remote paging, then the first load call must specify the <em>start</em>
14920      * and <em>limit</em> properties in the options.params property to establish the initial
14921      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14922      * <p>
14923      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14924      * and this call will return before the new data has been loaded. Perform any post-processing
14925      * in a callback function, or in a "load" event handler.</strong>
14926      * <p>
14927      * @param {Object} options An object containing properties which control loading options:<ul>
14928      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14929      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14930      * passed the following arguments:<ul>
14931      * <li>r : Roo.data.Record[]</li>
14932      * <li>options: Options object from the load call</li>
14933      * <li>success: Boolean success indicator</li></ul></li>
14934      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14935      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14936      * </ul>
14937      */
14938     load : function(options){
14939         options = options || {};
14940         if(this.fireEvent("beforeload", this, options) !== false){
14941             this.storeOptions(options);
14942             var p = Roo.apply(options.params || {}, this.baseParams);
14943             // if meta was not loaded from remote source.. try requesting it.
14944             if (!this.reader.metaFromRemote) {
14945                 p._requestMeta = 1;
14946             }
14947             if(this.sortInfo && this.remoteSort){
14948                 var pn = this.paramNames;
14949                 p[pn["sort"]] = this.sortInfo.field;
14950                 p[pn["dir"]] = this.sortInfo.direction;
14951             }
14952             if (this.multiSort) {
14953                 var pn = this.paramNames;
14954                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14955             }
14956             
14957             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14958         }
14959     },
14960
14961     /**
14962      * Reloads the Record cache from the configured Proxy using the configured Reader and
14963      * the options from the last load operation performed.
14964      * @param {Object} options (optional) An object containing properties which may override the options
14965      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14966      * the most recently used options are reused).
14967      */
14968     reload : function(options){
14969         this.load(Roo.applyIf(options||{}, this.lastOptions));
14970     },
14971
14972     // private
14973     // Called as a callback by the Reader during a load operation.
14974     loadRecords : function(o, options, success){
14975         if(!o || success === false){
14976             if(success !== false){
14977                 this.fireEvent("load", this, [], options, o);
14978             }
14979             if(options.callback){
14980                 options.callback.call(options.scope || this, [], options, false);
14981             }
14982             return;
14983         }
14984         // if data returned failure - throw an exception.
14985         if (o.success === false) {
14986             // show a message if no listener is registered.
14987             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14988                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14989             }
14990             // loadmask wil be hooked into this..
14991             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14992             return;
14993         }
14994         var r = o.records, t = o.totalRecords || r.length;
14995         
14996         this.fireEvent("beforeloadadd", this, r, options, o);
14997         
14998         if(!options || options.add !== true){
14999             if(this.pruneModifiedRecords){
15000                 this.modified = [];
15001             }
15002             for(var i = 0, len = r.length; i < len; i++){
15003                 r[i].join(this);
15004             }
15005             if(this.snapshot){
15006                 this.data = this.snapshot;
15007                 delete this.snapshot;
15008             }
15009             this.data.clear();
15010             this.data.addAll(r);
15011             this.totalLength = t;
15012             this.applySort();
15013             this.fireEvent("datachanged", this);
15014         }else{
15015             this.totalLength = Math.max(t, this.data.length+r.length);
15016             this.add(r);
15017         }
15018         
15019         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15020                 
15021             var e = new Roo.data.Record({});
15022
15023             e.set(this.parent.displayField, this.parent.emptyTitle);
15024             e.set(this.parent.valueField, '');
15025
15026             this.insert(0, e);
15027         }
15028             
15029         this.fireEvent("load", this, r, options, o);
15030         if(options.callback){
15031             options.callback.call(options.scope || this, r, options, true);
15032         }
15033     },
15034
15035
15036     /**
15037      * Loads data from a passed data block. A Reader which understands the format of the data
15038      * must have been configured in the constructor.
15039      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15040      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15041      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15042      */
15043     loadData : function(o, append){
15044         var r = this.reader.readRecords(o);
15045         this.loadRecords(r, {add: append}, true);
15046     },
15047     
15048      /**
15049      * using 'cn' the nested child reader read the child array into it's child stores.
15050      * @param {Object} rec The record with a 'children array
15051      */
15052     loadDataFromChildren : function(rec)
15053     {
15054         this.loadData(this.reader.toLoadData(rec));
15055     },
15056     
15057
15058     /**
15059      * Gets the number of cached records.
15060      * <p>
15061      * <em>If using paging, this may not be the total size of the dataset. If the data object
15062      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15063      * the data set size</em>
15064      */
15065     getCount : function(){
15066         return this.data.length || 0;
15067     },
15068
15069     /**
15070      * Gets the total number of records in the dataset as returned by the server.
15071      * <p>
15072      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15073      * the dataset size</em>
15074      */
15075     getTotalCount : function(){
15076         return this.totalLength || 0;
15077     },
15078
15079     /**
15080      * Returns the sort state of the Store as an object with two properties:
15081      * <pre><code>
15082  field {String} The name of the field by which the Records are sorted
15083  direction {String} The sort order, "ASC" or "DESC"
15084      * </code></pre>
15085      */
15086     getSortState : function(){
15087         return this.sortInfo;
15088     },
15089
15090     // private
15091     applySort : function(){
15092         if(this.sortInfo && !this.remoteSort){
15093             var s = this.sortInfo, f = s.field;
15094             var st = this.fields.get(f).sortType;
15095             var fn = function(r1, r2){
15096                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15097                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15098             };
15099             this.data.sort(s.direction, fn);
15100             if(this.snapshot && this.snapshot != this.data){
15101                 this.snapshot.sort(s.direction, fn);
15102             }
15103         }
15104     },
15105
15106     /**
15107      * Sets the default sort column and order to be used by the next load operation.
15108      * @param {String} fieldName The name of the field to sort by.
15109      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15110      */
15111     setDefaultSort : function(field, dir){
15112         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15113     },
15114
15115     /**
15116      * Sort the Records.
15117      * If remote sorting is used, the sort is performed on the server, and the cache is
15118      * reloaded. If local sorting is used, the cache is sorted internally.
15119      * @param {String} fieldName The name of the field to sort by.
15120      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15121      */
15122     sort : function(fieldName, dir){
15123         var f = this.fields.get(fieldName);
15124         if(!dir){
15125             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15126             
15127             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15128                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15129             }else{
15130                 dir = f.sortDir;
15131             }
15132         }
15133         this.sortToggle[f.name] = dir;
15134         this.sortInfo = {field: f.name, direction: dir};
15135         if(!this.remoteSort){
15136             this.applySort();
15137             this.fireEvent("datachanged", this);
15138         }else{
15139             this.load(this.lastOptions);
15140         }
15141     },
15142
15143     /**
15144      * Calls the specified function for each of the Records in the cache.
15145      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15146      * Returning <em>false</em> aborts and exits the iteration.
15147      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15148      */
15149     each : function(fn, scope){
15150         this.data.each(fn, scope);
15151     },
15152
15153     /**
15154      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15155      * (e.g., during paging).
15156      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15157      */
15158     getModifiedRecords : function(){
15159         return this.modified;
15160     },
15161
15162     // private
15163     createFilterFn : function(property, value, anyMatch){
15164         if(!value.exec){ // not a regex
15165             value = String(value);
15166             if(value.length == 0){
15167                 return false;
15168             }
15169             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15170         }
15171         return function(r){
15172             return value.test(r.data[property]);
15173         };
15174     },
15175
15176     /**
15177      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15178      * @param {String} property A field on your records
15179      * @param {Number} start The record index to start at (defaults to 0)
15180      * @param {Number} end The last record index to include (defaults to length - 1)
15181      * @return {Number} The sum
15182      */
15183     sum : function(property, start, end){
15184         var rs = this.data.items, v = 0;
15185         start = start || 0;
15186         end = (end || end === 0) ? end : rs.length-1;
15187
15188         for(var i = start; i <= end; i++){
15189             v += (rs[i].data[property] || 0);
15190         }
15191         return v;
15192     },
15193
15194     /**
15195      * Filter the records by a specified property.
15196      * @param {String} field A field on your records
15197      * @param {String/RegExp} value Either a string that the field
15198      * should start with or a RegExp to test against the field
15199      * @param {Boolean} anyMatch True to match any part not just the beginning
15200      */
15201     filter : function(property, value, anyMatch){
15202         var fn = this.createFilterFn(property, value, anyMatch);
15203         return fn ? this.filterBy(fn) : this.clearFilter();
15204     },
15205
15206     /**
15207      * Filter by a function. The specified function will be called with each
15208      * record in this data source. If the function returns true the record is included,
15209      * otherwise it is filtered.
15210      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15211      * @param {Object} scope (optional) The scope of the function (defaults to this)
15212      */
15213     filterBy : function(fn, scope){
15214         this.snapshot = this.snapshot || this.data;
15215         this.data = this.queryBy(fn, scope||this);
15216         this.fireEvent("datachanged", this);
15217     },
15218
15219     /**
15220      * Query the records by a specified property.
15221      * @param {String} field A field on your records
15222      * @param {String/RegExp} value Either a string that the field
15223      * should start with or a RegExp to test against the field
15224      * @param {Boolean} anyMatch True to match any part not just the beginning
15225      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15226      */
15227     query : function(property, value, anyMatch){
15228         var fn = this.createFilterFn(property, value, anyMatch);
15229         return fn ? this.queryBy(fn) : this.data.clone();
15230     },
15231
15232     /**
15233      * Query by a function. The specified function will be called with each
15234      * record in this data source. If the function returns true the record is included
15235      * in the results.
15236      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15237      * @param {Object} scope (optional) The scope of the function (defaults to this)
15238       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15239      **/
15240     queryBy : function(fn, scope){
15241         var data = this.snapshot || this.data;
15242         return data.filterBy(fn, scope||this);
15243     },
15244
15245     /**
15246      * Collects unique values for a particular dataIndex from this store.
15247      * @param {String} dataIndex The property to collect
15248      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15249      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15250      * @return {Array} An array of the unique values
15251      **/
15252     collect : function(dataIndex, allowNull, bypassFilter){
15253         var d = (bypassFilter === true && this.snapshot) ?
15254                 this.snapshot.items : this.data.items;
15255         var v, sv, r = [], l = {};
15256         for(var i = 0, len = d.length; i < len; i++){
15257             v = d[i].data[dataIndex];
15258             sv = String(v);
15259             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15260                 l[sv] = true;
15261                 r[r.length] = v;
15262             }
15263         }
15264         return r;
15265     },
15266
15267     /**
15268      * Revert to a view of the Record cache with no filtering applied.
15269      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15270      */
15271     clearFilter : function(suppressEvent){
15272         if(this.snapshot && this.snapshot != this.data){
15273             this.data = this.snapshot;
15274             delete this.snapshot;
15275             if(suppressEvent !== true){
15276                 this.fireEvent("datachanged", this);
15277             }
15278         }
15279     },
15280
15281     // private
15282     afterEdit : function(record){
15283         if(this.modified.indexOf(record) == -1){
15284             this.modified.push(record);
15285         }
15286         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15287     },
15288     
15289     // private
15290     afterReject : function(record){
15291         this.modified.remove(record);
15292         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15293     },
15294
15295     // private
15296     afterCommit : function(record){
15297         this.modified.remove(record);
15298         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15299     },
15300
15301     /**
15302      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15303      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15304      */
15305     commitChanges : function(){
15306         var m = this.modified.slice(0);
15307         this.modified = [];
15308         for(var i = 0, len = m.length; i < len; i++){
15309             m[i].commit();
15310         }
15311     },
15312
15313     /**
15314      * Cancel outstanding changes on all changed records.
15315      */
15316     rejectChanges : function(){
15317         var m = this.modified.slice(0);
15318         this.modified = [];
15319         for(var i = 0, len = m.length; i < len; i++){
15320             m[i].reject();
15321         }
15322     },
15323
15324     onMetaChange : function(meta, rtype, o){
15325         this.recordType = rtype;
15326         this.fields = rtype.prototype.fields;
15327         delete this.snapshot;
15328         this.sortInfo = meta.sortInfo || this.sortInfo;
15329         this.modified = [];
15330         this.fireEvent('metachange', this, this.reader.meta);
15331     },
15332     
15333     moveIndex : function(data, type)
15334     {
15335         var index = this.indexOf(data);
15336         
15337         var newIndex = index + type;
15338         
15339         this.remove(data);
15340         
15341         this.insert(newIndex, data);
15342         
15343     }
15344 });/*
15345  * Based on:
15346  * Ext JS Library 1.1.1
15347  * Copyright(c) 2006-2007, Ext JS, LLC.
15348  *
15349  * Originally Released Under LGPL - original licence link has changed is not relivant.
15350  *
15351  * Fork - LGPL
15352  * <script type="text/javascript">
15353  */
15354
15355 /**
15356  * @class Roo.data.SimpleStore
15357  * @extends Roo.data.Store
15358  * Small helper class to make creating Stores from Array data easier.
15359  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15360  * @cfg {Array} fields An array of field definition objects, or field name strings.
15361  * @cfg {Object} an existing reader (eg. copied from another store)
15362  * @cfg {Array} data The multi-dimensional array of data
15363  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15364  * @cfg {Roo.data.Reader} reader  [not-required] 
15365  * @constructor
15366  * @param {Object} config
15367  */
15368 Roo.data.SimpleStore = function(config)
15369 {
15370     Roo.data.SimpleStore.superclass.constructor.call(this, {
15371         isLocal : true,
15372         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15373                 id: config.id
15374             },
15375             Roo.data.Record.create(config.fields)
15376         ),
15377         proxy : new Roo.data.MemoryProxy(config.data)
15378     });
15379     this.load();
15380 };
15381 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15382  * Based on:
15383  * Ext JS Library 1.1.1
15384  * Copyright(c) 2006-2007, Ext JS, LLC.
15385  *
15386  * Originally Released Under LGPL - original licence link has changed is not relivant.
15387  *
15388  * Fork - LGPL
15389  * <script type="text/javascript">
15390  */
15391
15392 /**
15393 /**
15394  * @extends Roo.data.Store
15395  * @class Roo.data.JsonStore
15396  * Small helper class to make creating Stores for JSON data easier. <br/>
15397 <pre><code>
15398 var store = new Roo.data.JsonStore({
15399     url: 'get-images.php',
15400     root: 'images',
15401     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15402 });
15403 </code></pre>
15404  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15405  * JsonReader and HttpProxy (unless inline data is provided).</b>
15406  * @cfg {Array} fields An array of field definition objects, or field name strings.
15407  * @constructor
15408  * @param {Object} config
15409  */
15410 Roo.data.JsonStore = function(c){
15411     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15412         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15413         reader: new Roo.data.JsonReader(c, c.fields)
15414     }));
15415 };
15416 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15417  * Based on:
15418  * Ext JS Library 1.1.1
15419  * Copyright(c) 2006-2007, Ext JS, LLC.
15420  *
15421  * Originally Released Under LGPL - original licence link has changed is not relivant.
15422  *
15423  * Fork - LGPL
15424  * <script type="text/javascript">
15425  */
15426
15427  
15428 Roo.data.Field = function(config){
15429     if(typeof config == "string"){
15430         config = {name: config};
15431     }
15432     Roo.apply(this, config);
15433     
15434     if(!this.type){
15435         this.type = "auto";
15436     }
15437     
15438     var st = Roo.data.SortTypes;
15439     // named sortTypes are supported, here we look them up
15440     if(typeof this.sortType == "string"){
15441         this.sortType = st[this.sortType];
15442     }
15443     
15444     // set default sortType for strings and dates
15445     if(!this.sortType){
15446         switch(this.type){
15447             case "string":
15448                 this.sortType = st.asUCString;
15449                 break;
15450             case "date":
15451                 this.sortType = st.asDate;
15452                 break;
15453             default:
15454                 this.sortType = st.none;
15455         }
15456     }
15457
15458     // define once
15459     var stripRe = /[\$,%]/g;
15460
15461     // prebuilt conversion function for this field, instead of
15462     // switching every time we're reading a value
15463     if(!this.convert){
15464         var cv, dateFormat = this.dateFormat;
15465         switch(this.type){
15466             case "":
15467             case "auto":
15468             case undefined:
15469                 cv = function(v){ return v; };
15470                 break;
15471             case "string":
15472                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15473                 break;
15474             case "int":
15475                 cv = function(v){
15476                     return v !== undefined && v !== null && v !== '' ?
15477                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15478                     };
15479                 break;
15480             case "float":
15481                 cv = function(v){
15482                     return v !== undefined && v !== null && v !== '' ?
15483                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15484                     };
15485                 break;
15486             case "bool":
15487             case "boolean":
15488                 cv = function(v){ return v === true || v === "true" || v == 1; };
15489                 break;
15490             case "date":
15491                 cv = function(v){
15492                     if(!v){
15493                         return '';
15494                     }
15495                     if(v instanceof Date){
15496                         return v;
15497                     }
15498                     if(dateFormat){
15499                         if(dateFormat == "timestamp"){
15500                             return new Date(v*1000);
15501                         }
15502                         return Date.parseDate(v, dateFormat);
15503                     }
15504                     var parsed = Date.parse(v);
15505                     return parsed ? new Date(parsed) : null;
15506                 };
15507              break;
15508             
15509         }
15510         this.convert = cv;
15511     }
15512 };
15513
15514 Roo.data.Field.prototype = {
15515     dateFormat: null,
15516     defaultValue: "",
15517     mapping: null,
15518     sortType : null,
15519     sortDir : "ASC"
15520 };/*
15521  * Based on:
15522  * Ext JS Library 1.1.1
15523  * Copyright(c) 2006-2007, Ext JS, LLC.
15524  *
15525  * Originally Released Under LGPL - original licence link has changed is not relivant.
15526  *
15527  * Fork - LGPL
15528  * <script type="text/javascript">
15529  */
15530  
15531 // Base class for reading structured data from a data source.  This class is intended to be
15532 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15533
15534 /**
15535  * @class Roo.data.DataReader
15536  * @abstract
15537  * Base class for reading structured data from a data source.  This class is intended to be
15538  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15539  */
15540
15541 Roo.data.DataReader = function(meta, recordType){
15542     
15543     this.meta = meta;
15544     
15545     this.recordType = recordType instanceof Array ? 
15546         Roo.data.Record.create(recordType) : recordType;
15547 };
15548
15549 Roo.data.DataReader.prototype = {
15550     
15551     
15552     readerType : 'Data',
15553      /**
15554      * Create an empty record
15555      * @param {Object} data (optional) - overlay some values
15556      * @return {Roo.data.Record} record created.
15557      */
15558     newRow :  function(d) {
15559         var da =  {};
15560         this.recordType.prototype.fields.each(function(c) {
15561             switch( c.type) {
15562                 case 'int' : da[c.name] = 0; break;
15563                 case 'date' : da[c.name] = new Date(); break;
15564                 case 'float' : da[c.name] = 0.0; break;
15565                 case 'boolean' : da[c.name] = false; break;
15566                 default : da[c.name] = ""; break;
15567             }
15568             
15569         });
15570         return new this.recordType(Roo.apply(da, d));
15571     }
15572     
15573     
15574 };/*
15575  * Based on:
15576  * Ext JS Library 1.1.1
15577  * Copyright(c) 2006-2007, Ext JS, LLC.
15578  *
15579  * Originally Released Under LGPL - original licence link has changed is not relivant.
15580  *
15581  * Fork - LGPL
15582  * <script type="text/javascript">
15583  */
15584
15585 /**
15586  * @class Roo.data.DataProxy
15587  * @extends Roo.data.Observable
15588  * @abstract
15589  * This class is an abstract base class for implementations which provide retrieval of
15590  * unformatted data objects.<br>
15591  * <p>
15592  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15593  * (of the appropriate type which knows how to parse the data object) to provide a block of
15594  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15595  * <p>
15596  * Custom implementations must implement the load method as described in
15597  * {@link Roo.data.HttpProxy#load}.
15598  */
15599 Roo.data.DataProxy = function(){
15600     this.addEvents({
15601         /**
15602          * @event beforeload
15603          * Fires before a network request is made to retrieve a data object.
15604          * @param {Object} This DataProxy object.
15605          * @param {Object} params The params parameter to the load function.
15606          */
15607         beforeload : true,
15608         /**
15609          * @event load
15610          * Fires before the load method's callback is called.
15611          * @param {Object} This DataProxy object.
15612          * @param {Object} o The data object.
15613          * @param {Object} arg The callback argument object passed to the load function.
15614          */
15615         load : true,
15616         /**
15617          * @event loadexception
15618          * Fires if an Exception occurs during data retrieval.
15619          * @param {Object} This DataProxy object.
15620          * @param {Object} o The data object.
15621          * @param {Object} arg The callback argument object passed to the load function.
15622          * @param {Object} e The Exception.
15623          */
15624         loadexception : true
15625     });
15626     Roo.data.DataProxy.superclass.constructor.call(this);
15627 };
15628
15629 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15630
15631     /**
15632      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15633      */
15634 /*
15635  * Based on:
15636  * Ext JS Library 1.1.1
15637  * Copyright(c) 2006-2007, Ext JS, LLC.
15638  *
15639  * Originally Released Under LGPL - original licence link has changed is not relivant.
15640  *
15641  * Fork - LGPL
15642  * <script type="text/javascript">
15643  */
15644 /**
15645  * @class Roo.data.MemoryProxy
15646  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15647  * to the Reader when its load method is called.
15648  * @constructor
15649  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15650  */
15651 Roo.data.MemoryProxy = function(data){
15652     if (data.data) {
15653         data = data.data;
15654     }
15655     Roo.data.MemoryProxy.superclass.constructor.call(this);
15656     this.data = data;
15657 };
15658
15659 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15660     
15661     /**
15662      * Load data from the requested source (in this case an in-memory
15663      * data object passed to the constructor), read the data object into
15664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15665      * process that block using the passed callback.
15666      * @param {Object} params This parameter is not used by the MemoryProxy class.
15667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15668      * object into a block of Roo.data.Records.
15669      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15670      * The function must be passed <ul>
15671      * <li>The Record block object</li>
15672      * <li>The "arg" argument from the load function</li>
15673      * <li>A boolean success indicator</li>
15674      * </ul>
15675      * @param {Object} scope The scope in which to call the callback
15676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15677      */
15678     load : function(params, reader, callback, scope, arg){
15679         params = params || {};
15680         var result;
15681         try {
15682             result = reader.readRecords(params.data ? params.data :this.data);
15683         }catch(e){
15684             this.fireEvent("loadexception", this, arg, null, e);
15685             callback.call(scope, null, arg, false);
15686             return;
15687         }
15688         callback.call(scope, result, arg, true);
15689     },
15690     
15691     // private
15692     update : function(params, records){
15693         
15694     }
15695 });/*
15696  * Based on:
15697  * Ext JS Library 1.1.1
15698  * Copyright(c) 2006-2007, Ext JS, LLC.
15699  *
15700  * Originally Released Under LGPL - original licence link has changed is not relivant.
15701  *
15702  * Fork - LGPL
15703  * <script type="text/javascript">
15704  */
15705 /**
15706  * @class Roo.data.HttpProxy
15707  * @extends Roo.data.DataProxy
15708  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15709  * configured to reference a certain URL.<br><br>
15710  * <p>
15711  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15712  * from which the running page was served.<br><br>
15713  * <p>
15714  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15715  * <p>
15716  * Be aware that to enable the browser to parse an XML document, the server must set
15717  * the Content-Type header in the HTTP response to "text/xml".
15718  * @constructor
15719  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15720  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15721  * will be used to make the request.
15722  */
15723 Roo.data.HttpProxy = function(conn){
15724     Roo.data.HttpProxy.superclass.constructor.call(this);
15725     // is conn a conn config or a real conn?
15726     this.conn = conn;
15727     this.useAjax = !conn || !conn.events;
15728   
15729 };
15730
15731 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15732     // thse are take from connection...
15733     
15734     /**
15735      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15736      */
15737     /**
15738      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15739      * extra parameters to each request made by this object. (defaults to undefined)
15740      */
15741     /**
15742      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15743      *  to each request made by this object. (defaults to undefined)
15744      */
15745     /**
15746      * @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)
15747      */
15748     /**
15749      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15750      */
15751      /**
15752      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15753      * @type Boolean
15754      */
15755   
15756
15757     /**
15758      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15759      * @type Boolean
15760      */
15761     /**
15762      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15763      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15764      * a finer-grained basis than the DataProxy events.
15765      */
15766     getConnection : function(){
15767         return this.useAjax ? Roo.Ajax : this.conn;
15768     },
15769
15770     /**
15771      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15772      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15773      * process that block using the passed callback.
15774      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15775      * for the request to the remote server.
15776      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15777      * object into a block of Roo.data.Records.
15778      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15779      * The function must be passed <ul>
15780      * <li>The Record block object</li>
15781      * <li>The "arg" argument from the load function</li>
15782      * <li>A boolean success indicator</li>
15783      * </ul>
15784      * @param {Object} scope The scope in which to call the callback
15785      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15786      */
15787     load : function(params, reader, callback, scope, arg){
15788         if(this.fireEvent("beforeload", this, params) !== false){
15789             var  o = {
15790                 params : params || {},
15791                 request: {
15792                     callback : callback,
15793                     scope : scope,
15794                     arg : arg
15795                 },
15796                 reader: reader,
15797                 callback : this.loadResponse,
15798                 scope: this
15799             };
15800             if(this.useAjax){
15801                 Roo.applyIf(o, this.conn);
15802                 if(this.activeRequest){
15803                     Roo.Ajax.abort(this.activeRequest);
15804                 }
15805                 this.activeRequest = Roo.Ajax.request(o);
15806             }else{
15807                 this.conn.request(o);
15808             }
15809         }else{
15810             callback.call(scope||this, null, arg, false);
15811         }
15812     },
15813
15814     // private
15815     loadResponse : function(o, success, response){
15816         delete this.activeRequest;
15817         if(!success){
15818             this.fireEvent("loadexception", this, o, response);
15819             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15820             return;
15821         }
15822         var result;
15823         try {
15824             result = o.reader.read(response);
15825         }catch(e){
15826             this.fireEvent("loadexception", this, o, response, e);
15827             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15828             return;
15829         }
15830         
15831         this.fireEvent("load", this, o, o.request.arg);
15832         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15833     },
15834
15835     // private
15836     update : function(dataSet){
15837
15838     },
15839
15840     // private
15841     updateResponse : function(dataSet){
15842
15843     }
15844 });/*
15845  * Based on:
15846  * Ext JS Library 1.1.1
15847  * Copyright(c) 2006-2007, Ext JS, LLC.
15848  *
15849  * Originally Released Under LGPL - original licence link has changed is not relivant.
15850  *
15851  * Fork - LGPL
15852  * <script type="text/javascript">
15853  */
15854
15855 /**
15856  * @class Roo.data.ScriptTagProxy
15857  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15858  * other than the originating domain of the running page.<br><br>
15859  * <p>
15860  * <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
15861  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15862  * <p>
15863  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15864  * source code that is used as the source inside a &lt;script> tag.<br><br>
15865  * <p>
15866  * In order for the browser to process the returned data, the server must wrap the data object
15867  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15868  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15869  * depending on whether the callback name was passed:
15870  * <p>
15871  * <pre><code>
15872 boolean scriptTag = false;
15873 String cb = request.getParameter("callback");
15874 if (cb != null) {
15875     scriptTag = true;
15876     response.setContentType("text/javascript");
15877 } else {
15878     response.setContentType("application/x-json");
15879 }
15880 Writer out = response.getWriter();
15881 if (scriptTag) {
15882     out.write(cb + "(");
15883 }
15884 out.print(dataBlock.toJsonString());
15885 if (scriptTag) {
15886     out.write(");");
15887 }
15888 </pre></code>
15889  *
15890  * @constructor
15891  * @param {Object} config A configuration object.
15892  */
15893 Roo.data.ScriptTagProxy = function(config){
15894     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15895     Roo.apply(this, config);
15896     this.head = document.getElementsByTagName("head")[0];
15897 };
15898
15899 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15900
15901 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15902     /**
15903      * @cfg {String} url The URL from which to request the data object.
15904      */
15905     /**
15906      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15907      */
15908     timeout : 30000,
15909     /**
15910      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15911      * the server the name of the callback function set up by the load call to process the returned data object.
15912      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15913      * javascript output which calls this named function passing the data object as its only parameter.
15914      */
15915     callbackParam : "callback",
15916     /**
15917      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15918      * name to the request.
15919      */
15920     nocache : true,
15921
15922     /**
15923      * Load data from the configured URL, read the data object into
15924      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15925      * process that block using the passed callback.
15926      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15927      * for the request to the remote server.
15928      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15929      * object into a block of Roo.data.Records.
15930      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15931      * The function must be passed <ul>
15932      * <li>The Record block object</li>
15933      * <li>The "arg" argument from the load function</li>
15934      * <li>A boolean success indicator</li>
15935      * </ul>
15936      * @param {Object} scope The scope in which to call the callback
15937      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15938      */
15939     load : function(params, reader, callback, scope, arg){
15940         if(this.fireEvent("beforeload", this, params) !== false){
15941
15942             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15943
15944             var url = this.url;
15945             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15946             if(this.nocache){
15947                 url += "&_dc=" + (new Date().getTime());
15948             }
15949             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15950             var trans = {
15951                 id : transId,
15952                 cb : "stcCallback"+transId,
15953                 scriptId : "stcScript"+transId,
15954                 params : params,
15955                 arg : arg,
15956                 url : url,
15957                 callback : callback,
15958                 scope : scope,
15959                 reader : reader
15960             };
15961             var conn = this;
15962
15963             window[trans.cb] = function(o){
15964                 conn.handleResponse(o, trans);
15965             };
15966
15967             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15968
15969             if(this.autoAbort !== false){
15970                 this.abort();
15971             }
15972
15973             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15974
15975             var script = document.createElement("script");
15976             script.setAttribute("src", url);
15977             script.setAttribute("type", "text/javascript");
15978             script.setAttribute("id", trans.scriptId);
15979             this.head.appendChild(script);
15980
15981             this.trans = trans;
15982         }else{
15983             callback.call(scope||this, null, arg, false);
15984         }
15985     },
15986
15987     // private
15988     isLoading : function(){
15989         return this.trans ? true : false;
15990     },
15991
15992     /**
15993      * Abort the current server request.
15994      */
15995     abort : function(){
15996         if(this.isLoading()){
15997             this.destroyTrans(this.trans);
15998         }
15999     },
16000
16001     // private
16002     destroyTrans : function(trans, isLoaded){
16003         this.head.removeChild(document.getElementById(trans.scriptId));
16004         clearTimeout(trans.timeoutId);
16005         if(isLoaded){
16006             window[trans.cb] = undefined;
16007             try{
16008                 delete window[trans.cb];
16009             }catch(e){}
16010         }else{
16011             // if hasn't been loaded, wait for load to remove it to prevent script error
16012             window[trans.cb] = function(){
16013                 window[trans.cb] = undefined;
16014                 try{
16015                     delete window[trans.cb];
16016                 }catch(e){}
16017             };
16018         }
16019     },
16020
16021     // private
16022     handleResponse : function(o, trans){
16023         this.trans = false;
16024         this.destroyTrans(trans, true);
16025         var result;
16026         try {
16027             result = trans.reader.readRecords(o);
16028         }catch(e){
16029             this.fireEvent("loadexception", this, o, trans.arg, e);
16030             trans.callback.call(trans.scope||window, null, trans.arg, false);
16031             return;
16032         }
16033         this.fireEvent("load", this, o, trans.arg);
16034         trans.callback.call(trans.scope||window, result, trans.arg, true);
16035     },
16036
16037     // private
16038     handleFailure : function(trans){
16039         this.trans = false;
16040         this.destroyTrans(trans, false);
16041         this.fireEvent("loadexception", this, null, trans.arg);
16042         trans.callback.call(trans.scope||window, null, trans.arg, false);
16043     }
16044 });/*
16045  * Based on:
16046  * Ext JS Library 1.1.1
16047  * Copyright(c) 2006-2007, Ext JS, LLC.
16048  *
16049  * Originally Released Under LGPL - original licence link has changed is not relivant.
16050  *
16051  * Fork - LGPL
16052  * <script type="text/javascript">
16053  */
16054
16055 /**
16056  * @class Roo.data.JsonReader
16057  * @extends Roo.data.DataReader
16058  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16059  * based on mappings in a provided Roo.data.Record constructor.
16060  * 
16061  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16062  * in the reply previously. 
16063  * 
16064  * <p>
16065  * Example code:
16066  * <pre><code>
16067 var RecordDef = Roo.data.Record.create([
16068     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16069     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16070 ]);
16071 var myReader = new Roo.data.JsonReader({
16072     totalProperty: "results",    // The property which contains the total dataset size (optional)
16073     root: "rows",                // The property which contains an Array of row objects
16074     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16075 }, RecordDef);
16076 </code></pre>
16077  * <p>
16078  * This would consume a JSON file like this:
16079  * <pre><code>
16080 { 'results': 2, 'rows': [
16081     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16082     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16083 }
16084 </code></pre>
16085  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16086  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16087  * paged from the remote server.
16088  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16089  * @cfg {String} root name of the property which contains the Array of row objects.
16090  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16091  * @cfg {Array} fields Array of field definition objects
16092  * @constructor
16093  * Create a new JsonReader
16094  * @param {Object} meta Metadata configuration options
16095  * @param {Object} recordType Either an Array of field definition objects,
16096  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16097  */
16098 Roo.data.JsonReader = function(meta, recordType){
16099     
16100     meta = meta || {};
16101     // set some defaults:
16102     Roo.applyIf(meta, {
16103         totalProperty: 'total',
16104         successProperty : 'success',
16105         root : 'data',
16106         id : 'id'
16107     });
16108     
16109     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16110 };
16111 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16112     
16113     readerType : 'Json',
16114     
16115     /**
16116      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16117      * Used by Store query builder to append _requestMeta to params.
16118      * 
16119      */
16120     metaFromRemote : false,
16121     /**
16122      * This method is only used by a DataProxy which has retrieved data from a remote server.
16123      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16124      * @return {Object} data A data block which is used by an Roo.data.Store object as
16125      * a cache of Roo.data.Records.
16126      */
16127     read : function(response){
16128         var json = response.responseText;
16129        
16130         var o = /* eval:var:o */ eval("("+json+")");
16131         if(!o) {
16132             throw {message: "JsonReader.read: Json object not found"};
16133         }
16134         
16135         if(o.metaData){
16136             
16137             delete this.ef;
16138             this.metaFromRemote = true;
16139             this.meta = o.metaData;
16140             this.recordType = Roo.data.Record.create(o.metaData.fields);
16141             this.onMetaChange(this.meta, this.recordType, o);
16142         }
16143         return this.readRecords(o);
16144     },
16145
16146     // private function a store will implement
16147     onMetaChange : function(meta, recordType, o){
16148
16149     },
16150
16151     /**
16152          * @ignore
16153          */
16154     simpleAccess: function(obj, subsc) {
16155         return obj[subsc];
16156     },
16157
16158         /**
16159          * @ignore
16160          */
16161     getJsonAccessor: function(){
16162         var re = /[\[\.]/;
16163         return function(expr) {
16164             try {
16165                 return(re.test(expr))
16166                     ? new Function("obj", "return obj." + expr)
16167                     : function(obj){
16168                         return obj[expr];
16169                     };
16170             } catch(e){}
16171             return Roo.emptyFn;
16172         };
16173     }(),
16174
16175     /**
16176      * Create a data block containing Roo.data.Records from an XML document.
16177      * @param {Object} o An object which contains an Array of row objects in the property specified
16178      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16179      * which contains the total size of the dataset.
16180      * @return {Object} data A data block which is used by an Roo.data.Store object as
16181      * a cache of Roo.data.Records.
16182      */
16183     readRecords : function(o){
16184         /**
16185          * After any data loads, the raw JSON data is available for further custom processing.
16186          * @type Object
16187          */
16188         this.o = o;
16189         var s = this.meta, Record = this.recordType,
16190             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16191
16192 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16193         if (!this.ef) {
16194             if(s.totalProperty) {
16195                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16196                 }
16197                 if(s.successProperty) {
16198                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16199                 }
16200                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16201                 if (s.id) {
16202                         var g = this.getJsonAccessor(s.id);
16203                         this.getId = function(rec) {
16204                                 var r = g(rec);  
16205                                 return (r === undefined || r === "") ? null : r;
16206                         };
16207                 } else {
16208                         this.getId = function(){return null;};
16209                 }
16210             this.ef = [];
16211             for(var jj = 0; jj < fl; jj++){
16212                 f = fi[jj];
16213                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16214                 this.ef[jj] = this.getJsonAccessor(map);
16215             }
16216         }
16217
16218         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16219         if(s.totalProperty){
16220             var vt = parseInt(this.getTotal(o), 10);
16221             if(!isNaN(vt)){
16222                 totalRecords = vt;
16223             }
16224         }
16225         if(s.successProperty){
16226             var vs = this.getSuccess(o);
16227             if(vs === false || vs === 'false'){
16228                 success = false;
16229             }
16230         }
16231         var records = [];
16232         for(var i = 0; i < c; i++){
16233                 var n = root[i];
16234             var values = {};
16235             var id = this.getId(n);
16236             for(var j = 0; j < fl; j++){
16237                 f = fi[j];
16238             var v = this.ef[j](n);
16239             if (!f.convert) {
16240                 Roo.log('missing convert for ' + f.name);
16241                 Roo.log(f);
16242                 continue;
16243             }
16244             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16245             }
16246             var record = new Record(values, id);
16247             record.json = n;
16248             records[i] = record;
16249         }
16250         return {
16251             raw : o,
16252             success : success,
16253             records : records,
16254             totalRecords : totalRecords
16255         };
16256     },
16257     // used when loading children.. @see loadDataFromChildren
16258     toLoadData: function(rec)
16259     {
16260         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16261         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16262         return { data : data, total : data.length };
16263         
16264     }
16265 });/*
16266  * Based on:
16267  * Ext JS Library 1.1.1
16268  * Copyright(c) 2006-2007, Ext JS, LLC.
16269  *
16270  * Originally Released Under LGPL - original licence link has changed is not relivant.
16271  *
16272  * Fork - LGPL
16273  * <script type="text/javascript">
16274  */
16275
16276 /**
16277  * @class Roo.data.ArrayReader
16278  * @extends Roo.data.DataReader
16279  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16280  * Each element of that Array represents a row of data fields. The
16281  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16282  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16283  * <p>
16284  * Example code:.
16285  * <pre><code>
16286 var RecordDef = Roo.data.Record.create([
16287     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16288     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16289 ]);
16290 var myReader = new Roo.data.ArrayReader({
16291     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16292 }, RecordDef);
16293 </code></pre>
16294  * <p>
16295  * This would consume an Array like this:
16296  * <pre><code>
16297 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16298   </code></pre>
16299  
16300  * @constructor
16301  * Create a new JsonReader
16302  * @param {Object} meta Metadata configuration options.
16303  * @param {Object|Array} recordType Either an Array of field definition objects
16304  * 
16305  * @cfg {Array} fields Array of field definition objects
16306  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16307  * as specified to {@link Roo.data.Record#create},
16308  * or an {@link Roo.data.Record} object
16309  *
16310  * 
16311  * created using {@link Roo.data.Record#create}.
16312  */
16313 Roo.data.ArrayReader = function(meta, recordType)
16314 {    
16315     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16316 };
16317
16318 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16319     
16320       /**
16321      * Create a data block containing Roo.data.Records from an XML document.
16322      * @param {Object} o An Array of row objects which represents the dataset.
16323      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16324      * a cache of Roo.data.Records.
16325      */
16326     readRecords : function(o)
16327     {
16328         var sid = this.meta ? this.meta.id : null;
16329         var recordType = this.recordType, fields = recordType.prototype.fields;
16330         var records = [];
16331         var root = o;
16332         for(var i = 0; i < root.length; i++){
16333             var n = root[i];
16334             var values = {};
16335             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16336             for(var j = 0, jlen = fields.length; j < jlen; j++){
16337                 var f = fields.items[j];
16338                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16339                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16340                 v = f.convert(v);
16341                 values[f.name] = v;
16342             }
16343             var record = new recordType(values, id);
16344             record.json = n;
16345             records[records.length] = record;
16346         }
16347         return {
16348             records : records,
16349             totalRecords : records.length
16350         };
16351     },
16352     // used when loading children.. @see loadDataFromChildren
16353     toLoadData: function(rec)
16354     {
16355         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16356         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16357         
16358     }
16359     
16360     
16361 });/*
16362  * - LGPL
16363  * * 
16364  */
16365
16366 /**
16367  * @class Roo.bootstrap.ComboBox
16368  * @extends Roo.bootstrap.TriggerField
16369  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16370  * @cfg {Boolean} append (true|false) default false
16371  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16372  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16373  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16374  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16375  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16376  * @cfg {Boolean} animate default true
16377  * @cfg {Boolean} emptyResultText only for touch device
16378  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16379  * @cfg {String} emptyTitle default ''
16380  * @cfg {Number} width fixed with? experimental
16381  * @constructor
16382  * Create a new ComboBox.
16383  * @param {Object} config Configuration options
16384  */
16385 Roo.bootstrap.ComboBox = function(config){
16386     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16387     this.addEvents({
16388         /**
16389          * @event expand
16390          * Fires when the dropdown list is expanded
16391         * @param {Roo.bootstrap.ComboBox} combo This combo box
16392         */
16393         'expand' : true,
16394         /**
16395          * @event collapse
16396          * Fires when the dropdown list is collapsed
16397         * @param {Roo.bootstrap.ComboBox} combo This combo box
16398         */
16399         'collapse' : true,
16400         /**
16401          * @event beforeselect
16402          * Fires before a list item is selected. Return false to cancel the selection.
16403         * @param {Roo.bootstrap.ComboBox} combo This combo box
16404         * @param {Roo.data.Record} record The data record returned from the underlying store
16405         * @param {Number} index The index of the selected item in the dropdown list
16406         */
16407         'beforeselect' : true,
16408         /**
16409          * @event select
16410          * Fires when a list item is selected
16411         * @param {Roo.bootstrap.ComboBox} combo This combo box
16412         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16413         * @param {Number} index The index of the selected item in the dropdown list
16414         */
16415         'select' : true,
16416         /**
16417          * @event beforequery
16418          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16419          * The event object passed has these properties:
16420         * @param {Roo.bootstrap.ComboBox} combo This combo box
16421         * @param {String} query The query
16422         * @param {Boolean} forceAll true to force "all" query
16423         * @param {Boolean} cancel true to cancel the query
16424         * @param {Object} e The query event object
16425         */
16426         'beforequery': true,
16427          /**
16428          * @event add
16429          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16430         * @param {Roo.bootstrap.ComboBox} combo This combo box
16431         */
16432         'add' : true,
16433         /**
16434          * @event edit
16435          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16436         * @param {Roo.bootstrap.ComboBox} combo This combo box
16437         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16438         */
16439         'edit' : true,
16440         /**
16441          * @event remove
16442          * Fires when the remove value from the combobox array
16443         * @param {Roo.bootstrap.ComboBox} combo This combo box
16444         */
16445         'remove' : true,
16446         /**
16447          * @event afterremove
16448          * Fires when the remove value from the combobox array
16449         * @param {Roo.bootstrap.ComboBox} combo This combo box
16450         */
16451         'afterremove' : true,
16452         /**
16453          * @event specialfilter
16454          * Fires when specialfilter
16455             * @param {Roo.bootstrap.ComboBox} combo This combo box
16456             */
16457         'specialfilter' : true,
16458         /**
16459          * @event tick
16460          * Fires when tick the element
16461             * @param {Roo.bootstrap.ComboBox} combo This combo box
16462             */
16463         'tick' : true,
16464         /**
16465          * @event touchviewdisplay
16466          * Fires when touch view require special display (default is using displayField)
16467             * @param {Roo.bootstrap.ComboBox} combo This combo box
16468             * @param {Object} cfg set html .
16469             */
16470         'touchviewdisplay' : true
16471         
16472     });
16473     
16474     this.item = [];
16475     this.tickItems = [];
16476     
16477     this.selectedIndex = -1;
16478     if(this.mode == 'local'){
16479         if(config.queryDelay === undefined){
16480             this.queryDelay = 10;
16481         }
16482         if(config.minChars === undefined){
16483             this.minChars = 0;
16484         }
16485     }
16486 };
16487
16488 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16489      
16490     /**
16491      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16492      * rendering into an Roo.Editor, defaults to false)
16493      */
16494     /**
16495      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16496      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16497      */
16498     /**
16499      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16500      */
16501     /**
16502      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16503      * the dropdown list (defaults to undefined, with no header element)
16504      */
16505
16506      /**
16507      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16508      */
16509      
16510      /**
16511      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16512      */
16513     listWidth: undefined,
16514     /**
16515      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16516      * mode = 'remote' or 'text' if mode = 'local')
16517      */
16518     displayField: undefined,
16519     
16520     /**
16521      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16522      * mode = 'remote' or 'value' if mode = 'local'). 
16523      * Note: use of a valueField requires the user make a selection
16524      * in order for a value to be mapped.
16525      */
16526     valueField: undefined,
16527     /**
16528      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16529      */
16530     modalTitle : '',
16531     
16532     /**
16533      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16534      * field's data value (defaults to the underlying DOM element's name)
16535      */
16536     hiddenName: undefined,
16537     /**
16538      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16539      */
16540     listClass: '',
16541     /**
16542      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16543      */
16544     selectedClass: 'active',
16545     
16546     /**
16547      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16548      */
16549     shadow:'sides',
16550     /**
16551      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16552      * anchor positions (defaults to 'tl-bl')
16553      */
16554     listAlign: 'tl-bl?',
16555     /**
16556      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16557      */
16558     maxHeight: 300,
16559     /**
16560      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16561      * query specified by the allQuery config option (defaults to 'query')
16562      */
16563     triggerAction: 'query',
16564     /**
16565      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16566      * (defaults to 4, does not apply if editable = false)
16567      */
16568     minChars : 4,
16569     /**
16570      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16571      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16572      */
16573     typeAhead: false,
16574     /**
16575      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16576      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16577      */
16578     queryDelay: 500,
16579     /**
16580      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16581      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16582      */
16583     pageSize: 0,
16584     /**
16585      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16586      * when editable = true (defaults to false)
16587      */
16588     selectOnFocus:false,
16589     /**
16590      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16591      */
16592     queryParam: 'query',
16593     /**
16594      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16595      * when mode = 'remote' (defaults to 'Loading...')
16596      */
16597     loadingText: 'Loading...',
16598     /**
16599      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16600      */
16601     resizable: false,
16602     /**
16603      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16604      */
16605     handleHeight : 8,
16606     /**
16607      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16608      * traditional select (defaults to true)
16609      */
16610     editable: true,
16611     /**
16612      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16613      */
16614     allQuery: '',
16615     /**
16616      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16617      */
16618     mode: 'remote',
16619     /**
16620      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16621      * listWidth has a higher value)
16622      */
16623     minListWidth : 70,
16624     /**
16625      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16626      * allow the user to set arbitrary text into the field (defaults to false)
16627      */
16628     forceSelection:false,
16629     /**
16630      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16631      * if typeAhead = true (defaults to 250)
16632      */
16633     typeAheadDelay : 250,
16634     /**
16635      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16636      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16637      */
16638     valueNotFoundText : undefined,
16639     /**
16640      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16641      */
16642     blockFocus : false,
16643     
16644     /**
16645      * @cfg {Boolean} disableClear Disable showing of clear button.
16646      */
16647     disableClear : false,
16648     /**
16649      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16650      */
16651     alwaysQuery : false,
16652     
16653     /**
16654      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16655      */
16656     multiple : false,
16657     
16658     /**
16659      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16660      */
16661     invalidClass : "has-warning",
16662     
16663     /**
16664      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16665      */
16666     validClass : "has-success",
16667     
16668     /**
16669      * @cfg {Boolean} specialFilter (true|false) special filter default false
16670      */
16671     specialFilter : false,
16672     
16673     /**
16674      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16675      */
16676     mobileTouchView : true,
16677     
16678     /**
16679      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16680      */
16681     useNativeIOS : false,
16682     
16683     /**
16684      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16685      */
16686     mobile_restrict_height : false,
16687     
16688     ios_options : false,
16689     
16690     //private
16691     addicon : false,
16692     editicon: false,
16693     
16694     page: 0,
16695     hasQuery: false,
16696     append: false,
16697     loadNext: false,
16698     autoFocus : true,
16699     tickable : false,
16700     btnPosition : 'right',
16701     triggerList : true,
16702     showToggleBtn : true,
16703     animate : true,
16704     emptyResultText: 'Empty',
16705     triggerText : 'Select',
16706     emptyTitle : '',
16707     width : false,
16708     
16709     // element that contains real text value.. (when hidden is used..)
16710     
16711     getAutoCreate : function()
16712     {   
16713         var cfg = false;
16714         //render
16715         /*
16716          * Render classic select for iso
16717          */
16718         
16719         if(Roo.isIOS && this.useNativeIOS){
16720             cfg = this.getAutoCreateNativeIOS();
16721             return cfg;
16722         }
16723         
16724         /*
16725          * Touch Devices
16726          */
16727         
16728         if(Roo.isTouch && this.mobileTouchView){
16729             cfg = this.getAutoCreateTouchView();
16730             return cfg;;
16731         }
16732         
16733         /*
16734          *  Normal ComboBox
16735          */
16736         if(!this.tickable){
16737             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16738             return cfg;
16739         }
16740         
16741         /*
16742          *  ComboBox with tickable selections
16743          */
16744              
16745         var align = this.labelAlign || this.parentLabelAlign();
16746         
16747         cfg = {
16748             cls : 'form-group roo-combobox-tickable' //input-group
16749         };
16750         
16751         var btn_text_select = '';
16752         var btn_text_done = '';
16753         var btn_text_cancel = '';
16754         
16755         if (this.btn_text_show) {
16756             btn_text_select = 'Select';
16757             btn_text_done = 'Done';
16758             btn_text_cancel = 'Cancel'; 
16759         }
16760         
16761         var buttons = {
16762             tag : 'div',
16763             cls : 'tickable-buttons',
16764             cn : [
16765                 {
16766                     tag : 'button',
16767                     type : 'button',
16768                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16769                     //html : this.triggerText
16770                     html: btn_text_select
16771                 },
16772                 {
16773                     tag : 'button',
16774                     type : 'button',
16775                     name : 'ok',
16776                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16777                     //html : 'Done'
16778                     html: btn_text_done
16779                 },
16780                 {
16781                     tag : 'button',
16782                     type : 'button',
16783                     name : 'cancel',
16784                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16785                     //html : 'Cancel'
16786                     html: btn_text_cancel
16787                 }
16788             ]
16789         };
16790         
16791         if(this.editable){
16792             buttons.cn.unshift({
16793                 tag: 'input',
16794                 cls: 'roo-select2-search-field-input'
16795             });
16796         }
16797         
16798         var _this = this;
16799         
16800         Roo.each(buttons.cn, function(c){
16801             if (_this.size) {
16802                 c.cls += ' btn-' + _this.size;
16803             }
16804
16805             if (_this.disabled) {
16806                 c.disabled = true;
16807             }
16808         });
16809         
16810         var box = {
16811             tag: 'div',
16812             style : 'display: contents',
16813             cn: [
16814                 {
16815                     tag: 'input',
16816                     type : 'hidden',
16817                     cls: 'form-hidden-field'
16818                 },
16819                 {
16820                     tag: 'ul',
16821                     cls: 'roo-select2-choices',
16822                     cn:[
16823                         {
16824                             tag: 'li',
16825                             cls: 'roo-select2-search-field',
16826                             cn: [
16827                                 buttons
16828                             ]
16829                         }
16830                     ]
16831                 }
16832             ]
16833         };
16834         
16835         var combobox = {
16836             cls: 'roo-select2-container input-group roo-select2-container-multi',
16837             cn: [
16838                 
16839                 box
16840 //                {
16841 //                    tag: 'ul',
16842 //                    cls: 'typeahead typeahead-long dropdown-menu',
16843 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16844 //                }
16845             ]
16846         };
16847         
16848         if(this.hasFeedback && !this.allowBlank){
16849             
16850             var feedback = {
16851                 tag: 'span',
16852                 cls: 'glyphicon form-control-feedback'
16853             };
16854
16855             combobox.cn.push(feedback);
16856         }
16857         
16858         
16859         
16860         var indicator = {
16861             tag : 'i',
16862             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16863             tooltip : 'This field is required'
16864         };
16865         if (Roo.bootstrap.version == 4) {
16866             indicator = {
16867                 tag : 'i',
16868                 style : 'display:none'
16869             };
16870         }
16871         if (align ==='left' && this.fieldLabel.length) {
16872             
16873             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16874             
16875             cfg.cn = [
16876                 indicator,
16877                 {
16878                     tag: 'label',
16879                     'for' :  id,
16880                     cls : 'control-label col-form-label',
16881                     html : this.fieldLabel
16882
16883                 },
16884                 {
16885                     cls : "", 
16886                     cn: [
16887                         combobox
16888                     ]
16889                 }
16890
16891             ];
16892             
16893             var labelCfg = cfg.cn[1];
16894             var contentCfg = cfg.cn[2];
16895             
16896
16897             if(this.indicatorpos == 'right'){
16898                 
16899                 cfg.cn = [
16900                     {
16901                         tag: 'label',
16902                         'for' :  id,
16903                         cls : 'control-label col-form-label',
16904                         cn : [
16905                             {
16906                                 tag : 'span',
16907                                 html : this.fieldLabel
16908                             },
16909                             indicator
16910                         ]
16911                     },
16912                     {
16913                         cls : "",
16914                         cn: [
16915                             combobox
16916                         ]
16917                     }
16918
16919                 ];
16920                 
16921                 
16922                 
16923                 labelCfg = cfg.cn[0];
16924                 contentCfg = cfg.cn[1];
16925             
16926             }
16927             
16928             if(this.labelWidth > 12){
16929                 labelCfg.style = "width: " + this.labelWidth + 'px';
16930             }
16931             if(this.width * 1 > 0){
16932                 contentCfg.style = "width: " + this.width + 'px';
16933             }
16934             if(this.labelWidth < 13 && this.labelmd == 0){
16935                 this.labelmd = this.labelWidth;
16936             }
16937             
16938             if(this.labellg > 0){
16939                 labelCfg.cls += ' col-lg-' + this.labellg;
16940                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16941             }
16942             
16943             if(this.labelmd > 0){
16944                 labelCfg.cls += ' col-md-' + this.labelmd;
16945                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16946             }
16947             
16948             if(this.labelsm > 0){
16949                 labelCfg.cls += ' col-sm-' + this.labelsm;
16950                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16951             }
16952             
16953             if(this.labelxs > 0){
16954                 labelCfg.cls += ' col-xs-' + this.labelxs;
16955                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16956             }
16957                 
16958                 
16959         } else if ( this.fieldLabel.length) {
16960 //                Roo.log(" label");
16961                  cfg.cn = [
16962                    indicator,
16963                     {
16964                         tag: 'label',
16965                         //cls : 'input-group-addon',
16966                         html : this.fieldLabel
16967                     },
16968                     combobox
16969                 ];
16970                 
16971                 if(this.indicatorpos == 'right'){
16972                     cfg.cn = [
16973                         {
16974                             tag: 'label',
16975                             //cls : 'input-group-addon',
16976                             html : this.fieldLabel
16977                         },
16978                         indicator,
16979                         combobox
16980                     ];
16981                     
16982                 }
16983
16984         } else {
16985             
16986 //                Roo.log(" no label && no align");
16987                 cfg = combobox
16988                      
16989                 
16990         }
16991          
16992         var settings=this;
16993         ['xs','sm','md','lg'].map(function(size){
16994             if (settings[size]) {
16995                 cfg.cls += ' col-' + size + '-' + settings[size];
16996             }
16997         });
16998         
16999         return cfg;
17000         
17001     },
17002     
17003     _initEventsCalled : false,
17004     
17005     // private
17006     initEvents: function()
17007     {   
17008         if (this._initEventsCalled) { // as we call render... prevent looping...
17009             return;
17010         }
17011         this._initEventsCalled = true;
17012         
17013         if (!this.store) {
17014             throw "can not find store for combo";
17015         }
17016         
17017         this.indicator = this.indicatorEl();
17018         
17019         this.store = Roo.factory(this.store, Roo.data);
17020         this.store.parent = this;
17021         
17022         // if we are building from html. then this element is so complex, that we can not really
17023         // use the rendered HTML.
17024         // so we have to trash and replace the previous code.
17025         if (Roo.XComponent.build_from_html) {
17026             // remove this element....
17027             var e = this.el.dom, k=0;
17028             while (e ) { e = e.previousSibling;  ++k;}
17029
17030             this.el.remove();
17031             
17032             this.el=false;
17033             this.rendered = false;
17034             
17035             this.render(this.parent().getChildContainer(true), k);
17036         }
17037         
17038         if(Roo.isIOS && this.useNativeIOS){
17039             this.initIOSView();
17040             return;
17041         }
17042         
17043         /*
17044          * Touch Devices
17045          */
17046         
17047         if(Roo.isTouch && this.mobileTouchView){
17048             this.initTouchView();
17049             return;
17050         }
17051         
17052         if(this.tickable){
17053             this.initTickableEvents();
17054             return;
17055         }
17056         
17057         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17058         
17059         if(this.hiddenName){
17060             
17061             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17062             
17063             this.hiddenField.dom.value =
17064                 this.hiddenValue !== undefined ? this.hiddenValue :
17065                 this.value !== undefined ? this.value : '';
17066
17067             // prevent input submission
17068             this.el.dom.removeAttribute('name');
17069             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17070              
17071              
17072         }
17073         //if(Roo.isGecko){
17074         //    this.el.dom.setAttribute('autocomplete', 'off');
17075         //}
17076         
17077         var cls = 'x-combo-list';
17078         
17079         //this.list = new Roo.Layer({
17080         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17081         //});
17082         
17083         var _this = this;
17084         
17085         (function(){
17086             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17087             _this.list.setWidth(lw);
17088         }).defer(100);
17089         
17090         this.list.on('mouseover', this.onViewOver, this);
17091         this.list.on('mousemove', this.onViewMove, this);
17092         this.list.on('scroll', this.onViewScroll, this);
17093         
17094         /*
17095         this.list.swallowEvent('mousewheel');
17096         this.assetHeight = 0;
17097
17098         if(this.title){
17099             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17100             this.assetHeight += this.header.getHeight();
17101         }
17102
17103         this.innerList = this.list.createChild({cls:cls+'-inner'});
17104         this.innerList.on('mouseover', this.onViewOver, this);
17105         this.innerList.on('mousemove', this.onViewMove, this);
17106         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17107         
17108         if(this.allowBlank && !this.pageSize && !this.disableClear){
17109             this.footer = this.list.createChild({cls:cls+'-ft'});
17110             this.pageTb = new Roo.Toolbar(this.footer);
17111            
17112         }
17113         if(this.pageSize){
17114             this.footer = this.list.createChild({cls:cls+'-ft'});
17115             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17116                     {pageSize: this.pageSize});
17117             
17118         }
17119         
17120         if (this.pageTb && this.allowBlank && !this.disableClear) {
17121             var _this = this;
17122             this.pageTb.add(new Roo.Toolbar.Fill(), {
17123                 cls: 'x-btn-icon x-btn-clear',
17124                 text: '&#160;',
17125                 handler: function()
17126                 {
17127                     _this.collapse();
17128                     _this.clearValue();
17129                     _this.onSelect(false, -1);
17130                 }
17131             });
17132         }
17133         if (this.footer) {
17134             this.assetHeight += this.footer.getHeight();
17135         }
17136         */
17137             
17138         if(!this.tpl){
17139             this.tpl = Roo.bootstrap.version == 4 ?
17140                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17141                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17142         }
17143
17144         this.view = new Roo.View(this.list, this.tpl, {
17145             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17146         });
17147         //this.view.wrapEl.setDisplayed(false);
17148         this.view.on('click', this.onViewClick, this);
17149         
17150         
17151         this.store.on('beforeload', this.onBeforeLoad, this);
17152         this.store.on('load', this.onLoad, this);
17153         this.store.on('loadexception', this.onLoadException, this);
17154         /*
17155         if(this.resizable){
17156             this.resizer = new Roo.Resizable(this.list,  {
17157                pinned:true, handles:'se'
17158             });
17159             this.resizer.on('resize', function(r, w, h){
17160                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17161                 this.listWidth = w;
17162                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17163                 this.restrictHeight();
17164             }, this);
17165             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17166         }
17167         */
17168         if(!this.editable){
17169             this.editable = true;
17170             this.setEditable(false);
17171         }
17172         
17173         /*
17174         
17175         if (typeof(this.events.add.listeners) != 'undefined') {
17176             
17177             this.addicon = this.wrap.createChild(
17178                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17179        
17180             this.addicon.on('click', function(e) {
17181                 this.fireEvent('add', this);
17182             }, this);
17183         }
17184         if (typeof(this.events.edit.listeners) != 'undefined') {
17185             
17186             this.editicon = this.wrap.createChild(
17187                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17188             if (this.addicon) {
17189                 this.editicon.setStyle('margin-left', '40px');
17190             }
17191             this.editicon.on('click', function(e) {
17192                 
17193                 // we fire even  if inothing is selected..
17194                 this.fireEvent('edit', this, this.lastData );
17195                 
17196             }, this);
17197         }
17198         */
17199         
17200         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17201             "up" : function(e){
17202                 this.inKeyMode = true;
17203                 this.selectPrev();
17204             },
17205
17206             "down" : function(e){
17207                 if(!this.isExpanded()){
17208                     this.onTriggerClick();
17209                 }else{
17210                     this.inKeyMode = true;
17211                     this.selectNext();
17212                 }
17213             },
17214
17215             "enter" : function(e){
17216 //                this.onViewClick();
17217                 //return true;
17218                 this.collapse();
17219                 
17220                 if(this.fireEvent("specialkey", this, e)){
17221                     this.onViewClick(false);
17222                 }
17223                 
17224                 return true;
17225             },
17226
17227             "esc" : function(e){
17228                 this.collapse();
17229             },
17230
17231             "tab" : function(e){
17232                 this.collapse();
17233                 
17234                 if(this.fireEvent("specialkey", this, e)){
17235                     this.onViewClick(false);
17236                 }
17237                 
17238                 return true;
17239             },
17240
17241             scope : this,
17242
17243             doRelay : function(foo, bar, hname){
17244                 if(hname == 'down' || this.scope.isExpanded()){
17245                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17246                 }
17247                 return true;
17248             },
17249
17250             forceKeyDown: true
17251         });
17252         
17253         
17254         this.queryDelay = Math.max(this.queryDelay || 10,
17255                 this.mode == 'local' ? 10 : 250);
17256         
17257         
17258         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17259         
17260         if(this.typeAhead){
17261             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17262         }
17263         if(this.editable !== false){
17264             this.inputEl().on("keyup", this.onKeyUp, this);
17265         }
17266         if(this.forceSelection){
17267             this.inputEl().on('blur', this.doForce, this);
17268         }
17269         
17270         if(this.multiple){
17271             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17272             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17273         }
17274     },
17275     
17276     initTickableEvents: function()
17277     {   
17278         this.createList();
17279         
17280         if(this.hiddenName){
17281             
17282             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17283             
17284             this.hiddenField.dom.value =
17285                 this.hiddenValue !== undefined ? this.hiddenValue :
17286                 this.value !== undefined ? this.value : '';
17287
17288             // prevent input submission
17289             this.el.dom.removeAttribute('name');
17290             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17291              
17292              
17293         }
17294         
17295 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17296         
17297         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17298         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17299         if(this.triggerList){
17300             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17301         }
17302          
17303         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17304         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17305         
17306         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17307         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17308         
17309         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17310         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17311         
17312         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17313         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17314         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17315         
17316         this.okBtn.hide();
17317         this.cancelBtn.hide();
17318         
17319         var _this = this;
17320         
17321         (function(){
17322             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17323             _this.list.setWidth(lw);
17324         }).defer(100);
17325         
17326         this.list.on('mouseover', this.onViewOver, this);
17327         this.list.on('mousemove', this.onViewMove, this);
17328         
17329         this.list.on('scroll', this.onViewScroll, this);
17330         
17331         if(!this.tpl){
17332             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17333                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17334         }
17335
17336         this.view = new Roo.View(this.list, this.tpl, {
17337             singleSelect:true,
17338             tickable:true,
17339             parent:this,
17340             store: this.store,
17341             selectedClass: this.selectedClass
17342         });
17343         
17344         //this.view.wrapEl.setDisplayed(false);
17345         this.view.on('click', this.onViewClick, this);
17346         
17347         
17348         
17349         this.store.on('beforeload', this.onBeforeLoad, this);
17350         this.store.on('load', this.onLoad, this);
17351         this.store.on('loadexception', this.onLoadException, this);
17352         
17353         if(this.editable){
17354             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17355                 "up" : function(e){
17356                     this.inKeyMode = true;
17357                     this.selectPrev();
17358                 },
17359
17360                 "down" : function(e){
17361                     this.inKeyMode = true;
17362                     this.selectNext();
17363                 },
17364
17365                 "enter" : function(e){
17366                     if(this.fireEvent("specialkey", this, e)){
17367                         this.onViewClick(false);
17368                     }
17369                     
17370                     return true;
17371                 },
17372
17373                 "esc" : function(e){
17374                     this.onTickableFooterButtonClick(e, false, false);
17375                 },
17376
17377                 "tab" : function(e){
17378                     this.fireEvent("specialkey", this, e);
17379                     
17380                     this.onTickableFooterButtonClick(e, false, false);
17381                     
17382                     return true;
17383                 },
17384
17385                 scope : this,
17386
17387                 doRelay : function(e, fn, key){
17388                     if(this.scope.isExpanded()){
17389                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17390                     }
17391                     return true;
17392                 },
17393
17394                 forceKeyDown: true
17395             });
17396         }
17397         
17398         this.queryDelay = Math.max(this.queryDelay || 10,
17399                 this.mode == 'local' ? 10 : 250);
17400         
17401         
17402         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17403         
17404         if(this.typeAhead){
17405             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17406         }
17407         
17408         if(this.editable !== false){
17409             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17410         }
17411         
17412         this.indicator = this.indicatorEl();
17413         
17414         if(this.indicator){
17415             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17416             this.indicator.hide();
17417         }
17418         
17419     },
17420
17421     onDestroy : function(){
17422         if(this.view){
17423             this.view.setStore(null);
17424             this.view.el.removeAllListeners();
17425             this.view.el.remove();
17426             this.view.purgeListeners();
17427         }
17428         if(this.list){
17429             this.list.dom.innerHTML  = '';
17430         }
17431         
17432         if(this.store){
17433             this.store.un('beforeload', this.onBeforeLoad, this);
17434             this.store.un('load', this.onLoad, this);
17435             this.store.un('loadexception', this.onLoadException, this);
17436         }
17437         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17438     },
17439
17440     // private
17441     fireKey : function(e){
17442         if(e.isNavKeyPress() && !this.list.isVisible()){
17443             this.fireEvent("specialkey", this, e);
17444         }
17445     },
17446
17447     // private
17448     onResize: function(w, h)
17449     {
17450         
17451         
17452 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17453 //        
17454 //        if(typeof w != 'number'){
17455 //            // we do not handle it!?!?
17456 //            return;
17457 //        }
17458 //        var tw = this.trigger.getWidth();
17459 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17460 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17461 //        var x = w - tw;
17462 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17463 //            
17464 //        //this.trigger.setStyle('left', x+'px');
17465 //        
17466 //        if(this.list && this.listWidth === undefined){
17467 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17468 //            this.list.setWidth(lw);
17469 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17470 //        }
17471         
17472     
17473         
17474     },
17475
17476     /**
17477      * Allow or prevent the user from directly editing the field text.  If false is passed,
17478      * the user will only be able to select from the items defined in the dropdown list.  This method
17479      * is the runtime equivalent of setting the 'editable' config option at config time.
17480      * @param {Boolean} value True to allow the user to directly edit the field text
17481      */
17482     setEditable : function(value){
17483         if(value == this.editable){
17484             return;
17485         }
17486         this.editable = value;
17487         if(!value){
17488             this.inputEl().dom.setAttribute('readOnly', true);
17489             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17490             this.inputEl().addClass('x-combo-noedit');
17491         }else{
17492             this.inputEl().dom.removeAttribute('readOnly');
17493             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17494             this.inputEl().removeClass('x-combo-noedit');
17495         }
17496     },
17497
17498     // private
17499     
17500     onBeforeLoad : function(combo,opts){
17501         if(!this.hasFocus){
17502             return;
17503         }
17504          if (!opts.add) {
17505             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17506          }
17507         this.restrictHeight();
17508         this.selectedIndex = -1;
17509     },
17510
17511     // private
17512     onLoad : function(){
17513         
17514         this.hasQuery = false;
17515         
17516         if(!this.hasFocus){
17517             return;
17518         }
17519         
17520         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17521             this.loading.hide();
17522         }
17523         
17524         if(this.store.getCount() > 0){
17525             
17526             this.expand();
17527             this.restrictHeight();
17528             if(this.lastQuery == this.allQuery){
17529                 if(this.editable && !this.tickable){
17530                     this.inputEl().dom.select();
17531                 }
17532                 
17533                 if(
17534                     !this.selectByValue(this.value, true) &&
17535                     this.autoFocus && 
17536                     (
17537                         !this.store.lastOptions ||
17538                         typeof(this.store.lastOptions.add) == 'undefined' || 
17539                         this.store.lastOptions.add != true
17540                     )
17541                 ){
17542                     this.select(0, true);
17543                 }
17544             }else{
17545                 if(this.autoFocus){
17546                     this.selectNext();
17547                 }
17548                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17549                     this.taTask.delay(this.typeAheadDelay);
17550                 }
17551             }
17552         }else{
17553             this.onEmptyResults();
17554         }
17555         
17556         //this.el.focus();
17557     },
17558     // private
17559     onLoadException : function()
17560     {
17561         this.hasQuery = false;
17562         
17563         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17564             this.loading.hide();
17565         }
17566         
17567         if(this.tickable && this.editable){
17568             return;
17569         }
17570         
17571         this.collapse();
17572         // only causes errors at present
17573         //Roo.log(this.store.reader.jsonData);
17574         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17575             // fixme
17576             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17577         //}
17578         
17579         
17580     },
17581     // private
17582     onTypeAhead : function(){
17583         if(this.store.getCount() > 0){
17584             var r = this.store.getAt(0);
17585             var newValue = r.data[this.displayField];
17586             var len = newValue.length;
17587             var selStart = this.getRawValue().length;
17588             
17589             if(selStart != len){
17590                 this.setRawValue(newValue);
17591                 this.selectText(selStart, newValue.length);
17592             }
17593         }
17594     },
17595
17596     // private
17597     onSelect : function(record, index){
17598         
17599         if(this.fireEvent('beforeselect', this, record, index) !== false){
17600         
17601             this.setFromData(index > -1 ? record.data : false);
17602             
17603             this.collapse();
17604             this.fireEvent('select', this, record, index);
17605         }
17606     },
17607
17608     /**
17609      * Returns the currently selected field value or empty string if no value is set.
17610      * @return {String} value The selected value
17611      */
17612     getValue : function()
17613     {
17614         if(Roo.isIOS && this.useNativeIOS){
17615             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17616         }
17617         
17618         if(this.multiple){
17619             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17620         }
17621         
17622         if(this.valueField){
17623             return typeof this.value != 'undefined' ? this.value : '';
17624         }else{
17625             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17626         }
17627     },
17628     
17629     getRawValue : function()
17630     {
17631         if(Roo.isIOS && this.useNativeIOS){
17632             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17633         }
17634         
17635         var v = this.inputEl().getValue();
17636         
17637         return v;
17638     },
17639
17640     /**
17641      * Clears any text/value currently set in the field
17642      */
17643     clearValue : function(){
17644         
17645         if(this.hiddenField){
17646             this.hiddenField.dom.value = '';
17647         }
17648         this.value = '';
17649         this.setRawValue('');
17650         this.lastSelectionText = '';
17651         this.lastData = false;
17652         
17653         var close = this.closeTriggerEl();
17654         
17655         if(close){
17656             close.hide();
17657         }
17658         
17659         this.validate();
17660         
17661     },
17662
17663     /**
17664      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17665      * will be displayed in the field.  If the value does not match the data value of an existing item,
17666      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17667      * Otherwise the field will be blank (although the value will still be set).
17668      * @param {String} value The value to match
17669      */
17670     setValue : function(v)
17671     {
17672         if(Roo.isIOS && this.useNativeIOS){
17673             this.setIOSValue(v);
17674             return;
17675         }
17676         
17677         if(this.multiple){
17678             this.syncValue();
17679             return;
17680         }
17681         
17682         var text = v;
17683         if(this.valueField){
17684             var r = this.findRecord(this.valueField, v);
17685             if(r){
17686                 text = r.data[this.displayField];
17687             }else if(this.valueNotFoundText !== undefined){
17688                 text = this.valueNotFoundText;
17689             }
17690         }
17691         this.lastSelectionText = text;
17692         if(this.hiddenField){
17693             this.hiddenField.dom.value = v;
17694         }
17695         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17696         this.value = v;
17697         
17698         var close = this.closeTriggerEl();
17699         
17700         if(close){
17701             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17702         }
17703         
17704         this.validate();
17705     },
17706     /**
17707      * @property {Object} the last set data for the element
17708      */
17709     
17710     lastData : false,
17711     /**
17712      * Sets the value of the field based on a object which is related to the record format for the store.
17713      * @param {Object} value the value to set as. or false on reset?
17714      */
17715     setFromData : function(o){
17716         
17717         if(this.multiple){
17718             this.addItem(o);
17719             return;
17720         }
17721             
17722         var dv = ''; // display value
17723         var vv = ''; // value value..
17724         this.lastData = o;
17725         if (this.displayField) {
17726             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17727         } else {
17728             // this is an error condition!!!
17729             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17730         }
17731         
17732         if(this.valueField){
17733             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17734         }
17735         
17736         var close = this.closeTriggerEl();
17737         
17738         if(close){
17739             if(dv.length || vv * 1 > 0){
17740                 close.show() ;
17741                 this.blockFocus=true;
17742             } else {
17743                 close.hide();
17744             }             
17745         }
17746         
17747         if(this.hiddenField){
17748             this.hiddenField.dom.value = vv;
17749             
17750             this.lastSelectionText = dv;
17751             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17752             this.value = vv;
17753             return;
17754         }
17755         // no hidden field.. - we store the value in 'value', but still display
17756         // display field!!!!
17757         this.lastSelectionText = dv;
17758         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17759         this.value = vv;
17760         
17761         
17762         
17763     },
17764     // private
17765     reset : function(){
17766         // overridden so that last data is reset..
17767         
17768         if(this.multiple){
17769             this.clearItem();
17770             return;
17771         }
17772         
17773         this.setValue(this.originalValue);
17774         //this.clearInvalid();
17775         this.lastData = false;
17776         if (this.view) {
17777             this.view.clearSelections();
17778         }
17779         
17780         this.validate();
17781     },
17782     // private
17783     findRecord : function(prop, value){
17784         var record;
17785         if(this.store.getCount() > 0){
17786             this.store.each(function(r){
17787                 if(r.data[prop] == value){
17788                     record = r;
17789                     return false;
17790                 }
17791                 return true;
17792             });
17793         }
17794         return record;
17795     },
17796     
17797     getName: function()
17798     {
17799         // returns hidden if it's set..
17800         if (!this.rendered) {return ''};
17801         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17802         
17803     },
17804     // private
17805     onViewMove : function(e, t){
17806         this.inKeyMode = false;
17807     },
17808
17809     // private
17810     onViewOver : function(e, t){
17811         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17812             return;
17813         }
17814         var item = this.view.findItemFromChild(t);
17815         
17816         if(item){
17817             var index = this.view.indexOf(item);
17818             this.select(index, false);
17819         }
17820     },
17821
17822     // private
17823     onViewClick : function(view, doFocus, el, e)
17824     {
17825         var index = this.view.getSelectedIndexes()[0];
17826         
17827         var r = this.store.getAt(index);
17828         
17829         if(this.tickable){
17830             
17831             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17832                 return;
17833             }
17834             
17835             var rm = false;
17836             var _this = this;
17837             
17838             Roo.each(this.tickItems, function(v,k){
17839                 
17840                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17841                     Roo.log(v);
17842                     _this.tickItems.splice(k, 1);
17843                     
17844                     if(typeof(e) == 'undefined' && view == false){
17845                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17846                     }
17847                     
17848                     rm = true;
17849                     return;
17850                 }
17851             });
17852             
17853             if(rm){
17854                 return;
17855             }
17856             
17857             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17858                 this.tickItems.push(r.data);
17859             }
17860             
17861             if(typeof(e) == 'undefined' && view == false){
17862                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17863             }
17864                     
17865             return;
17866         }
17867         
17868         if(r){
17869             this.onSelect(r, index);
17870         }
17871         if(doFocus !== false && !this.blockFocus){
17872             this.inputEl().focus();
17873         }
17874     },
17875
17876     // private
17877     restrictHeight : function(){
17878         //this.innerList.dom.style.height = '';
17879         //var inner = this.innerList.dom;
17880         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17881         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17882         //this.list.beginUpdate();
17883         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17884         this.list.alignTo(this.inputEl(), this.listAlign);
17885         this.list.alignTo(this.inputEl(), this.listAlign);
17886         //this.list.endUpdate();
17887     },
17888
17889     // private
17890     onEmptyResults : function(){
17891         
17892         if(this.tickable && this.editable){
17893             this.hasFocus = false;
17894             this.restrictHeight();
17895             return;
17896         }
17897         
17898         this.collapse();
17899     },
17900
17901     /**
17902      * Returns true if the dropdown list is expanded, else false.
17903      */
17904     isExpanded : function(){
17905         return this.list.isVisible();
17906     },
17907
17908     /**
17909      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17910      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17911      * @param {String} value The data value of the item to select
17912      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17913      * selected item if it is not currently in view (defaults to true)
17914      * @return {Boolean} True if the value matched an item in the list, else false
17915      */
17916     selectByValue : function(v, scrollIntoView){
17917         if(v !== undefined && v !== null){
17918             var r = this.findRecord(this.valueField || this.displayField, v);
17919             if(r){
17920                 this.select(this.store.indexOf(r), scrollIntoView);
17921                 return true;
17922             }
17923         }
17924         return false;
17925     },
17926
17927     /**
17928      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17929      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17930      * @param {Number} index The zero-based index of the list item to select
17931      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17932      * selected item if it is not currently in view (defaults to true)
17933      */
17934     select : function(index, scrollIntoView){
17935         this.selectedIndex = index;
17936         this.view.select(index);
17937         if(scrollIntoView !== false){
17938             var el = this.view.getNode(index);
17939             /*
17940              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17941              */
17942             if(el){
17943                 this.list.scrollChildIntoView(el, false);
17944             }
17945         }
17946     },
17947
17948     // private
17949     selectNext : function(){
17950         var ct = this.store.getCount();
17951         if(ct > 0){
17952             if(this.selectedIndex == -1){
17953                 this.select(0);
17954             }else if(this.selectedIndex < ct-1){
17955                 this.select(this.selectedIndex+1);
17956             }
17957         }
17958     },
17959
17960     // private
17961     selectPrev : function(){
17962         var ct = this.store.getCount();
17963         if(ct > 0){
17964             if(this.selectedIndex == -1){
17965                 this.select(0);
17966             }else if(this.selectedIndex != 0){
17967                 this.select(this.selectedIndex-1);
17968             }
17969         }
17970     },
17971
17972     // private
17973     onKeyUp : function(e){
17974         if(this.editable !== false && !e.isSpecialKey()){
17975             this.lastKey = e.getKey();
17976             this.dqTask.delay(this.queryDelay);
17977         }
17978     },
17979
17980     // private
17981     validateBlur : function(){
17982         return !this.list || !this.list.isVisible();   
17983     },
17984
17985     // private
17986     initQuery : function(){
17987         
17988         var v = this.getRawValue();
17989         
17990         if(this.tickable && this.editable){
17991             v = this.tickableInputEl().getValue();
17992         }
17993         
17994         this.doQuery(v);
17995     },
17996
17997     // private
17998     doForce : function(){
17999         if(this.inputEl().dom.value.length > 0){
18000             this.inputEl().dom.value =
18001                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18002              
18003         }
18004     },
18005
18006     /**
18007      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18008      * query allowing the query action to be canceled if needed.
18009      * @param {String} query The SQL query to execute
18010      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18011      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18012      * saved in the current store (defaults to false)
18013      */
18014     doQuery : function(q, forceAll){
18015         
18016         if(q === undefined || q === null){
18017             q = '';
18018         }
18019         var qe = {
18020             query: q,
18021             forceAll: forceAll,
18022             combo: this,
18023             cancel:false
18024         };
18025         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18026             return false;
18027         }
18028         q = qe.query;
18029         
18030         forceAll = qe.forceAll;
18031         if(forceAll === true || (q.length >= this.minChars)){
18032             
18033             this.hasQuery = true;
18034             
18035             if(this.lastQuery != q || this.alwaysQuery){
18036                 this.lastQuery = q;
18037                 if(this.mode == 'local'){
18038                     this.selectedIndex = -1;
18039                     if(forceAll){
18040                         this.store.clearFilter();
18041                     }else{
18042                         
18043                         if(this.specialFilter){
18044                             this.fireEvent('specialfilter', this);
18045                             this.onLoad();
18046                             return;
18047                         }
18048                         
18049                         this.store.filter(this.displayField, q);
18050                     }
18051                     
18052                     this.store.fireEvent("datachanged", this.store);
18053                     
18054                     this.onLoad();
18055                     
18056                     
18057                 }else{
18058                     
18059                     this.store.baseParams[this.queryParam] = q;
18060                     
18061                     var options = {params : this.getParams(q)};
18062                     
18063                     if(this.loadNext){
18064                         options.add = true;
18065                         options.params.start = this.page * this.pageSize;
18066                     }
18067                     
18068                     this.store.load(options);
18069                     
18070                     /*
18071                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18072                      *  we should expand the list on onLoad
18073                      *  so command out it
18074                      */
18075 //                    this.expand();
18076                 }
18077             }else{
18078                 this.selectedIndex = -1;
18079                 this.onLoad();   
18080             }
18081         }
18082         
18083         this.loadNext = false;
18084     },
18085     
18086     // private
18087     getParams : function(q){
18088         var p = {};
18089         //p[this.queryParam] = q;
18090         
18091         if(this.pageSize){
18092             p.start = 0;
18093             p.limit = this.pageSize;
18094         }
18095         return p;
18096     },
18097
18098     /**
18099      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18100      */
18101     collapse : function(){
18102         if(!this.isExpanded()){
18103             return;
18104         }
18105         
18106         this.list.hide();
18107         
18108         this.hasFocus = false;
18109         
18110         if(this.tickable){
18111             this.okBtn.hide();
18112             this.cancelBtn.hide();
18113             this.trigger.show();
18114             
18115             if(this.editable){
18116                 this.tickableInputEl().dom.value = '';
18117                 this.tickableInputEl().blur();
18118             }
18119             
18120         }
18121         
18122         Roo.get(document).un('mousedown', this.collapseIf, this);
18123         Roo.get(document).un('mousewheel', this.collapseIf, this);
18124         if (!this.editable) {
18125             Roo.get(document).un('keydown', this.listKeyPress, this);
18126         }
18127         this.fireEvent('collapse', this);
18128         
18129         this.validate();
18130     },
18131
18132     // private
18133     collapseIf : function(e){
18134         var in_combo  = e.within(this.el);
18135         var in_list =  e.within(this.list);
18136         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18137         
18138         if (in_combo || in_list || is_list) {
18139             //e.stopPropagation();
18140             return;
18141         }
18142         
18143         if(this.tickable){
18144             this.onTickableFooterButtonClick(e, false, false);
18145         }
18146
18147         this.collapse();
18148         
18149     },
18150
18151     /**
18152      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18153      */
18154     expand : function(){
18155        
18156         if(this.isExpanded() || !this.hasFocus){
18157             return;
18158         }
18159         
18160         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18161         this.list.setWidth(lw);
18162         
18163         Roo.log('expand');
18164         
18165         this.list.show();
18166         
18167         this.restrictHeight();
18168         
18169         if(this.tickable){
18170             
18171             this.tickItems = Roo.apply([], this.item);
18172             
18173             this.okBtn.show();
18174             this.cancelBtn.show();
18175             this.trigger.hide();
18176             
18177             if(this.editable){
18178                 this.tickableInputEl().focus();
18179             }
18180             
18181         }
18182         
18183         Roo.get(document).on('mousedown', this.collapseIf, this);
18184         Roo.get(document).on('mousewheel', this.collapseIf, this);
18185         if (!this.editable) {
18186             Roo.get(document).on('keydown', this.listKeyPress, this);
18187         }
18188         
18189         this.fireEvent('expand', this);
18190     },
18191
18192     // private
18193     // Implements the default empty TriggerField.onTriggerClick function
18194     onTriggerClick : function(e)
18195     {
18196         Roo.log('trigger click');
18197         
18198         if(this.disabled || !this.triggerList){
18199             return;
18200         }
18201         
18202         this.page = 0;
18203         this.loadNext = false;
18204         
18205         if(this.isExpanded()){
18206             this.collapse();
18207             if (!this.blockFocus) {
18208                 this.inputEl().focus();
18209             }
18210             
18211         }else {
18212             this.hasFocus = true;
18213             if(this.triggerAction == 'all') {
18214                 this.doQuery(this.allQuery, true);
18215             } else {
18216                 this.doQuery(this.getRawValue());
18217             }
18218             if (!this.blockFocus) {
18219                 this.inputEl().focus();
18220             }
18221         }
18222     },
18223     
18224     onTickableTriggerClick : function(e)
18225     {
18226         if(this.disabled){
18227             return;
18228         }
18229         
18230         this.page = 0;
18231         this.loadNext = false;
18232         this.hasFocus = true;
18233         
18234         if(this.triggerAction == 'all') {
18235             this.doQuery(this.allQuery, true);
18236         } else {
18237             this.doQuery(this.getRawValue());
18238         }
18239     },
18240     
18241     onSearchFieldClick : function(e)
18242     {
18243         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18244             this.onTickableFooterButtonClick(e, false, false);
18245             return;
18246         }
18247         
18248         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18249             return;
18250         }
18251         
18252         this.page = 0;
18253         this.loadNext = false;
18254         this.hasFocus = true;
18255         
18256         if(this.triggerAction == 'all') {
18257             this.doQuery(this.allQuery, true);
18258         } else {
18259             this.doQuery(this.getRawValue());
18260         }
18261     },
18262     
18263     listKeyPress : function(e)
18264     {
18265         //Roo.log('listkeypress');
18266         // scroll to first matching element based on key pres..
18267         if (e.isSpecialKey()) {
18268             return false;
18269         }
18270         var k = String.fromCharCode(e.getKey()).toUpperCase();
18271         //Roo.log(k);
18272         var match  = false;
18273         var csel = this.view.getSelectedNodes();
18274         var cselitem = false;
18275         if (csel.length) {
18276             var ix = this.view.indexOf(csel[0]);
18277             cselitem  = this.store.getAt(ix);
18278             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18279                 cselitem = false;
18280             }
18281             
18282         }
18283         
18284         this.store.each(function(v) { 
18285             if (cselitem) {
18286                 // start at existing selection.
18287                 if (cselitem.id == v.id) {
18288                     cselitem = false;
18289                 }
18290                 return true;
18291             }
18292                 
18293             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18294                 match = this.store.indexOf(v);
18295                 return false;
18296             }
18297             return true;
18298         }, this);
18299         
18300         if (match === false) {
18301             return true; // no more action?
18302         }
18303         // scroll to?
18304         this.view.select(match);
18305         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18306         sn.scrollIntoView(sn.dom.parentNode, false);
18307     },
18308     
18309     onViewScroll : function(e, t){
18310         
18311         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){
18312             return;
18313         }
18314         
18315         this.hasQuery = true;
18316         
18317         this.loading = this.list.select('.loading', true).first();
18318         
18319         if(this.loading === null){
18320             this.list.createChild({
18321                 tag: 'div',
18322                 cls: 'loading roo-select2-more-results roo-select2-active',
18323                 html: 'Loading more results...'
18324             });
18325             
18326             this.loading = this.list.select('.loading', true).first();
18327             
18328             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18329             
18330             this.loading.hide();
18331         }
18332         
18333         this.loading.show();
18334         
18335         var _combo = this;
18336         
18337         this.page++;
18338         this.loadNext = true;
18339         
18340         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18341         
18342         return;
18343     },
18344     
18345     addItem : function(o)
18346     {   
18347         var dv = ''; // display value
18348         
18349         if (this.displayField) {
18350             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18351         } else {
18352             // this is an error condition!!!
18353             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18354         }
18355         
18356         if(!dv.length){
18357             return;
18358         }
18359         
18360         var choice = this.choices.createChild({
18361             tag: 'li',
18362             cls: 'roo-select2-search-choice',
18363             cn: [
18364                 {
18365                     tag: 'div',
18366                     html: dv
18367                 },
18368                 {
18369                     tag: 'a',
18370                     href: '#',
18371                     cls: 'roo-select2-search-choice-close fa fa-times',
18372                     tabindex: '-1'
18373                 }
18374             ]
18375             
18376         }, this.searchField);
18377         
18378         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18379         
18380         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18381         
18382         this.item.push(o);
18383         
18384         this.lastData = o;
18385         
18386         this.syncValue();
18387         
18388         this.inputEl().dom.value = '';
18389         
18390         this.validate();
18391     },
18392     
18393     onRemoveItem : function(e, _self, o)
18394     {
18395         e.preventDefault();
18396         
18397         this.lastItem = Roo.apply([], this.item);
18398         
18399         var index = this.item.indexOf(o.data) * 1;
18400         
18401         if( index < 0){
18402             Roo.log('not this item?!');
18403             return;
18404         }
18405         
18406         this.item.splice(index, 1);
18407         o.item.remove();
18408         
18409         this.syncValue();
18410         
18411         this.fireEvent('remove', this, e);
18412         
18413         this.validate();
18414         
18415     },
18416     
18417     syncValue : function()
18418     {
18419         if(!this.item.length){
18420             this.clearValue();
18421             return;
18422         }
18423             
18424         var value = [];
18425         var _this = this;
18426         Roo.each(this.item, function(i){
18427             if(_this.valueField){
18428                 value.push(i[_this.valueField]);
18429                 return;
18430             }
18431
18432             value.push(i);
18433         });
18434
18435         this.value = value.join(',');
18436
18437         if(this.hiddenField){
18438             this.hiddenField.dom.value = this.value;
18439         }
18440         
18441         this.store.fireEvent("datachanged", this.store);
18442         
18443         this.validate();
18444     },
18445     
18446     clearItem : function()
18447     {
18448         if(!this.multiple){
18449             return;
18450         }
18451         
18452         this.item = [];
18453         
18454         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18455            c.remove();
18456         });
18457         
18458         this.syncValue();
18459         
18460         this.validate();
18461         
18462         if(this.tickable && !Roo.isTouch){
18463             this.view.refresh();
18464         }
18465     },
18466     
18467     inputEl: function ()
18468     {
18469         if(Roo.isIOS && this.useNativeIOS){
18470             return this.el.select('select.roo-ios-select', true).first();
18471         }
18472         
18473         if(Roo.isTouch && this.mobileTouchView){
18474             return this.el.select('input.form-control',true).first();
18475         }
18476         
18477         if(this.tickable){
18478             return this.searchField;
18479         }
18480         
18481         return this.el.select('input.form-control',true).first();
18482     },
18483     
18484     onTickableFooterButtonClick : function(e, btn, el)
18485     {
18486         e.preventDefault();
18487         
18488         this.lastItem = Roo.apply([], this.item);
18489         
18490         if(btn && btn.name == 'cancel'){
18491             this.tickItems = Roo.apply([], this.item);
18492             this.collapse();
18493             return;
18494         }
18495         
18496         this.clearItem();
18497         
18498         var _this = this;
18499         
18500         Roo.each(this.tickItems, function(o){
18501             _this.addItem(o);
18502         });
18503         
18504         this.collapse();
18505         
18506     },
18507     
18508     validate : function()
18509     {
18510         if(this.getVisibilityEl().hasClass('hidden')){
18511             return true;
18512         }
18513         
18514         var v = this.getRawValue();
18515         
18516         if(this.multiple){
18517             v = this.getValue();
18518         }
18519         
18520         if(this.disabled || this.allowBlank || v.length){
18521             this.markValid();
18522             return true;
18523         }
18524         
18525         this.markInvalid();
18526         return false;
18527     },
18528     
18529     tickableInputEl : function()
18530     {
18531         if(!this.tickable || !this.editable){
18532             return this.inputEl();
18533         }
18534         
18535         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18536     },
18537     
18538     
18539     getAutoCreateTouchView : function()
18540     {
18541         var id = Roo.id();
18542         
18543         var cfg = {
18544             cls: 'form-group' //input-group
18545         };
18546         
18547         var input =  {
18548             tag: 'input',
18549             id : id,
18550             type : this.inputType,
18551             cls : 'form-control x-combo-noedit',
18552             autocomplete: 'new-password',
18553             placeholder : this.placeholder || '',
18554             readonly : true
18555         };
18556         
18557         if (this.name) {
18558             input.name = this.name;
18559         }
18560         
18561         if (this.size) {
18562             input.cls += ' input-' + this.size;
18563         }
18564         
18565         if (this.disabled) {
18566             input.disabled = true;
18567         }
18568         
18569         var inputblock = {
18570             cls : 'roo-combobox-wrap',
18571             cn : [
18572                 input
18573             ]
18574         };
18575         
18576         if(this.before){
18577             inputblock.cls += ' input-group';
18578             
18579             inputblock.cn.unshift({
18580                 tag :'span',
18581                 cls : 'input-group-addon input-group-prepend input-group-text',
18582                 html : this.before
18583             });
18584         }
18585         
18586         if(this.removable && !this.multiple){
18587             inputblock.cls += ' roo-removable';
18588             
18589             inputblock.cn.push({
18590                 tag: 'button',
18591                 html : 'x',
18592                 cls : 'roo-combo-removable-btn close'
18593             });
18594         }
18595
18596         if(this.hasFeedback && !this.allowBlank){
18597             
18598             inputblock.cls += ' has-feedback';
18599             
18600             inputblock.cn.push({
18601                 tag: 'span',
18602                 cls: 'glyphicon form-control-feedback'
18603             });
18604             
18605         }
18606         
18607         if (this.after) {
18608             
18609             inputblock.cls += (this.before) ? '' : ' input-group';
18610             
18611             inputblock.cn.push({
18612                 tag :'span',
18613                 cls : 'input-group-addon input-group-append input-group-text',
18614                 html : this.after
18615             });
18616         }
18617
18618         
18619         var ibwrap = inputblock;
18620         
18621         if(this.multiple){
18622             ibwrap = {
18623                 tag: 'ul',
18624                 cls: 'roo-select2-choices',
18625                 cn:[
18626                     {
18627                         tag: 'li',
18628                         cls: 'roo-select2-search-field',
18629                         cn: [
18630
18631                             inputblock
18632                         ]
18633                     }
18634                 ]
18635             };
18636         
18637             
18638         }
18639         
18640         var combobox = {
18641             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18642             cn: [
18643                 {
18644                     tag: 'input',
18645                     type : 'hidden',
18646                     cls: 'form-hidden-field'
18647                 },
18648                 ibwrap
18649             ]
18650         };
18651         
18652         if(!this.multiple && this.showToggleBtn){
18653             
18654             var caret = {
18655                 cls: 'caret'
18656             };
18657             
18658             if (this.caret != false) {
18659                 caret = {
18660                      tag: 'i',
18661                      cls: 'fa fa-' + this.caret
18662                 };
18663                 
18664             }
18665             
18666             combobox.cn.push({
18667                 tag :'span',
18668                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18669                 cn : [
18670                     Roo.bootstrap.version == 3 ? caret : '',
18671                     {
18672                         tag: 'span',
18673                         cls: 'combobox-clear',
18674                         cn  : [
18675                             {
18676                                 tag : 'i',
18677                                 cls: 'icon-remove'
18678                             }
18679                         ]
18680                     }
18681                 ]
18682
18683             })
18684         }
18685         
18686         if(this.multiple){
18687             combobox.cls += ' roo-select2-container-multi';
18688         }
18689         
18690         var required =  this.allowBlank ?  {
18691                     tag : 'i',
18692                     style: 'display: none'
18693                 } : {
18694                    tag : 'i',
18695                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18696                    tooltip : 'This field is required'
18697                 };
18698         
18699         var align = this.labelAlign || this.parentLabelAlign();
18700         
18701         if (align ==='left' && this.fieldLabel.length) {
18702
18703             cfg.cn = [
18704                 required,
18705                 {
18706                     tag: 'label',
18707                     cls : 'control-label col-form-label',
18708                     html : this.fieldLabel
18709
18710                 },
18711                 {
18712                     cls : 'roo-combobox-wrap ', 
18713                     cn: [
18714                         combobox
18715                     ]
18716                 }
18717             ];
18718             
18719             var labelCfg = cfg.cn[1];
18720             var contentCfg = cfg.cn[2];
18721             
18722
18723             if(this.indicatorpos == 'right'){
18724                 cfg.cn = [
18725                     {
18726                         tag: 'label',
18727                         'for' :  id,
18728                         cls : 'control-label col-form-label',
18729                         cn : [
18730                             {
18731                                 tag : 'span',
18732                                 html : this.fieldLabel
18733                             },
18734                             required
18735                         ]
18736                     },
18737                     {
18738                         cls : "roo-combobox-wrap ",
18739                         cn: [
18740                             combobox
18741                         ]
18742                     }
18743
18744                 ];
18745                 
18746                 labelCfg = cfg.cn[0];
18747                 contentCfg = cfg.cn[1];
18748             }
18749             
18750            
18751             
18752             if(this.labelWidth > 12){
18753                 labelCfg.style = "width: " + this.labelWidth + 'px';
18754             }
18755            
18756             if(this.labelWidth < 13 && this.labelmd == 0){
18757                 this.labelmd = this.labelWidth;
18758             }
18759             
18760             if(this.labellg > 0){
18761                 labelCfg.cls += ' col-lg-' + this.labellg;
18762                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18763             }
18764             
18765             if(this.labelmd > 0){
18766                 labelCfg.cls += ' col-md-' + this.labelmd;
18767                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18768             }
18769             
18770             if(this.labelsm > 0){
18771                 labelCfg.cls += ' col-sm-' + this.labelsm;
18772                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18773             }
18774             
18775             if(this.labelxs > 0){
18776                 labelCfg.cls += ' col-xs-' + this.labelxs;
18777                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18778             }
18779                 
18780                 
18781         } else if ( this.fieldLabel.length) {
18782             cfg.cn = [
18783                required,
18784                 {
18785                     tag: 'label',
18786                     cls : 'control-label',
18787                     html : this.fieldLabel
18788
18789                 },
18790                 {
18791                     cls : '', 
18792                     cn: [
18793                         combobox
18794                     ]
18795                 }
18796             ];
18797             
18798             if(this.indicatorpos == 'right'){
18799                 cfg.cn = [
18800                     {
18801                         tag: 'label',
18802                         cls : 'control-label',
18803                         html : this.fieldLabel,
18804                         cn : [
18805                             required
18806                         ]
18807                     },
18808                     {
18809                         cls : '', 
18810                         cn: [
18811                             combobox
18812                         ]
18813                     }
18814                 ];
18815             }
18816         } else {
18817             cfg.cn = combobox;    
18818         }
18819         
18820         
18821         var settings = this;
18822         
18823         ['xs','sm','md','lg'].map(function(size){
18824             if (settings[size]) {
18825                 cfg.cls += ' col-' + size + '-' + settings[size];
18826             }
18827         });
18828         
18829         return cfg;
18830     },
18831     
18832     initTouchView : function()
18833     {
18834         this.renderTouchView();
18835         
18836         this.touchViewEl.on('scroll', function(){
18837             this.el.dom.scrollTop = 0;
18838         }, this);
18839         
18840         this.originalValue = this.getValue();
18841         
18842         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18843         
18844         this.inputEl().on("click", this.showTouchView, this);
18845         if (this.triggerEl) {
18846             this.triggerEl.on("click", this.showTouchView, this);
18847         }
18848         
18849         
18850         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18851         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18852         
18853         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18854         
18855         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18856         this.store.on('load', this.onTouchViewLoad, this);
18857         this.store.on('loadexception', this.onTouchViewLoadException, this);
18858         
18859         if(this.hiddenName){
18860             
18861             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18862             
18863             this.hiddenField.dom.value =
18864                 this.hiddenValue !== undefined ? this.hiddenValue :
18865                 this.value !== undefined ? this.value : '';
18866         
18867             this.el.dom.removeAttribute('name');
18868             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18869         }
18870         
18871         if(this.multiple){
18872             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18873             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18874         }
18875         
18876         if(this.removable && !this.multiple){
18877             var close = this.closeTriggerEl();
18878             if(close){
18879                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18880                 close.on('click', this.removeBtnClick, this, close);
18881             }
18882         }
18883         /*
18884          * fix the bug in Safari iOS8
18885          */
18886         this.inputEl().on("focus", function(e){
18887             document.activeElement.blur();
18888         }, this);
18889         
18890         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18891         
18892         return;
18893         
18894         
18895     },
18896     
18897     renderTouchView : function()
18898     {
18899         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18900         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901         
18902         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18903         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18904         
18905         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18906         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18907         this.touchViewBodyEl.setStyle('overflow', 'auto');
18908         
18909         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18910         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18911         
18912         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18913         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18914         
18915     },
18916     
18917     showTouchView : function()
18918     {
18919         if(this.disabled){
18920             return;
18921         }
18922         
18923         this.touchViewHeaderEl.hide();
18924
18925         if(this.modalTitle.length){
18926             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18927             this.touchViewHeaderEl.show();
18928         }
18929
18930         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18931         this.touchViewEl.show();
18932
18933         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18934         
18935         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18936         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18937
18938         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18939
18940         if(this.modalTitle.length){
18941             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18942         }
18943         
18944         this.touchViewBodyEl.setHeight(bodyHeight);
18945
18946         if(this.animate){
18947             var _this = this;
18948             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18949         }else{
18950             this.touchViewEl.addClass(['in','show']);
18951         }
18952         
18953         if(this._touchViewMask){
18954             Roo.get(document.body).addClass("x-body-masked");
18955             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18956             this._touchViewMask.setStyle('z-index', 10000);
18957             this._touchViewMask.addClass('show');
18958         }
18959         
18960         this.doTouchViewQuery();
18961         
18962     },
18963     
18964     hideTouchView : function()
18965     {
18966         this.touchViewEl.removeClass(['in','show']);
18967
18968         if(this.animate){
18969             var _this = this;
18970             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18971         }else{
18972             this.touchViewEl.setStyle('display', 'none');
18973         }
18974         
18975         if(this._touchViewMask){
18976             this._touchViewMask.removeClass('show');
18977             Roo.get(document.body).removeClass("x-body-masked");
18978         }
18979     },
18980     
18981     setTouchViewValue : function()
18982     {
18983         if(this.multiple){
18984             this.clearItem();
18985         
18986             var _this = this;
18987
18988             Roo.each(this.tickItems, function(o){
18989                 this.addItem(o);
18990             }, this);
18991         }
18992         
18993         this.hideTouchView();
18994     },
18995     
18996     doTouchViewQuery : function()
18997     {
18998         var qe = {
18999             query: '',
19000             forceAll: true,
19001             combo: this,
19002             cancel:false
19003         };
19004         
19005         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19006             return false;
19007         }
19008         
19009         if(!this.alwaysQuery || this.mode == 'local'){
19010             this.onTouchViewLoad();
19011             return;
19012         }
19013         
19014         this.store.load();
19015     },
19016     
19017     onTouchViewBeforeLoad : function(combo,opts)
19018     {
19019         return;
19020     },
19021
19022     // private
19023     onTouchViewLoad : function()
19024     {
19025         if(this.store.getCount() < 1){
19026             this.onTouchViewEmptyResults();
19027             return;
19028         }
19029         
19030         this.clearTouchView();
19031         
19032         var rawValue = this.getRawValue();
19033         
19034         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19035         
19036         this.tickItems = [];
19037         
19038         this.store.data.each(function(d, rowIndex){
19039             var row = this.touchViewListGroup.createChild(template);
19040             
19041             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19042                 row.addClass(d.data.cls);
19043             }
19044             
19045             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19046                 var cfg = {
19047                     data : d.data,
19048                     html : d.data[this.displayField]
19049                 };
19050                 
19051                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19052                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19053                 }
19054             }
19055             row.removeClass('selected');
19056             if(!this.multiple && this.valueField &&
19057                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19058             {
19059                 // radio buttons..
19060                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19061                 row.addClass('selected');
19062             }
19063             
19064             if(this.multiple && this.valueField &&
19065                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19066             {
19067                 
19068                 // checkboxes...
19069                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19070                 this.tickItems.push(d.data);
19071             }
19072             
19073             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19074             
19075         }, this);
19076         
19077         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19078         
19079         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19080
19081         if(this.modalTitle.length){
19082             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19083         }
19084
19085         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19086         
19087         if(this.mobile_restrict_height && listHeight < bodyHeight){
19088             this.touchViewBodyEl.setHeight(listHeight);
19089         }
19090         
19091         var _this = this;
19092         
19093         if(firstChecked && listHeight > bodyHeight){
19094             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19095         }
19096         
19097     },
19098     
19099     onTouchViewLoadException : function()
19100     {
19101         this.hideTouchView();
19102     },
19103     
19104     onTouchViewEmptyResults : function()
19105     {
19106         this.clearTouchView();
19107         
19108         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19109         
19110         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19111         
19112     },
19113     
19114     clearTouchView : function()
19115     {
19116         this.touchViewListGroup.dom.innerHTML = '';
19117     },
19118     
19119     onTouchViewClick : function(e, el, o)
19120     {
19121         e.preventDefault();
19122         
19123         var row = o.row;
19124         var rowIndex = o.rowIndex;
19125         
19126         var r = this.store.getAt(rowIndex);
19127         
19128         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19129             
19130             if(!this.multiple){
19131                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19132                     c.dom.removeAttribute('checked');
19133                 }, this);
19134
19135                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19136
19137                 this.setFromData(r.data);
19138
19139                 var close = this.closeTriggerEl();
19140
19141                 if(close){
19142                     close.show();
19143                 }
19144
19145                 this.hideTouchView();
19146
19147                 this.fireEvent('select', this, r, rowIndex);
19148
19149                 return;
19150             }
19151
19152             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19153                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19154                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19155                 return;
19156             }
19157
19158             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19159             this.addItem(r.data);
19160             this.tickItems.push(r.data);
19161         }
19162     },
19163     
19164     getAutoCreateNativeIOS : function()
19165     {
19166         var cfg = {
19167             cls: 'form-group' //input-group,
19168         };
19169         
19170         var combobox =  {
19171             tag: 'select',
19172             cls : 'roo-ios-select'
19173         };
19174         
19175         if (this.name) {
19176             combobox.name = this.name;
19177         }
19178         
19179         if (this.disabled) {
19180             combobox.disabled = true;
19181         }
19182         
19183         var settings = this;
19184         
19185         ['xs','sm','md','lg'].map(function(size){
19186             if (settings[size]) {
19187                 cfg.cls += ' col-' + size + '-' + settings[size];
19188             }
19189         });
19190         
19191         cfg.cn = combobox;
19192         
19193         return cfg;
19194         
19195     },
19196     
19197     initIOSView : function()
19198     {
19199         this.store.on('load', this.onIOSViewLoad, this);
19200         
19201         return;
19202     },
19203     
19204     onIOSViewLoad : function()
19205     {
19206         if(this.store.getCount() < 1){
19207             return;
19208         }
19209         
19210         this.clearIOSView();
19211         
19212         if(this.allowBlank) {
19213             
19214             var default_text = '-- SELECT --';
19215             
19216             if(this.placeholder.length){
19217                 default_text = this.placeholder;
19218             }
19219             
19220             if(this.emptyTitle.length){
19221                 default_text += ' - ' + this.emptyTitle + ' -';
19222             }
19223             
19224             var opt = this.inputEl().createChild({
19225                 tag: 'option',
19226                 value : 0,
19227                 html : default_text
19228             });
19229             
19230             var o = {};
19231             o[this.valueField] = 0;
19232             o[this.displayField] = default_text;
19233             
19234             this.ios_options.push({
19235                 data : o,
19236                 el : opt
19237             });
19238             
19239         }
19240         
19241         this.store.data.each(function(d, rowIndex){
19242             
19243             var html = '';
19244             
19245             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19246                 html = d.data[this.displayField];
19247             }
19248             
19249             var value = '';
19250             
19251             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19252                 value = d.data[this.valueField];
19253             }
19254             
19255             var option = {
19256                 tag: 'option',
19257                 value : value,
19258                 html : html
19259             };
19260             
19261             if(this.value == d.data[this.valueField]){
19262                 option['selected'] = true;
19263             }
19264             
19265             var opt = this.inputEl().createChild(option);
19266             
19267             this.ios_options.push({
19268                 data : d.data,
19269                 el : opt
19270             });
19271             
19272         }, this);
19273         
19274         this.inputEl().on('change', function(){
19275            this.fireEvent('select', this);
19276         }, this);
19277         
19278     },
19279     
19280     clearIOSView: function()
19281     {
19282         this.inputEl().dom.innerHTML = '';
19283         
19284         this.ios_options = [];
19285     },
19286     
19287     setIOSValue: function(v)
19288     {
19289         this.value = v;
19290         
19291         if(!this.ios_options){
19292             return;
19293         }
19294         
19295         Roo.each(this.ios_options, function(opts){
19296            
19297            opts.el.dom.removeAttribute('selected');
19298            
19299            if(opts.data[this.valueField] != v){
19300                return;
19301            }
19302            
19303            opts.el.dom.setAttribute('selected', true);
19304            
19305         }, this);
19306     }
19307
19308     /** 
19309     * @cfg {Boolean} grow 
19310     * @hide 
19311     */
19312     /** 
19313     * @cfg {Number} growMin 
19314     * @hide 
19315     */
19316     /** 
19317     * @cfg {Number} growMax 
19318     * @hide 
19319     */
19320     /**
19321      * @hide
19322      * @method autoSize
19323      */
19324 });
19325
19326 Roo.apply(Roo.bootstrap.ComboBox,  {
19327     
19328     header : {
19329         tag: 'div',
19330         cls: 'modal-header',
19331         cn: [
19332             {
19333                 tag: 'h4',
19334                 cls: 'modal-title'
19335             }
19336         ]
19337     },
19338     
19339     body : {
19340         tag: 'div',
19341         cls: 'modal-body',
19342         cn: [
19343             {
19344                 tag: 'ul',
19345                 cls: 'list-group'
19346             }
19347         ]
19348     },
19349     
19350     listItemRadio : {
19351         tag: 'li',
19352         cls: 'list-group-item',
19353         cn: [
19354             {
19355                 tag: 'span',
19356                 cls: 'roo-combobox-list-group-item-value'
19357             },
19358             {
19359                 tag: 'div',
19360                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19361                 cn: [
19362                     {
19363                         tag: 'input',
19364                         type: 'radio'
19365                     },
19366                     {
19367                         tag: 'label'
19368                     }
19369                 ]
19370             }
19371         ]
19372     },
19373     
19374     listItemCheckbox : {
19375         tag: 'li',
19376         cls: 'list-group-item',
19377         cn: [
19378             {
19379                 tag: 'span',
19380                 cls: 'roo-combobox-list-group-item-value'
19381             },
19382             {
19383                 tag: 'div',
19384                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19385                 cn: [
19386                     {
19387                         tag: 'input',
19388                         type: 'checkbox'
19389                     },
19390                     {
19391                         tag: 'label'
19392                     }
19393                 ]
19394             }
19395         ]
19396     },
19397     
19398     emptyResult : {
19399         tag: 'div',
19400         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19401     },
19402     
19403     footer : {
19404         tag: 'div',
19405         cls: 'modal-footer',
19406         cn: [
19407             {
19408                 tag: 'div',
19409                 cls: 'row',
19410                 cn: [
19411                     {
19412                         tag: 'div',
19413                         cls: 'col-xs-6 text-left',
19414                         cn: {
19415                             tag: 'button',
19416                             cls: 'btn btn-danger roo-touch-view-cancel',
19417                             html: 'Cancel'
19418                         }
19419                     },
19420                     {
19421                         tag: 'div',
19422                         cls: 'col-xs-6 text-right',
19423                         cn: {
19424                             tag: 'button',
19425                             cls: 'btn btn-success roo-touch-view-ok',
19426                             html: 'OK'
19427                         }
19428                     }
19429                 ]
19430             }
19431         ]
19432         
19433     }
19434 });
19435
19436 Roo.apply(Roo.bootstrap.ComboBox,  {
19437     
19438     touchViewTemplate : {
19439         tag: 'div',
19440         cls: 'modal fade roo-combobox-touch-view',
19441         cn: [
19442             {
19443                 tag: 'div',
19444                 cls: 'modal-dialog',
19445                 style : 'position:fixed', // we have to fix position....
19446                 cn: [
19447                     {
19448                         tag: 'div',
19449                         cls: 'modal-content',
19450                         cn: [
19451                             Roo.bootstrap.ComboBox.header,
19452                             Roo.bootstrap.ComboBox.body,
19453                             Roo.bootstrap.ComboBox.footer
19454                         ]
19455                     }
19456                 ]
19457             }
19458         ]
19459     }
19460 });/*
19461  * Based on:
19462  * Ext JS Library 1.1.1
19463  * Copyright(c) 2006-2007, Ext JS, LLC.
19464  *
19465  * Originally Released Under LGPL - original licence link has changed is not relivant.
19466  *
19467  * Fork - LGPL
19468  * <script type="text/javascript">
19469  */
19470
19471 /**
19472  * @class Roo.View
19473  * @extends Roo.util.Observable
19474  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19475  * This class also supports single and multi selection modes. <br>
19476  * Create a data model bound view:
19477  <pre><code>
19478  var store = new Roo.data.Store(...);
19479
19480  var view = new Roo.View({
19481     el : "my-element",
19482     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19483  
19484     singleSelect: true,
19485     selectedClass: "ydataview-selected",
19486     store: store
19487  });
19488
19489  // listen for node click?
19490  view.on("click", function(vw, index, node, e){
19491  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19492  });
19493
19494  // load XML data
19495  dataModel.load("foobar.xml");
19496  </code></pre>
19497  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19498  * <br><br>
19499  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19500  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19501  * 
19502  * Note: old style constructor is still suported (container, template, config)
19503  * 
19504  * @constructor
19505  * Create a new View
19506  * @param {Object} config The config object
19507  * 
19508  */
19509 Roo.View = function(config, depreciated_tpl, depreciated_config){
19510     
19511     this.parent = false;
19512     
19513     if (typeof(depreciated_tpl) == 'undefined') {
19514         // new way.. - universal constructor.
19515         Roo.apply(this, config);
19516         this.el  = Roo.get(this.el);
19517     } else {
19518         // old format..
19519         this.el  = Roo.get(config);
19520         this.tpl = depreciated_tpl;
19521         Roo.apply(this, depreciated_config);
19522     }
19523     this.wrapEl  = this.el.wrap().wrap();
19524     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19525     
19526     
19527     if(typeof(this.tpl) == "string"){
19528         this.tpl = new Roo.Template(this.tpl);
19529     } else {
19530         // support xtype ctors..
19531         this.tpl = new Roo.factory(this.tpl, Roo);
19532     }
19533     
19534     
19535     this.tpl.compile();
19536     
19537     /** @private */
19538     this.addEvents({
19539         /**
19540          * @event beforeclick
19541          * Fires before a click is processed. Returns false to cancel the default action.
19542          * @param {Roo.View} this
19543          * @param {Number} index The index of the target node
19544          * @param {HTMLElement} node The target node
19545          * @param {Roo.EventObject} e The raw event object
19546          */
19547             "beforeclick" : true,
19548         /**
19549          * @event click
19550          * Fires when a template node is clicked.
19551          * @param {Roo.View} this
19552          * @param {Number} index The index of the target node
19553          * @param {HTMLElement} node The target node
19554          * @param {Roo.EventObject} e The raw event object
19555          */
19556             "click" : true,
19557         /**
19558          * @event dblclick
19559          * Fires when a template node is double clicked.
19560          * @param {Roo.View} this
19561          * @param {Number} index The index of the target node
19562          * @param {HTMLElement} node The target node
19563          * @param {Roo.EventObject} e The raw event object
19564          */
19565             "dblclick" : true,
19566         /**
19567          * @event contextmenu
19568          * Fires when a template node is right clicked.
19569          * @param {Roo.View} this
19570          * @param {Number} index The index of the target node
19571          * @param {HTMLElement} node The target node
19572          * @param {Roo.EventObject} e The raw event object
19573          */
19574             "contextmenu" : true,
19575         /**
19576          * @event selectionchange
19577          * Fires when the selected nodes change.
19578          * @param {Roo.View} this
19579          * @param {Array} selections Array of the selected nodes
19580          */
19581             "selectionchange" : true,
19582     
19583         /**
19584          * @event beforeselect
19585          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19586          * @param {Roo.View} this
19587          * @param {HTMLElement} node The node to be selected
19588          * @param {Array} selections Array of currently selected nodes
19589          */
19590             "beforeselect" : true,
19591         /**
19592          * @event preparedata
19593          * Fires on every row to render, to allow you to change the data.
19594          * @param {Roo.View} this
19595          * @param {Object} data to be rendered (change this)
19596          */
19597           "preparedata" : true
19598           
19599           
19600         });
19601
19602
19603
19604     this.el.on({
19605         "click": this.onClick,
19606         "dblclick": this.onDblClick,
19607         "contextmenu": this.onContextMenu,
19608         scope:this
19609     });
19610
19611     this.selections = [];
19612     this.nodes = [];
19613     this.cmp = new Roo.CompositeElementLite([]);
19614     if(this.store){
19615         this.store = Roo.factory(this.store, Roo.data);
19616         this.setStore(this.store, true);
19617     }
19618     
19619     if ( this.footer && this.footer.xtype) {
19620            
19621          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19622         
19623         this.footer.dataSource = this.store;
19624         this.footer.container = fctr;
19625         this.footer = Roo.factory(this.footer, Roo);
19626         fctr.insertFirst(this.el);
19627         
19628         // this is a bit insane - as the paging toolbar seems to detach the el..
19629 //        dom.parentNode.parentNode.parentNode
19630          // they get detached?
19631     }
19632     
19633     
19634     Roo.View.superclass.constructor.call(this);
19635     
19636     
19637 };
19638
19639 Roo.extend(Roo.View, Roo.util.Observable, {
19640     
19641      /**
19642      * @cfg {Roo.data.Store} store Data store to load data from.
19643      */
19644     store : false,
19645     
19646     /**
19647      * @cfg {String|Roo.Element} el The container element.
19648      */
19649     el : '',
19650     
19651     /**
19652      * @cfg {String|Roo.Template} tpl The template used by this View 
19653      */
19654     tpl : false,
19655     /**
19656      * @cfg {String} dataName the named area of the template to use as the data area
19657      *                          Works with domtemplates roo-name="name"
19658      */
19659     dataName: false,
19660     /**
19661      * @cfg {String} selectedClass The css class to add to selected nodes
19662      */
19663     selectedClass : "x-view-selected",
19664      /**
19665      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19666      */
19667     emptyText : "",
19668     
19669     /**
19670      * @cfg {String} text to display on mask (default Loading)
19671      */
19672     mask : false,
19673     /**
19674      * @cfg {Boolean} multiSelect Allow multiple selection
19675      */
19676     multiSelect : false,
19677     /**
19678      * @cfg {Boolean} singleSelect Allow single selection
19679      */
19680     singleSelect:  false,
19681     
19682     /**
19683      * @cfg {Boolean} toggleSelect - selecting 
19684      */
19685     toggleSelect : false,
19686     
19687     /**
19688      * @cfg {Boolean} tickable - selecting 
19689      */
19690     tickable : false,
19691     
19692     /**
19693      * Returns the element this view is bound to.
19694      * @return {Roo.Element}
19695      */
19696     getEl : function(){
19697         return this.wrapEl;
19698     },
19699     
19700     
19701
19702     /**
19703      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19704      */
19705     refresh : function(){
19706         //Roo.log('refresh');
19707         var t = this.tpl;
19708         
19709         // if we are using something like 'domtemplate', then
19710         // the what gets used is:
19711         // t.applySubtemplate(NAME, data, wrapping data..)
19712         // the outer template then get' applied with
19713         //     the store 'extra data'
19714         // and the body get's added to the
19715         //      roo-name="data" node?
19716         //      <span class='roo-tpl-{name}'></span> ?????
19717         
19718         
19719         
19720         this.clearSelections();
19721         this.el.update("");
19722         var html = [];
19723         var records = this.store.getRange();
19724         if(records.length < 1) {
19725             
19726             // is this valid??  = should it render a template??
19727             
19728             this.el.update(this.emptyText);
19729             return;
19730         }
19731         var el = this.el;
19732         if (this.dataName) {
19733             this.el.update(t.apply(this.store.meta)); //????
19734             el = this.el.child('.roo-tpl-' + this.dataName);
19735         }
19736         
19737         for(var i = 0, len = records.length; i < len; i++){
19738             var data = this.prepareData(records[i].data, i, records[i]);
19739             this.fireEvent("preparedata", this, data, i, records[i]);
19740             
19741             var d = Roo.apply({}, data);
19742             
19743             if(this.tickable){
19744                 Roo.apply(d, {'roo-id' : Roo.id()});
19745                 
19746                 var _this = this;
19747             
19748                 Roo.each(this.parent.item, function(item){
19749                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19750                         return;
19751                     }
19752                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19753                 });
19754             }
19755             
19756             html[html.length] = Roo.util.Format.trim(
19757                 this.dataName ?
19758                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19759                     t.apply(d)
19760             );
19761         }
19762         
19763         
19764         
19765         el.update(html.join(""));
19766         this.nodes = el.dom.childNodes;
19767         this.updateIndexes(0);
19768     },
19769     
19770
19771     /**
19772      * Function to override to reformat the data that is sent to
19773      * the template for each node.
19774      * DEPRICATED - use the preparedata event handler.
19775      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19776      * a JSON object for an UpdateManager bound view).
19777      */
19778     prepareData : function(data, index, record)
19779     {
19780         this.fireEvent("preparedata", this, data, index, record);
19781         return data;
19782     },
19783
19784     onUpdate : function(ds, record){
19785         // Roo.log('on update');   
19786         this.clearSelections();
19787         var index = this.store.indexOf(record);
19788         var n = this.nodes[index];
19789         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19790         n.parentNode.removeChild(n);
19791         this.updateIndexes(index, index);
19792     },
19793
19794     
19795     
19796 // --------- FIXME     
19797     onAdd : function(ds, records, index)
19798     {
19799         //Roo.log(['on Add', ds, records, index] );        
19800         this.clearSelections();
19801         if(this.nodes.length == 0){
19802             this.refresh();
19803             return;
19804         }
19805         var n = this.nodes[index];
19806         for(var i = 0, len = records.length; i < len; i++){
19807             var d = this.prepareData(records[i].data, i, records[i]);
19808             if(n){
19809                 this.tpl.insertBefore(n, d);
19810             }else{
19811                 
19812                 this.tpl.append(this.el, d);
19813             }
19814         }
19815         this.updateIndexes(index);
19816     },
19817
19818     onRemove : function(ds, record, index){
19819        // Roo.log('onRemove');
19820         this.clearSelections();
19821         var el = this.dataName  ?
19822             this.el.child('.roo-tpl-' + this.dataName) :
19823             this.el; 
19824         
19825         el.dom.removeChild(this.nodes[index]);
19826         this.updateIndexes(index);
19827     },
19828
19829     /**
19830      * Refresh an individual node.
19831      * @param {Number} index
19832      */
19833     refreshNode : function(index){
19834         this.onUpdate(this.store, this.store.getAt(index));
19835     },
19836
19837     updateIndexes : function(startIndex, endIndex){
19838         var ns = this.nodes;
19839         startIndex = startIndex || 0;
19840         endIndex = endIndex || ns.length - 1;
19841         for(var i = startIndex; i <= endIndex; i++){
19842             ns[i].nodeIndex = i;
19843         }
19844     },
19845
19846     /**
19847      * Changes the data store this view uses and refresh the view.
19848      * @param {Store} store
19849      */
19850     setStore : function(store, initial){
19851         if(!initial && this.store){
19852             this.store.un("datachanged", this.refresh);
19853             this.store.un("add", this.onAdd);
19854             this.store.un("remove", this.onRemove);
19855             this.store.un("update", this.onUpdate);
19856             this.store.un("clear", this.refresh);
19857             this.store.un("beforeload", this.onBeforeLoad);
19858             this.store.un("load", this.onLoad);
19859             this.store.un("loadexception", this.onLoad);
19860         }
19861         if(store){
19862           
19863             store.on("datachanged", this.refresh, this);
19864             store.on("add", this.onAdd, this);
19865             store.on("remove", this.onRemove, this);
19866             store.on("update", this.onUpdate, this);
19867             store.on("clear", this.refresh, this);
19868             store.on("beforeload", this.onBeforeLoad, this);
19869             store.on("load", this.onLoad, this);
19870             store.on("loadexception", this.onLoad, this);
19871         }
19872         
19873         if(store){
19874             this.refresh();
19875         }
19876     },
19877     /**
19878      * onbeforeLoad - masks the loading area.
19879      *
19880      */
19881     onBeforeLoad : function(store,opts)
19882     {
19883          //Roo.log('onBeforeLoad');   
19884         if (!opts.add) {
19885             this.el.update("");
19886         }
19887         this.el.mask(this.mask ? this.mask : "Loading" ); 
19888     },
19889     onLoad : function ()
19890     {
19891         this.el.unmask();
19892     },
19893     
19894
19895     /**
19896      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19897      * @param {HTMLElement} node
19898      * @return {HTMLElement} The template node
19899      */
19900     findItemFromChild : function(node){
19901         var el = this.dataName  ?
19902             this.el.child('.roo-tpl-' + this.dataName,true) :
19903             this.el.dom; 
19904         
19905         if(!node || node.parentNode == el){
19906                     return node;
19907             }
19908             var p = node.parentNode;
19909             while(p && p != el){
19910             if(p.parentNode == el){
19911                 return p;
19912             }
19913             p = p.parentNode;
19914         }
19915             return null;
19916     },
19917
19918     /** @ignore */
19919     onClick : function(e){
19920         var item = this.findItemFromChild(e.getTarget());
19921         if(item){
19922             var index = this.indexOf(item);
19923             if(this.onItemClick(item, index, e) !== false){
19924                 this.fireEvent("click", this, index, item, e);
19925             }
19926         }else{
19927             this.clearSelections();
19928         }
19929     },
19930
19931     /** @ignore */
19932     onContextMenu : function(e){
19933         var item = this.findItemFromChild(e.getTarget());
19934         if(item){
19935             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19936         }
19937     },
19938
19939     /** @ignore */
19940     onDblClick : function(e){
19941         var item = this.findItemFromChild(e.getTarget());
19942         if(item){
19943             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19944         }
19945     },
19946
19947     onItemClick : function(item, index, e)
19948     {
19949         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19950             return false;
19951         }
19952         if (this.toggleSelect) {
19953             var m = this.isSelected(item) ? 'unselect' : 'select';
19954             //Roo.log(m);
19955             var _t = this;
19956             _t[m](item, true, false);
19957             return true;
19958         }
19959         if(this.multiSelect || this.singleSelect){
19960             if(this.multiSelect && e.shiftKey && this.lastSelection){
19961                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19962             }else{
19963                 this.select(item, this.multiSelect && e.ctrlKey);
19964                 this.lastSelection = item;
19965             }
19966             
19967             if(!this.tickable){
19968                 e.preventDefault();
19969             }
19970             
19971         }
19972         return true;
19973     },
19974
19975     /**
19976      * Get the number of selected nodes.
19977      * @return {Number}
19978      */
19979     getSelectionCount : function(){
19980         return this.selections.length;
19981     },
19982
19983     /**
19984      * Get the currently selected nodes.
19985      * @return {Array} An array of HTMLElements
19986      */
19987     getSelectedNodes : function(){
19988         return this.selections;
19989     },
19990
19991     /**
19992      * Get the indexes of the selected nodes.
19993      * @return {Array}
19994      */
19995     getSelectedIndexes : function(){
19996         var indexes = [], s = this.selections;
19997         for(var i = 0, len = s.length; i < len; i++){
19998             indexes.push(s[i].nodeIndex);
19999         }
20000         return indexes;
20001     },
20002
20003     /**
20004      * Clear all selections
20005      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20006      */
20007     clearSelections : function(suppressEvent){
20008         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20009             this.cmp.elements = this.selections;
20010             this.cmp.removeClass(this.selectedClass);
20011             this.selections = [];
20012             if(!suppressEvent){
20013                 this.fireEvent("selectionchange", this, this.selections);
20014             }
20015         }
20016     },
20017
20018     /**
20019      * Returns true if the passed node is selected
20020      * @param {HTMLElement/Number} node The node or node index
20021      * @return {Boolean}
20022      */
20023     isSelected : function(node){
20024         var s = this.selections;
20025         if(s.length < 1){
20026             return false;
20027         }
20028         node = this.getNode(node);
20029         return s.indexOf(node) !== -1;
20030     },
20031
20032     /**
20033      * Selects nodes.
20034      * @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
20035      * @param {Boolean} keepExisting (optional) true to keep existing selections
20036      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20037      */
20038     select : function(nodeInfo, keepExisting, suppressEvent){
20039         if(nodeInfo instanceof Array){
20040             if(!keepExisting){
20041                 this.clearSelections(true);
20042             }
20043             for(var i = 0, len = nodeInfo.length; i < len; i++){
20044                 this.select(nodeInfo[i], true, true);
20045             }
20046             return;
20047         } 
20048         var node = this.getNode(nodeInfo);
20049         if(!node || this.isSelected(node)){
20050             return; // already selected.
20051         }
20052         if(!keepExisting){
20053             this.clearSelections(true);
20054         }
20055         
20056         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20057             Roo.fly(node).addClass(this.selectedClass);
20058             this.selections.push(node);
20059             if(!suppressEvent){
20060                 this.fireEvent("selectionchange", this, this.selections);
20061             }
20062         }
20063         
20064         
20065     },
20066       /**
20067      * Unselects nodes.
20068      * @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
20069      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20070      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20071      */
20072     unselect : function(nodeInfo, keepExisting, suppressEvent)
20073     {
20074         if(nodeInfo instanceof Array){
20075             Roo.each(this.selections, function(s) {
20076                 this.unselect(s, nodeInfo);
20077             }, this);
20078             return;
20079         }
20080         var node = this.getNode(nodeInfo);
20081         if(!node || !this.isSelected(node)){
20082             //Roo.log("not selected");
20083             return; // not selected.
20084         }
20085         // fireevent???
20086         var ns = [];
20087         Roo.each(this.selections, function(s) {
20088             if (s == node ) {
20089                 Roo.fly(node).removeClass(this.selectedClass);
20090
20091                 return;
20092             }
20093             ns.push(s);
20094         },this);
20095         
20096         this.selections= ns;
20097         this.fireEvent("selectionchange", this, this.selections);
20098     },
20099
20100     /**
20101      * Gets a template node.
20102      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20103      * @return {HTMLElement} The node or null if it wasn't found
20104      */
20105     getNode : function(nodeInfo){
20106         if(typeof nodeInfo == "string"){
20107             return document.getElementById(nodeInfo);
20108         }else if(typeof nodeInfo == "number"){
20109             return this.nodes[nodeInfo];
20110         }
20111         return nodeInfo;
20112     },
20113
20114     /**
20115      * Gets a range template nodes.
20116      * @param {Number} startIndex
20117      * @param {Number} endIndex
20118      * @return {Array} An array of nodes
20119      */
20120     getNodes : function(start, end){
20121         var ns = this.nodes;
20122         start = start || 0;
20123         end = typeof end == "undefined" ? ns.length - 1 : end;
20124         var nodes = [];
20125         if(start <= end){
20126             for(var i = start; i <= end; i++){
20127                 nodes.push(ns[i]);
20128             }
20129         } else{
20130             for(var i = start; i >= end; i--){
20131                 nodes.push(ns[i]);
20132             }
20133         }
20134         return nodes;
20135     },
20136
20137     /**
20138      * Finds the index of the passed node
20139      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20140      * @return {Number} The index of the node or -1
20141      */
20142     indexOf : function(node){
20143         node = this.getNode(node);
20144         if(typeof node.nodeIndex == "number"){
20145             return node.nodeIndex;
20146         }
20147         var ns = this.nodes;
20148         for(var i = 0, len = ns.length; i < len; i++){
20149             if(ns[i] == node){
20150                 return i;
20151             }
20152         }
20153         return -1;
20154     }
20155 });
20156 /*
20157  * - LGPL
20158  *
20159  * based on jquery fullcalendar
20160  * 
20161  */
20162
20163 Roo.bootstrap = Roo.bootstrap || {};
20164 /**
20165  * @class Roo.bootstrap.Calendar
20166  * @extends Roo.bootstrap.Component
20167  * Bootstrap Calendar class
20168  * @cfg {Boolean} loadMask (true|false) default false
20169  * @cfg {Object} header generate the user specific header of the calendar, default false
20170
20171  * @constructor
20172  * Create a new Container
20173  * @param {Object} config The config object
20174  */
20175
20176
20177
20178 Roo.bootstrap.Calendar = function(config){
20179     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20180      this.addEvents({
20181         /**
20182              * @event select
20183              * Fires when a date is selected
20184              * @param {DatePicker} this
20185              * @param {Date} date The selected date
20186              */
20187         'select': true,
20188         /**
20189              * @event monthchange
20190              * Fires when the displayed month changes 
20191              * @param {DatePicker} this
20192              * @param {Date} date The selected month
20193              */
20194         'monthchange': true,
20195         /**
20196              * @event evententer
20197              * Fires when mouse over an event
20198              * @param {Calendar} this
20199              * @param {event} Event
20200              */
20201         'evententer': true,
20202         /**
20203              * @event eventleave
20204              * Fires when the mouse leaves an
20205              * @param {Calendar} this
20206              * @param {event}
20207              */
20208         'eventleave': true,
20209         /**
20210              * @event eventclick
20211              * Fires when the mouse click an
20212              * @param {Calendar} this
20213              * @param {event}
20214              */
20215         'eventclick': true
20216         
20217     });
20218
20219 };
20220
20221 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20222     
20223           /**
20224      * @cfg {Roo.data.Store} store
20225      * The data source for the calendar
20226      */
20227         store : false,
20228      /**
20229      * @cfg {Number} startDay
20230      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20231      */
20232     startDay : 0,
20233     
20234     loadMask : false,
20235     
20236     header : false,
20237       
20238     getAutoCreate : function(){
20239         
20240         
20241         var fc_button = function(name, corner, style, content ) {
20242             return Roo.apply({},{
20243                 tag : 'span',
20244                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20245                          (corner.length ?
20246                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20247                             ''
20248                         ),
20249                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20250                 unselectable: 'on'
20251             });
20252         };
20253         
20254         var header = {};
20255         
20256         if(!this.header){
20257             header = {
20258                 tag : 'table',
20259                 cls : 'fc-header',
20260                 style : 'width:100%',
20261                 cn : [
20262                     {
20263                         tag: 'tr',
20264                         cn : [
20265                             {
20266                                 tag : 'td',
20267                                 cls : 'fc-header-left',
20268                                 cn : [
20269                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20270                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20271                                     { tag: 'span', cls: 'fc-header-space' },
20272                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20273
20274
20275                                 ]
20276                             },
20277
20278                             {
20279                                 tag : 'td',
20280                                 cls : 'fc-header-center',
20281                                 cn : [
20282                                     {
20283                                         tag: 'span',
20284                                         cls: 'fc-header-title',
20285                                         cn : {
20286                                             tag: 'H2',
20287                                             html : 'month / year'
20288                                         }
20289                                     }
20290
20291                                 ]
20292                             },
20293                             {
20294                                 tag : 'td',
20295                                 cls : 'fc-header-right',
20296                                 cn : [
20297                               /*      fc_button('month', 'left', '', 'month' ),
20298                                     fc_button('week', '', '', 'week' ),
20299                                     fc_button('day', 'right', '', 'day' )
20300                                 */    
20301
20302                                 ]
20303                             }
20304
20305                         ]
20306                     }
20307                 ]
20308             };
20309         }
20310         
20311         header = this.header;
20312         
20313        
20314         var cal_heads = function() {
20315             var ret = [];
20316             // fixme - handle this.
20317             
20318             for (var i =0; i < Date.dayNames.length; i++) {
20319                 var d = Date.dayNames[i];
20320                 ret.push({
20321                     tag: 'th',
20322                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20323                     html : d.substring(0,3)
20324                 });
20325                 
20326             }
20327             ret[0].cls += ' fc-first';
20328             ret[6].cls += ' fc-last';
20329             return ret;
20330         };
20331         var cal_cell = function(n) {
20332             return  {
20333                 tag: 'td',
20334                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20335                 cn : [
20336                     {
20337                         cn : [
20338                             {
20339                                 cls: 'fc-day-number',
20340                                 html: 'D'
20341                             },
20342                             {
20343                                 cls: 'fc-day-content',
20344                              
20345                                 cn : [
20346                                      {
20347                                         style: 'position: relative;' // height: 17px;
20348                                     }
20349                                 ]
20350                             }
20351                             
20352                             
20353                         ]
20354                     }
20355                 ]
20356                 
20357             }
20358         };
20359         var cal_rows = function() {
20360             
20361             var ret = [];
20362             for (var r = 0; r < 6; r++) {
20363                 var row= {
20364                     tag : 'tr',
20365                     cls : 'fc-week',
20366                     cn : []
20367                 };
20368                 
20369                 for (var i =0; i < Date.dayNames.length; i++) {
20370                     var d = Date.dayNames[i];
20371                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20372
20373                 }
20374                 row.cn[0].cls+=' fc-first';
20375                 row.cn[0].cn[0].style = 'min-height:90px';
20376                 row.cn[6].cls+=' fc-last';
20377                 ret.push(row);
20378                 
20379             }
20380             ret[0].cls += ' fc-first';
20381             ret[4].cls += ' fc-prev-last';
20382             ret[5].cls += ' fc-last';
20383             return ret;
20384             
20385         };
20386         
20387         var cal_table = {
20388             tag: 'table',
20389             cls: 'fc-border-separate',
20390             style : 'width:100%',
20391             cellspacing  : 0,
20392             cn : [
20393                 { 
20394                     tag: 'thead',
20395                     cn : [
20396                         { 
20397                             tag: 'tr',
20398                             cls : 'fc-first fc-last',
20399                             cn : cal_heads()
20400                         }
20401                     ]
20402                 },
20403                 { 
20404                     tag: 'tbody',
20405                     cn : cal_rows()
20406                 }
20407                   
20408             ]
20409         };
20410          
20411          var cfg = {
20412             cls : 'fc fc-ltr',
20413             cn : [
20414                 header,
20415                 {
20416                     cls : 'fc-content',
20417                     style : "position: relative;",
20418                     cn : [
20419                         {
20420                             cls : 'fc-view fc-view-month fc-grid',
20421                             style : 'position: relative',
20422                             unselectable : 'on',
20423                             cn : [
20424                                 {
20425                                     cls : 'fc-event-container',
20426                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20427                                 },
20428                                 cal_table
20429                             ]
20430                         }
20431                     ]
20432     
20433                 }
20434            ] 
20435             
20436         };
20437         
20438          
20439         
20440         return cfg;
20441     },
20442     
20443     
20444     initEvents : function()
20445     {
20446         if(!this.store){
20447             throw "can not find store for calendar";
20448         }
20449         
20450         var mark = {
20451             tag: "div",
20452             cls:"x-dlg-mask",
20453             style: "text-align:center",
20454             cn: [
20455                 {
20456                     tag: "div",
20457                     style: "background-color:white;width:50%;margin:250 auto",
20458                     cn: [
20459                         {
20460                             tag: "img",
20461                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20462                         },
20463                         {
20464                             tag: "span",
20465                             html: "Loading"
20466                         }
20467                         
20468                     ]
20469                 }
20470             ]
20471         };
20472         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20473         
20474         var size = this.el.select('.fc-content', true).first().getSize();
20475         this.maskEl.setSize(size.width, size.height);
20476         this.maskEl.enableDisplayMode("block");
20477         if(!this.loadMask){
20478             this.maskEl.hide();
20479         }
20480         
20481         this.store = Roo.factory(this.store, Roo.data);
20482         this.store.on('load', this.onLoad, this);
20483         this.store.on('beforeload', this.onBeforeLoad, this);
20484         
20485         this.resize();
20486         
20487         this.cells = this.el.select('.fc-day',true);
20488         //Roo.log(this.cells);
20489         this.textNodes = this.el.query('.fc-day-number');
20490         this.cells.addClassOnOver('fc-state-hover');
20491         
20492         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20493         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20494         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20495         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20496         
20497         this.on('monthchange', this.onMonthChange, this);
20498         
20499         this.update(new Date().clearTime());
20500     },
20501     
20502     resize : function() {
20503         var sz  = this.el.getSize();
20504         
20505         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20506         this.el.select('.fc-day-content div',true).setHeight(34);
20507     },
20508     
20509     
20510     // private
20511     showPrevMonth : function(e){
20512         this.update(this.activeDate.add("mo", -1));
20513     },
20514     showToday : function(e){
20515         this.update(new Date().clearTime());
20516     },
20517     // private
20518     showNextMonth : function(e){
20519         this.update(this.activeDate.add("mo", 1));
20520     },
20521
20522     // private
20523     showPrevYear : function(){
20524         this.update(this.activeDate.add("y", -1));
20525     },
20526
20527     // private
20528     showNextYear : function(){
20529         this.update(this.activeDate.add("y", 1));
20530     },
20531
20532     
20533    // private
20534     update : function(date)
20535     {
20536         var vd = this.activeDate;
20537         this.activeDate = date;
20538 //        if(vd && this.el){
20539 //            var t = date.getTime();
20540 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20541 //                Roo.log('using add remove');
20542 //                
20543 //                this.fireEvent('monthchange', this, date);
20544 //                
20545 //                this.cells.removeClass("fc-state-highlight");
20546 //                this.cells.each(function(c){
20547 //                   if(c.dateValue == t){
20548 //                       c.addClass("fc-state-highlight");
20549 //                       setTimeout(function(){
20550 //                            try{c.dom.firstChild.focus();}catch(e){}
20551 //                       }, 50);
20552 //                       return false;
20553 //                   }
20554 //                   return true;
20555 //                });
20556 //                return;
20557 //            }
20558 //        }
20559         
20560         var days = date.getDaysInMonth();
20561         
20562         var firstOfMonth = date.getFirstDateOfMonth();
20563         var startingPos = firstOfMonth.getDay()-this.startDay;
20564         
20565         if(startingPos < this.startDay){
20566             startingPos += 7;
20567         }
20568         
20569         var pm = date.add(Date.MONTH, -1);
20570         var prevStart = pm.getDaysInMonth()-startingPos;
20571 //        
20572         this.cells = this.el.select('.fc-day',true);
20573         this.textNodes = this.el.query('.fc-day-number');
20574         this.cells.addClassOnOver('fc-state-hover');
20575         
20576         var cells = this.cells.elements;
20577         var textEls = this.textNodes;
20578         
20579         Roo.each(cells, function(cell){
20580             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20581         });
20582         
20583         days += startingPos;
20584
20585         // convert everything to numbers so it's fast
20586         var day = 86400000;
20587         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20588         //Roo.log(d);
20589         //Roo.log(pm);
20590         //Roo.log(prevStart);
20591         
20592         var today = new Date().clearTime().getTime();
20593         var sel = date.clearTime().getTime();
20594         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20595         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20596         var ddMatch = this.disabledDatesRE;
20597         var ddText = this.disabledDatesText;
20598         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20599         var ddaysText = this.disabledDaysText;
20600         var format = this.format;
20601         
20602         var setCellClass = function(cal, cell){
20603             cell.row = 0;
20604             cell.events = [];
20605             cell.more = [];
20606             //Roo.log('set Cell Class');
20607             cell.title = "";
20608             var t = d.getTime();
20609             
20610             //Roo.log(d);
20611             
20612             cell.dateValue = t;
20613             if(t == today){
20614                 cell.className += " fc-today";
20615                 cell.className += " fc-state-highlight";
20616                 cell.title = cal.todayText;
20617             }
20618             if(t == sel){
20619                 // disable highlight in other month..
20620                 //cell.className += " fc-state-highlight";
20621                 
20622             }
20623             // disabling
20624             if(t < min) {
20625                 cell.className = " fc-state-disabled";
20626                 cell.title = cal.minText;
20627                 return;
20628             }
20629             if(t > max) {
20630                 cell.className = " fc-state-disabled";
20631                 cell.title = cal.maxText;
20632                 return;
20633             }
20634             if(ddays){
20635                 if(ddays.indexOf(d.getDay()) != -1){
20636                     cell.title = ddaysText;
20637                     cell.className = " fc-state-disabled";
20638                 }
20639             }
20640             if(ddMatch && format){
20641                 var fvalue = d.dateFormat(format);
20642                 if(ddMatch.test(fvalue)){
20643                     cell.title = ddText.replace("%0", fvalue);
20644                     cell.className = " fc-state-disabled";
20645                 }
20646             }
20647             
20648             if (!cell.initialClassName) {
20649                 cell.initialClassName = cell.dom.className;
20650             }
20651             
20652             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20653         };
20654
20655         var i = 0;
20656         
20657         for(; i < startingPos; i++) {
20658             textEls[i].innerHTML = (++prevStart);
20659             d.setDate(d.getDate()+1);
20660             
20661             cells[i].className = "fc-past fc-other-month";
20662             setCellClass(this, cells[i]);
20663         }
20664         
20665         var intDay = 0;
20666         
20667         for(; i < days; i++){
20668             intDay = i - startingPos + 1;
20669             textEls[i].innerHTML = (intDay);
20670             d.setDate(d.getDate()+1);
20671             
20672             cells[i].className = ''; // "x-date-active";
20673             setCellClass(this, cells[i]);
20674         }
20675         var extraDays = 0;
20676         
20677         for(; i < 42; i++) {
20678             textEls[i].innerHTML = (++extraDays);
20679             d.setDate(d.getDate()+1);
20680             
20681             cells[i].className = "fc-future fc-other-month";
20682             setCellClass(this, cells[i]);
20683         }
20684         
20685         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20686         
20687         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20688         
20689         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20690         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20691         
20692         if(totalRows != 6){
20693             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20694             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20695         }
20696         
20697         this.fireEvent('monthchange', this, date);
20698         
20699         
20700         /*
20701         if(!this.internalRender){
20702             var main = this.el.dom.firstChild;
20703             var w = main.offsetWidth;
20704             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20705             Roo.fly(main).setWidth(w);
20706             this.internalRender = true;
20707             // opera does not respect the auto grow header center column
20708             // then, after it gets a width opera refuses to recalculate
20709             // without a second pass
20710             if(Roo.isOpera && !this.secondPass){
20711                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20712                 this.secondPass = true;
20713                 this.update.defer(10, this, [date]);
20714             }
20715         }
20716         */
20717         
20718     },
20719     
20720     findCell : function(dt) {
20721         dt = dt.clearTime().getTime();
20722         var ret = false;
20723         this.cells.each(function(c){
20724             //Roo.log("check " +c.dateValue + '?=' + dt);
20725             if(c.dateValue == dt){
20726                 ret = c;
20727                 return false;
20728             }
20729             return true;
20730         });
20731         
20732         return ret;
20733     },
20734     
20735     findCells : function(ev) {
20736         var s = ev.start.clone().clearTime().getTime();
20737        // Roo.log(s);
20738         var e= ev.end.clone().clearTime().getTime();
20739        // Roo.log(e);
20740         var ret = [];
20741         this.cells.each(function(c){
20742              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20743             
20744             if(c.dateValue > e){
20745                 return ;
20746             }
20747             if(c.dateValue < s){
20748                 return ;
20749             }
20750             ret.push(c);
20751         });
20752         
20753         return ret;    
20754     },
20755     
20756 //    findBestRow: function(cells)
20757 //    {
20758 //        var ret = 0;
20759 //        
20760 //        for (var i =0 ; i < cells.length;i++) {
20761 //            ret  = Math.max(cells[i].rows || 0,ret);
20762 //        }
20763 //        return ret;
20764 //        
20765 //    },
20766     
20767     
20768     addItem : function(ev)
20769     {
20770         // look for vertical location slot in
20771         var cells = this.findCells(ev);
20772         
20773 //        ev.row = this.findBestRow(cells);
20774         
20775         // work out the location.
20776         
20777         var crow = false;
20778         var rows = [];
20779         for(var i =0; i < cells.length; i++) {
20780             
20781             cells[i].row = cells[0].row;
20782             
20783             if(i == 0){
20784                 cells[i].row = cells[i].row + 1;
20785             }
20786             
20787             if (!crow) {
20788                 crow = {
20789                     start : cells[i],
20790                     end :  cells[i]
20791                 };
20792                 continue;
20793             }
20794             if (crow.start.getY() == cells[i].getY()) {
20795                 // on same row.
20796                 crow.end = cells[i];
20797                 continue;
20798             }
20799             // different row.
20800             rows.push(crow);
20801             crow = {
20802                 start: cells[i],
20803                 end : cells[i]
20804             };
20805             
20806         }
20807         
20808         rows.push(crow);
20809         ev.els = [];
20810         ev.rows = rows;
20811         ev.cells = cells;
20812         
20813         cells[0].events.push(ev);
20814         
20815         this.calevents.push(ev);
20816     },
20817     
20818     clearEvents: function() {
20819         
20820         if(!this.calevents){
20821             return;
20822         }
20823         
20824         Roo.each(this.cells.elements, function(c){
20825             c.row = 0;
20826             c.events = [];
20827             c.more = [];
20828         });
20829         
20830         Roo.each(this.calevents, function(e) {
20831             Roo.each(e.els, function(el) {
20832                 el.un('mouseenter' ,this.onEventEnter, this);
20833                 el.un('mouseleave' ,this.onEventLeave, this);
20834                 el.remove();
20835             },this);
20836         },this);
20837         
20838         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20839             e.remove();
20840         });
20841         
20842     },
20843     
20844     renderEvents: function()
20845     {   
20846         var _this = this;
20847         
20848         this.cells.each(function(c) {
20849             
20850             if(c.row < 5){
20851                 return;
20852             }
20853             
20854             var ev = c.events;
20855             
20856             var r = 4;
20857             if(c.row != c.events.length){
20858                 r = 4 - (4 - (c.row - c.events.length));
20859             }
20860             
20861             c.events = ev.slice(0, r);
20862             c.more = ev.slice(r);
20863             
20864             if(c.more.length && c.more.length == 1){
20865                 c.events.push(c.more.pop());
20866             }
20867             
20868             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20869             
20870         });
20871             
20872         this.cells.each(function(c) {
20873             
20874             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20875             
20876             
20877             for (var e = 0; e < c.events.length; e++){
20878                 var ev = c.events[e];
20879                 var rows = ev.rows;
20880                 
20881                 for(var i = 0; i < rows.length; i++) {
20882                 
20883                     // how many rows should it span..
20884
20885                     var  cfg = {
20886                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20887                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20888
20889                         unselectable : "on",
20890                         cn : [
20891                             {
20892                                 cls: 'fc-event-inner',
20893                                 cn : [
20894     //                                {
20895     //                                  tag:'span',
20896     //                                  cls: 'fc-event-time',
20897     //                                  html : cells.length > 1 ? '' : ev.time
20898     //                                },
20899                                     {
20900                                       tag:'span',
20901                                       cls: 'fc-event-title',
20902                                       html : String.format('{0}', ev.title)
20903                                     }
20904
20905
20906                                 ]
20907                             },
20908                             {
20909                                 cls: 'ui-resizable-handle ui-resizable-e',
20910                                 html : '&nbsp;&nbsp;&nbsp'
20911                             }
20912
20913                         ]
20914                     };
20915
20916                     if (i == 0) {
20917                         cfg.cls += ' fc-event-start';
20918                     }
20919                     if ((i+1) == rows.length) {
20920                         cfg.cls += ' fc-event-end';
20921                     }
20922
20923                     var ctr = _this.el.select('.fc-event-container',true).first();
20924                     var cg = ctr.createChild(cfg);
20925
20926                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20927                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20928
20929                     var r = (c.more.length) ? 1 : 0;
20930                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20931                     cg.setWidth(ebox.right - sbox.x -2);
20932
20933                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20934                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20935                     cg.on('click', _this.onEventClick, _this, ev);
20936
20937                     ev.els.push(cg);
20938                     
20939                 }
20940                 
20941             }
20942             
20943             
20944             if(c.more.length){
20945                 var  cfg = {
20946                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20947                     style : 'position: absolute',
20948                     unselectable : "on",
20949                     cn : [
20950                         {
20951                             cls: 'fc-event-inner',
20952                             cn : [
20953                                 {
20954                                   tag:'span',
20955                                   cls: 'fc-event-title',
20956                                   html : 'More'
20957                                 }
20958
20959
20960                             ]
20961                         },
20962                         {
20963                             cls: 'ui-resizable-handle ui-resizable-e',
20964                             html : '&nbsp;&nbsp;&nbsp'
20965                         }
20966
20967                     ]
20968                 };
20969
20970                 var ctr = _this.el.select('.fc-event-container',true).first();
20971                 var cg = ctr.createChild(cfg);
20972
20973                 var sbox = c.select('.fc-day-content',true).first().getBox();
20974                 var ebox = c.select('.fc-day-content',true).first().getBox();
20975                 //Roo.log(cg);
20976                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20977                 cg.setWidth(ebox.right - sbox.x -2);
20978
20979                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20980                 
20981             }
20982             
20983         });
20984         
20985         
20986         
20987     },
20988     
20989     onEventEnter: function (e, el,event,d) {
20990         this.fireEvent('evententer', this, el, event);
20991     },
20992     
20993     onEventLeave: function (e, el,event,d) {
20994         this.fireEvent('eventleave', this, el, event);
20995     },
20996     
20997     onEventClick: function (e, el,event,d) {
20998         this.fireEvent('eventclick', this, el, event);
20999     },
21000     
21001     onMonthChange: function () {
21002         this.store.load();
21003     },
21004     
21005     onMoreEventClick: function(e, el, more)
21006     {
21007         var _this = this;
21008         
21009         this.calpopover.placement = 'right';
21010         this.calpopover.setTitle('More');
21011         
21012         this.calpopover.setContent('');
21013         
21014         var ctr = this.calpopover.el.select('.popover-content', true).first();
21015         
21016         Roo.each(more, function(m){
21017             var cfg = {
21018                 cls : 'fc-event-hori fc-event-draggable',
21019                 html : m.title
21020             };
21021             var cg = ctr.createChild(cfg);
21022             
21023             cg.on('click', _this.onEventClick, _this, m);
21024         });
21025         
21026         this.calpopover.show(el);
21027         
21028         
21029     },
21030     
21031     onLoad: function () 
21032     {   
21033         this.calevents = [];
21034         var cal = this;
21035         
21036         if(this.store.getCount() > 0){
21037             this.store.data.each(function(d){
21038                cal.addItem({
21039                     id : d.data.id,
21040                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21041                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21042                     time : d.data.start_time,
21043                     title : d.data.title,
21044                     description : d.data.description,
21045                     venue : d.data.venue
21046                 });
21047             });
21048         }
21049         
21050         this.renderEvents();
21051         
21052         if(this.calevents.length && this.loadMask){
21053             this.maskEl.hide();
21054         }
21055     },
21056     
21057     onBeforeLoad: function()
21058     {
21059         this.clearEvents();
21060         if(this.loadMask){
21061             this.maskEl.show();
21062         }
21063     }
21064 });
21065
21066  
21067  /*
21068  * - LGPL
21069  *
21070  * element
21071  * 
21072  */
21073
21074 /**
21075  * @class Roo.bootstrap.Popover
21076  * @extends Roo.bootstrap.Component
21077  * @builder-top
21078  * @children Roo.bootstrap.Component
21079  * Bootstrap Popover class
21080  * @cfg {String} html contents of the popover   (or false to use children..)
21081  * @cfg {String} title of popover (or false to hide)
21082  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21083  * @cfg {String} trigger click || hover (or false to trigger manually)
21084  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21085  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21086  *      - if false and it has a 'parent' then it will be automatically added to that element
21087  *      - if string - Roo.get  will be called 
21088  * @cfg {Number} delay - delay before showing
21089  
21090  * @constructor
21091  * Create a new Popover
21092  * @param {Object} config The config object
21093  */
21094
21095 Roo.bootstrap.Popover = function(config){
21096     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21097     
21098     this.addEvents({
21099         // raw events
21100          /**
21101          * @event show
21102          * After the popover show
21103          * 
21104          * @param {Roo.bootstrap.Popover} this
21105          */
21106         "show" : true,
21107         /**
21108          * @event hide
21109          * After the popover hide
21110          * 
21111          * @param {Roo.bootstrap.Popover} this
21112          */
21113         "hide" : true
21114     });
21115 };
21116
21117 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21118     
21119     title: false,
21120     html: false,
21121     
21122     placement : 'right',
21123     trigger : 'hover', // hover
21124     modal : false,
21125     delay : 0,
21126     
21127     over: false,
21128     
21129     can_build_overlaid : false,
21130     
21131     maskEl : false, // the mask element
21132     headerEl : false,
21133     contentEl : false,
21134     alignEl : false, // when show is called with an element - this get's stored.
21135     
21136     getChildContainer : function()
21137     {
21138         return this.contentEl;
21139         
21140     },
21141     getPopoverHeader : function()
21142     {
21143         this.title = true; // flag not to hide it..
21144         this.headerEl.addClass('p-0');
21145         return this.headerEl
21146     },
21147     
21148     
21149     getAutoCreate : function(){
21150          
21151         var cfg = {
21152            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21153            style: 'display:block',
21154            cn : [
21155                 {
21156                     cls : 'arrow'
21157                 },
21158                 {
21159                     cls : 'popover-inner ',
21160                     cn : [
21161                         {
21162                             tag: 'h3',
21163                             cls: 'popover-title popover-header',
21164                             html : this.title === false ? '' : this.title
21165                         },
21166                         {
21167                             cls : 'popover-content popover-body '  + (this.cls || ''),
21168                             html : this.html || ''
21169                         }
21170                     ]
21171                     
21172                 }
21173            ]
21174         };
21175         
21176         return cfg;
21177     },
21178     /**
21179      * @param {string} the title
21180      */
21181     setTitle: function(str)
21182     {
21183         this.title = str;
21184         if (this.el) {
21185             this.headerEl.dom.innerHTML = str;
21186         }
21187         
21188     },
21189     /**
21190      * @param {string} the body content
21191      */
21192     setContent: function(str)
21193     {
21194         this.html = str;
21195         if (this.contentEl) {
21196             this.contentEl.dom.innerHTML = str;
21197         }
21198         
21199     },
21200     // as it get's added to the bottom of the page.
21201     onRender : function(ct, position)
21202     {
21203         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21204         
21205         
21206         
21207         if(!this.el){
21208             var cfg = Roo.apply({},  this.getAutoCreate());
21209             cfg.id = Roo.id();
21210             
21211             if (this.cls) {
21212                 cfg.cls += ' ' + this.cls;
21213             }
21214             if (this.style) {
21215                 cfg.style = this.style;
21216             }
21217             //Roo.log("adding to ");
21218             this.el = Roo.get(document.body).createChild(cfg, position);
21219 //            Roo.log(this.el);
21220         }
21221         
21222         this.contentEl = this.el.select('.popover-content',true).first();
21223         this.headerEl =  this.el.select('.popover-title',true).first();
21224         
21225         var nitems = [];
21226         if(typeof(this.items) != 'undefined'){
21227             var items = this.items;
21228             delete this.items;
21229
21230             for(var i =0;i < items.length;i++) {
21231                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21232             }
21233         }
21234
21235         this.items = nitems;
21236         
21237         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21238         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21239         
21240         
21241         
21242         this.initEvents();
21243     },
21244     
21245     resizeMask : function()
21246     {
21247         this.maskEl.setSize(
21248             Roo.lib.Dom.getViewWidth(true),
21249             Roo.lib.Dom.getViewHeight(true)
21250         );
21251     },
21252     
21253     initEvents : function()
21254     {
21255         
21256         if (!this.modal) { 
21257             Roo.bootstrap.Popover.register(this);
21258         }
21259          
21260         this.arrowEl = this.el.select('.arrow',true).first();
21261         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21262         this.el.enableDisplayMode('block');
21263         this.el.hide();
21264  
21265         
21266         if (this.over === false && !this.parent()) {
21267             return; 
21268         }
21269         if (this.triggers === false) {
21270             return;
21271         }
21272          
21273         // support parent
21274         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21275         var triggers = this.trigger ? this.trigger.split(' ') : [];
21276         Roo.each(triggers, function(trigger) {
21277         
21278             if (trigger == 'click') {
21279                 on_el.on('click', this.toggle, this);
21280             } else if (trigger != 'manual') {
21281                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21282                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21283       
21284                 on_el.on(eventIn  ,this.enter, this);
21285                 on_el.on(eventOut, this.leave, this);
21286             }
21287         }, this);
21288     },
21289     
21290     
21291     // private
21292     timeout : null,
21293     hoverState : null,
21294     
21295     toggle : function () {
21296         this.hoverState == 'in' ? this.leave() : this.enter();
21297     },
21298     
21299     enter : function () {
21300         
21301         clearTimeout(this.timeout);
21302     
21303         this.hoverState = 'in';
21304     
21305         if (!this.delay || !this.delay.show) {
21306             this.show();
21307             return;
21308         }
21309         var _t = this;
21310         this.timeout = setTimeout(function () {
21311             if (_t.hoverState == 'in') {
21312                 _t.show();
21313             }
21314         }, this.delay.show)
21315     },
21316     
21317     leave : function() {
21318         clearTimeout(this.timeout);
21319     
21320         this.hoverState = 'out';
21321     
21322         if (!this.delay || !this.delay.hide) {
21323             this.hide();
21324             return;
21325         }
21326         var _t = this;
21327         this.timeout = setTimeout(function () {
21328             if (_t.hoverState == 'out') {
21329                 _t.hide();
21330             }
21331         }, this.delay.hide)
21332     },
21333     /**
21334      * Show the popover
21335      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21336      * @param {string} (left|right|top|bottom) position
21337      */
21338     show : function (on_el, placement)
21339     {
21340         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21341         on_el = on_el || false; // default to false
21342          
21343         if (!on_el) {
21344             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21345                 on_el = this.parent().el;
21346             } else if (this.over) {
21347                 on_el = Roo.get(this.over);
21348             }
21349             
21350         }
21351         
21352         this.alignEl = Roo.get( on_el );
21353
21354         if (!this.el) {
21355             this.render(document.body);
21356         }
21357         
21358         
21359          
21360         
21361         if (this.title === false) {
21362             this.headerEl.hide();
21363         }
21364         
21365        
21366         this.el.show();
21367         this.el.dom.style.display = 'block';
21368          
21369  
21370         if (this.alignEl) {
21371             this.updatePosition(this.placement, true);
21372              
21373         } else {
21374             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21375             var es = this.el.getSize();
21376             var x = Roo.lib.Dom.getViewWidth()/2;
21377             var y = Roo.lib.Dom.getViewHeight()/2;
21378             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21379             
21380         }
21381
21382         
21383         //var arrow = this.el.select('.arrow',true).first();
21384         //arrow.set(align[2], 
21385         
21386         this.el.addClass('in');
21387         
21388          
21389         
21390         this.hoverState = 'in';
21391         
21392         if (this.modal) {
21393             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21394             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21395             this.maskEl.dom.style.display = 'block';
21396             this.maskEl.addClass('show');
21397         }
21398         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21399  
21400         this.fireEvent('show', this);
21401         
21402     },
21403     /**
21404      * fire this manually after loading a grid in the table for example
21405      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21406      * @param {Boolean} try and move it if we cant get right position.
21407      */
21408     updatePosition : function(placement, try_move)
21409     {
21410         // allow for calling with no parameters
21411         placement = placement   ? placement :  this.placement;
21412         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21413         
21414         this.el.removeClass([
21415             'fade','top','bottom', 'left', 'right','in',
21416             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21417         ]);
21418         this.el.addClass(placement + ' bs-popover-' + placement);
21419         
21420         if (!this.alignEl ) {
21421             return false;
21422         }
21423         
21424         switch (placement) {
21425             case 'right':
21426                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21427                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21428                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21429                     //normal display... or moved up/down.
21430                     this.el.setXY(offset);
21431                     var xy = this.alignEl.getAnchorXY('tr', false);
21432                     xy[0]+=2;xy[1]+=5;
21433                     this.arrowEl.setXY(xy);
21434                     return true;
21435                 }
21436                 // continue through...
21437                 return this.updatePosition('left', false);
21438                 
21439             
21440             case 'left':
21441                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21442                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21443                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21444                     //normal display... or moved up/down.
21445                     this.el.setXY(offset);
21446                     var xy = this.alignEl.getAnchorXY('tl', false);
21447                     xy[0]-=10;xy[1]+=5; // << fix me
21448                     this.arrowEl.setXY(xy);
21449                     return true;
21450                 }
21451                 // call self...
21452                 return this.updatePosition('right', false);
21453             
21454             case 'top':
21455                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21456                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21457                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21458                     //normal display... or moved up/down.
21459                     this.el.setXY(offset);
21460                     var xy = this.alignEl.getAnchorXY('t', false);
21461                     xy[1]-=10; // << fix me
21462                     this.arrowEl.setXY(xy);
21463                     return true;
21464                 }
21465                 // fall through
21466                return this.updatePosition('bottom', false);
21467             
21468             case 'bottom':
21469                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21470                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21471                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21472                     //normal display... or moved up/down.
21473                     this.el.setXY(offset);
21474                     var xy = this.alignEl.getAnchorXY('b', false);
21475                      xy[1]+=2; // << fix me
21476                     this.arrowEl.setXY(xy);
21477                     return true;
21478                 }
21479                 // fall through
21480                 return this.updatePosition('top', false);
21481                 
21482             
21483         }
21484         
21485         
21486         return false;
21487     },
21488     
21489     hide : function()
21490     {
21491         this.el.setXY([0,0]);
21492         this.el.removeClass('in');
21493         this.el.hide();
21494         this.hoverState = null;
21495         this.maskEl.hide(); // always..
21496         this.fireEvent('hide', this);
21497     }
21498     
21499 });
21500
21501
21502 Roo.apply(Roo.bootstrap.Popover, {
21503
21504     alignment : {
21505         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21506         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21507         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21508         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21509     },
21510     
21511     zIndex : 20001,
21512
21513     clickHander : false,
21514     
21515     
21516
21517     onMouseDown : function(e)
21518     {
21519         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21520             /// what is nothing is showing..
21521             this.hideAll();
21522         }
21523          
21524     },
21525     
21526     
21527     popups : [],
21528     
21529     register : function(popup)
21530     {
21531         if (!Roo.bootstrap.Popover.clickHandler) {
21532             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21533         }
21534         // hide other popups.
21535         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21536         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21537         this.hideAll(); //<< why?
21538         //this.popups.push(popup);
21539     },
21540     hideAll : function()
21541     {
21542         this.popups.forEach(function(p) {
21543             p.hide();
21544         });
21545     },
21546     onShow : function() {
21547         Roo.bootstrap.Popover.popups.push(this);
21548     },
21549     onHide : function() {
21550         Roo.bootstrap.Popover.popups.remove(this);
21551     } 
21552
21553 });/*
21554  * - LGPL
21555  *
21556  * Card header - holder for the card header elements.
21557  * 
21558  */
21559
21560 /**
21561  * @class Roo.bootstrap.PopoverNav
21562  * @extends Roo.bootstrap.NavGroup
21563  * Bootstrap Popover header navigation class
21564  * @constructor
21565  * Create a new Popover Header Navigation 
21566  * @param {Object} config The config object
21567  */
21568
21569 Roo.bootstrap.PopoverNav = function(config){
21570     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21571 };
21572
21573 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21574     
21575     
21576     container_method : 'getPopoverHeader' 
21577     
21578      
21579     
21580     
21581    
21582 });
21583
21584  
21585
21586  /*
21587  * - LGPL
21588  *
21589  * Progress
21590  * 
21591  */
21592
21593 /**
21594  * @class Roo.bootstrap.Progress
21595  * @extends Roo.bootstrap.Component
21596  * Bootstrap Progress class
21597  * @cfg {Boolean} striped striped of the progress bar
21598  * @cfg {Boolean} active animated of the progress bar
21599  * 
21600  * 
21601  * @constructor
21602  * Create a new Progress
21603  * @param {Object} config The config object
21604  */
21605
21606 Roo.bootstrap.Progress = function(config){
21607     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21608 };
21609
21610 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21611     
21612     striped : false,
21613     active: false,
21614     
21615     getAutoCreate : function(){
21616         var cfg = {
21617             tag: 'div',
21618             cls: 'progress'
21619         };
21620         
21621         
21622         if(this.striped){
21623             cfg.cls += ' progress-striped';
21624         }
21625       
21626         if(this.active){
21627             cfg.cls += ' active';
21628         }
21629         
21630         
21631         return cfg;
21632     }
21633    
21634 });
21635
21636  
21637
21638  /*
21639  * - LGPL
21640  *
21641  * ProgressBar
21642  * 
21643  */
21644
21645 /**
21646  * @class Roo.bootstrap.ProgressBar
21647  * @extends Roo.bootstrap.Component
21648  * Bootstrap ProgressBar class
21649  * @cfg {Number} aria_valuenow aria-value now
21650  * @cfg {Number} aria_valuemin aria-value min
21651  * @cfg {Number} aria_valuemax aria-value max
21652  * @cfg {String} label label for the progress bar
21653  * @cfg {String} panel (success | info | warning | danger )
21654  * @cfg {String} role role of the progress bar
21655  * @cfg {String} sr_only text
21656  * 
21657  * 
21658  * @constructor
21659  * Create a new ProgressBar
21660  * @param {Object} config The config object
21661  */
21662
21663 Roo.bootstrap.ProgressBar = function(config){
21664     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21665 };
21666
21667 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21668     
21669     aria_valuenow : 0,
21670     aria_valuemin : 0,
21671     aria_valuemax : 100,
21672     label : false,
21673     panel : false,
21674     role : false,
21675     sr_only: false,
21676     
21677     getAutoCreate : function()
21678     {
21679         
21680         var cfg = {
21681             tag: 'div',
21682             cls: 'progress-bar',
21683             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21684         };
21685         
21686         if(this.sr_only){
21687             cfg.cn = {
21688                 tag: 'span',
21689                 cls: 'sr-only',
21690                 html: this.sr_only
21691             }
21692         }
21693         
21694         if(this.role){
21695             cfg.role = this.role;
21696         }
21697         
21698         if(this.aria_valuenow){
21699             cfg['aria-valuenow'] = this.aria_valuenow;
21700         }
21701         
21702         if(this.aria_valuemin){
21703             cfg['aria-valuemin'] = this.aria_valuemin;
21704         }
21705         
21706         if(this.aria_valuemax){
21707             cfg['aria-valuemax'] = this.aria_valuemax;
21708         }
21709         
21710         if(this.label && !this.sr_only){
21711             cfg.html = this.label;
21712         }
21713         
21714         if(this.panel){
21715             cfg.cls += ' progress-bar-' + this.panel;
21716         }
21717         
21718         return cfg;
21719     },
21720     
21721     update : function(aria_valuenow)
21722     {
21723         this.aria_valuenow = aria_valuenow;
21724         
21725         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21726     }
21727    
21728 });
21729
21730  
21731
21732  /*
21733  * - LGPL
21734  *
21735  * column
21736  * 
21737  */
21738
21739 /**
21740  * @class Roo.bootstrap.TabGroup
21741  * @extends Roo.bootstrap.Column
21742  * Bootstrap Column class
21743  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21744  * @cfg {Boolean} carousel true to make the group behave like a carousel
21745  * @cfg {Boolean} bullets show bullets for the panels
21746  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21747  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21748  * @cfg {Boolean} showarrow (true|false) show arrow default true
21749  * 
21750  * @constructor
21751  * Create a new TabGroup
21752  * @param {Object} config The config object
21753  */
21754
21755 Roo.bootstrap.TabGroup = function(config){
21756     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21757     if (!this.navId) {
21758         this.navId = Roo.id();
21759     }
21760     this.tabs = [];
21761     Roo.bootstrap.TabGroup.register(this);
21762     
21763 };
21764
21765 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21766     
21767     carousel : false,
21768     transition : false,
21769     bullets : 0,
21770     timer : 0,
21771     autoslide : false,
21772     slideFn : false,
21773     slideOnTouch : false,
21774     showarrow : true,
21775     
21776     getAutoCreate : function()
21777     {
21778         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21779         
21780         cfg.cls += ' tab-content';
21781         
21782         if (this.carousel) {
21783             cfg.cls += ' carousel slide';
21784             
21785             cfg.cn = [{
21786                cls : 'carousel-inner',
21787                cn : []
21788             }];
21789         
21790             if(this.bullets  && !Roo.isTouch){
21791                 
21792                 var bullets = {
21793                     cls : 'carousel-bullets',
21794                     cn : []
21795                 };
21796                
21797                 if(this.bullets_cls){
21798                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21799                 }
21800                 
21801                 bullets.cn.push({
21802                     cls : 'clear'
21803                 });
21804                 
21805                 cfg.cn[0].cn.push(bullets);
21806             }
21807             
21808             if(this.showarrow){
21809                 cfg.cn[0].cn.push({
21810                     tag : 'div',
21811                     class : 'carousel-arrow',
21812                     cn : [
21813                         {
21814                             tag : 'div',
21815                             class : 'carousel-prev',
21816                             cn : [
21817                                 {
21818                                     tag : 'i',
21819                                     class : 'fa fa-chevron-left'
21820                                 }
21821                             ]
21822                         },
21823                         {
21824                             tag : 'div',
21825                             class : 'carousel-next',
21826                             cn : [
21827                                 {
21828                                     tag : 'i',
21829                                     class : 'fa fa-chevron-right'
21830                                 }
21831                             ]
21832                         }
21833                     ]
21834                 });
21835             }
21836             
21837         }
21838         
21839         return cfg;
21840     },
21841     
21842     initEvents:  function()
21843     {
21844 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21845 //            this.el.on("touchstart", this.onTouchStart, this);
21846 //        }
21847         
21848         if(this.autoslide){
21849             var _this = this;
21850             
21851             this.slideFn = window.setInterval(function() {
21852                 _this.showPanelNext();
21853             }, this.timer);
21854         }
21855         
21856         if(this.showarrow){
21857             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21858             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21859         }
21860         
21861         
21862     },
21863     
21864 //    onTouchStart : function(e, el, o)
21865 //    {
21866 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21867 //            return;
21868 //        }
21869 //        
21870 //        this.showPanelNext();
21871 //    },
21872     
21873     
21874     getChildContainer : function()
21875     {
21876         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21877     },
21878     
21879     /**
21880     * register a Navigation item
21881     * @param {Roo.bootstrap.NavItem} the navitem to add
21882     */
21883     register : function(item)
21884     {
21885         this.tabs.push( item);
21886         item.navId = this.navId; // not really needed..
21887         this.addBullet();
21888     
21889     },
21890     
21891     getActivePanel : function()
21892     {
21893         var r = false;
21894         Roo.each(this.tabs, function(t) {
21895             if (t.active) {
21896                 r = t;
21897                 return false;
21898             }
21899             return null;
21900         });
21901         return r;
21902         
21903     },
21904     getPanelByName : function(n)
21905     {
21906         var r = false;
21907         Roo.each(this.tabs, function(t) {
21908             if (t.tabId == n) {
21909                 r = t;
21910                 return false;
21911             }
21912             return null;
21913         });
21914         return r;
21915     },
21916     indexOfPanel : function(p)
21917     {
21918         var r = false;
21919         Roo.each(this.tabs, function(t,i) {
21920             if (t.tabId == p.tabId) {
21921                 r = i;
21922                 return false;
21923             }
21924             return null;
21925         });
21926         return r;
21927     },
21928     /**
21929      * show a specific panel
21930      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21931      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21932      */
21933     showPanel : function (pan)
21934     {
21935         if(this.transition || typeof(pan) == 'undefined'){
21936             Roo.log("waiting for the transitionend");
21937             return false;
21938         }
21939         
21940         if (typeof(pan) == 'number') {
21941             pan = this.tabs[pan];
21942         }
21943         
21944         if (typeof(pan) == 'string') {
21945             pan = this.getPanelByName(pan);
21946         }
21947         
21948         var cur = this.getActivePanel();
21949         
21950         if(!pan || !cur){
21951             Roo.log('pan or acitve pan is undefined');
21952             return false;
21953         }
21954         
21955         if (pan.tabId == this.getActivePanel().tabId) {
21956             return true;
21957         }
21958         
21959         if (false === cur.fireEvent('beforedeactivate')) {
21960             return false;
21961         }
21962         
21963         if(this.bullets > 0 && !Roo.isTouch){
21964             this.setActiveBullet(this.indexOfPanel(pan));
21965         }
21966         
21967         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21968             
21969             //class="carousel-item carousel-item-next carousel-item-left"
21970             
21971             this.transition = true;
21972             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21973             var lr = dir == 'next' ? 'left' : 'right';
21974             pan.el.addClass(dir); // or prev
21975             pan.el.addClass('carousel-item-' + dir); // or prev
21976             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21977             cur.el.addClass(lr); // or right
21978             pan.el.addClass(lr);
21979             cur.el.addClass('carousel-item-' +lr); // or right
21980             pan.el.addClass('carousel-item-' +lr);
21981             
21982             
21983             var _this = this;
21984             cur.el.on('transitionend', function() {
21985                 Roo.log("trans end?");
21986                 
21987                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21988                 pan.setActive(true);
21989                 
21990                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21991                 cur.setActive(false);
21992                 
21993                 _this.transition = false;
21994                 
21995             }, this, { single:  true } );
21996             
21997             return true;
21998         }
21999         
22000         cur.setActive(false);
22001         pan.setActive(true);
22002         
22003         return true;
22004         
22005     },
22006     showPanelNext : function()
22007     {
22008         var i = this.indexOfPanel(this.getActivePanel());
22009         
22010         if (i >= this.tabs.length - 1 && !this.autoslide) {
22011             return;
22012         }
22013         
22014         if (i >= this.tabs.length - 1 && this.autoslide) {
22015             i = -1;
22016         }
22017         
22018         this.showPanel(this.tabs[i+1]);
22019     },
22020     
22021     showPanelPrev : function()
22022     {
22023         var i = this.indexOfPanel(this.getActivePanel());
22024         
22025         if (i  < 1 && !this.autoslide) {
22026             return;
22027         }
22028         
22029         if (i < 1 && this.autoslide) {
22030             i = this.tabs.length;
22031         }
22032         
22033         this.showPanel(this.tabs[i-1]);
22034     },
22035     
22036     
22037     addBullet: function()
22038     {
22039         if(!this.bullets || Roo.isTouch){
22040             return;
22041         }
22042         var ctr = this.el.select('.carousel-bullets',true).first();
22043         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22044         var bullet = ctr.createChild({
22045             cls : 'bullet bullet-' + i
22046         },ctr.dom.lastChild);
22047         
22048         
22049         var _this = this;
22050         
22051         bullet.on('click', (function(e, el, o, ii, t){
22052
22053             e.preventDefault();
22054
22055             this.showPanel(ii);
22056
22057             if(this.autoslide && this.slideFn){
22058                 clearInterval(this.slideFn);
22059                 this.slideFn = window.setInterval(function() {
22060                     _this.showPanelNext();
22061                 }, this.timer);
22062             }
22063
22064         }).createDelegate(this, [i, bullet], true));
22065                 
22066         
22067     },
22068      
22069     setActiveBullet : function(i)
22070     {
22071         if(Roo.isTouch){
22072             return;
22073         }
22074         
22075         Roo.each(this.el.select('.bullet', true).elements, function(el){
22076             el.removeClass('selected');
22077         });
22078
22079         var bullet = this.el.select('.bullet-' + i, true).first();
22080         
22081         if(!bullet){
22082             return;
22083         }
22084         
22085         bullet.addClass('selected');
22086     }
22087     
22088     
22089   
22090 });
22091
22092  
22093
22094  
22095  
22096 Roo.apply(Roo.bootstrap.TabGroup, {
22097     
22098     groups: {},
22099      /**
22100     * register a Navigation Group
22101     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22102     */
22103     register : function(navgrp)
22104     {
22105         this.groups[navgrp.navId] = navgrp;
22106         
22107     },
22108     /**
22109     * fetch a Navigation Group based on the navigation ID
22110     * if one does not exist , it will get created.
22111     * @param {string} the navgroup to add
22112     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22113     */
22114     get: function(navId) {
22115         if (typeof(this.groups[navId]) == 'undefined') {
22116             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22117         }
22118         return this.groups[navId] ;
22119     }
22120     
22121     
22122     
22123 });
22124
22125  /*
22126  * - LGPL
22127  *
22128  * TabPanel
22129  * 
22130  */
22131
22132 /**
22133  * @class Roo.bootstrap.TabPanel
22134  * @extends Roo.bootstrap.Component
22135  * @children Roo.bootstrap.Component
22136  * Bootstrap TabPanel class
22137  * @cfg {Boolean} active panel active
22138  * @cfg {String} html panel content
22139  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22140  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22141  * @cfg {String} href click to link..
22142  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22143  * 
22144  * 
22145  * @constructor
22146  * Create a new TabPanel
22147  * @param {Object} config The config object
22148  */
22149
22150 Roo.bootstrap.TabPanel = function(config){
22151     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22152     this.addEvents({
22153         /**
22154              * @event changed
22155              * Fires when the active status changes
22156              * @param {Roo.bootstrap.TabPanel} this
22157              * @param {Boolean} state the new state
22158             
22159          */
22160         'changed': true,
22161         /**
22162              * @event beforedeactivate
22163              * Fires before a tab is de-activated - can be used to do validation on a form.
22164              * @param {Roo.bootstrap.TabPanel} this
22165              * @return {Boolean} false if there is an error
22166             
22167          */
22168         'beforedeactivate': true
22169      });
22170     
22171     this.tabId = this.tabId || Roo.id();
22172   
22173 };
22174
22175 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22176     
22177     active: false,
22178     html: false,
22179     tabId: false,
22180     navId : false,
22181     href : '',
22182     touchSlide : false,
22183     getAutoCreate : function(){
22184         
22185         
22186         var cfg = {
22187             tag: 'div',
22188             // item is needed for carousel - not sure if it has any effect otherwise
22189             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22190             html: this.html || ''
22191         };
22192         
22193         if(this.active){
22194             cfg.cls += ' active';
22195         }
22196         
22197         if(this.tabId){
22198             cfg.tabId = this.tabId;
22199         }
22200         
22201         
22202         
22203         return cfg;
22204     },
22205     
22206     initEvents:  function()
22207     {
22208         var p = this.parent();
22209         
22210         this.navId = this.navId || p.navId;
22211         
22212         if (typeof(this.navId) != 'undefined') {
22213             // not really needed.. but just in case.. parent should be a NavGroup.
22214             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22215             
22216             tg.register(this);
22217             
22218             var i = tg.tabs.length - 1;
22219             
22220             if(this.active && tg.bullets > 0 && i < tg.bullets){
22221                 tg.setActiveBullet(i);
22222             }
22223         }
22224         
22225         this.el.on('click', this.onClick, this);
22226         
22227         if(Roo.isTouch && this.touchSlide){
22228             this.el.on("touchstart", this.onTouchStart, this);
22229             this.el.on("touchmove", this.onTouchMove, this);
22230             this.el.on("touchend", this.onTouchEnd, this);
22231         }
22232         
22233     },
22234     
22235     onRender : function(ct, position)
22236     {
22237         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22238     },
22239     
22240     setActive : function(state)
22241     {
22242         Roo.log("panel - set active " + this.tabId + "=" + state);
22243         
22244         this.active = state;
22245         if (!state) {
22246             this.el.removeClass('active');
22247             
22248         } else  if (!this.el.hasClass('active')) {
22249             this.el.addClass('active');
22250         }
22251         
22252         this.fireEvent('changed', this, state);
22253     },
22254     
22255     onClick : function(e)
22256     {
22257         e.preventDefault();
22258         
22259         if(!this.href.length){
22260             return;
22261         }
22262         
22263         window.location.href = this.href;
22264     },
22265     
22266     startX : 0,
22267     startY : 0,
22268     endX : 0,
22269     endY : 0,
22270     swiping : false,
22271     
22272     onTouchStart : function(e)
22273     {
22274         this.swiping = false;
22275         
22276         this.startX = e.browserEvent.touches[0].clientX;
22277         this.startY = e.browserEvent.touches[0].clientY;
22278     },
22279     
22280     onTouchMove : function(e)
22281     {
22282         this.swiping = true;
22283         
22284         this.endX = e.browserEvent.touches[0].clientX;
22285         this.endY = e.browserEvent.touches[0].clientY;
22286     },
22287     
22288     onTouchEnd : function(e)
22289     {
22290         if(!this.swiping){
22291             this.onClick(e);
22292             return;
22293         }
22294         
22295         var tabGroup = this.parent();
22296         
22297         if(this.endX > this.startX){ // swiping right
22298             tabGroup.showPanelPrev();
22299             return;
22300         }
22301         
22302         if(this.startX > this.endX){ // swiping left
22303             tabGroup.showPanelNext();
22304             return;
22305         }
22306     }
22307     
22308     
22309 });
22310  
22311
22312  
22313
22314  /*
22315  * - LGPL
22316  *
22317  * DateField
22318  * 
22319  */
22320
22321 /**
22322  * @class Roo.bootstrap.DateField
22323  * @extends Roo.bootstrap.Input
22324  * Bootstrap DateField class
22325  * @cfg {Number} weekStart default 0
22326  * @cfg {String} viewMode default empty, (months|years)
22327  * @cfg {String} minViewMode default empty, (months|years)
22328  * @cfg {Number} startDate default -Infinity
22329  * @cfg {Number} endDate default Infinity
22330  * @cfg {Boolean} todayHighlight default false
22331  * @cfg {Boolean} todayBtn default false
22332  * @cfg {Boolean} calendarWeeks default false
22333  * @cfg {Object} daysOfWeekDisabled default empty
22334  * @cfg {Boolean} singleMode default false (true | false)
22335  * 
22336  * @cfg {Boolean} keyboardNavigation default true
22337  * @cfg {String} language default en
22338  * 
22339  * @constructor
22340  * Create a new DateField
22341  * @param {Object} config The config object
22342  */
22343
22344 Roo.bootstrap.DateField = function(config){
22345     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22346      this.addEvents({
22347             /**
22348              * @event show
22349              * Fires when this field show.
22350              * @param {Roo.bootstrap.DateField} this
22351              * @param {Mixed} date The date value
22352              */
22353             show : true,
22354             /**
22355              * @event show
22356              * Fires when this field hide.
22357              * @param {Roo.bootstrap.DateField} this
22358              * @param {Mixed} date The date value
22359              */
22360             hide : true,
22361             /**
22362              * @event select
22363              * Fires when select a date.
22364              * @param {Roo.bootstrap.DateField} this
22365              * @param {Mixed} date The date value
22366              */
22367             select : true,
22368             /**
22369              * @event beforeselect
22370              * Fires when before select a date.
22371              * @param {Roo.bootstrap.DateField} this
22372              * @param {Mixed} date The date value
22373              */
22374             beforeselect : true
22375         });
22376 };
22377
22378 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22379     
22380     /**
22381      * @cfg {String} format
22382      * The default date format string which can be overriden for localization support.  The format must be
22383      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22384      */
22385     format : "m/d/y",
22386     /**
22387      * @cfg {String} altFormats
22388      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22389      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22390      */
22391     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22392     
22393     weekStart : 0,
22394     
22395     viewMode : '',
22396     
22397     minViewMode : '',
22398     
22399     todayHighlight : false,
22400     
22401     todayBtn: false,
22402     
22403     language: 'en',
22404     
22405     keyboardNavigation: true,
22406     
22407     calendarWeeks: false,
22408     
22409     startDate: -Infinity,
22410     
22411     endDate: Infinity,
22412     
22413     daysOfWeekDisabled: [],
22414     
22415     _events: [],
22416     
22417     singleMode : false,
22418     
22419     UTCDate: function()
22420     {
22421         return new Date(Date.UTC.apply(Date, arguments));
22422     },
22423     
22424     UTCToday: function()
22425     {
22426         var today = new Date();
22427         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22428     },
22429     
22430     getDate: function() {
22431             var d = this.getUTCDate();
22432             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22433     },
22434     
22435     getUTCDate: function() {
22436             return this.date;
22437     },
22438     
22439     setDate: function(d) {
22440             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22441     },
22442     
22443     setUTCDate: function(d) {
22444             this.date = d;
22445             this.setValue(this.formatDate(this.date));
22446     },
22447         
22448     onRender: function(ct, position)
22449     {
22450         
22451         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22452         
22453         this.language = this.language || 'en';
22454         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22455         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22456         
22457         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22458         this.format = this.format || 'm/d/y';
22459         this.isInline = false;
22460         this.isInput = true;
22461         this.component = this.el.select('.add-on', true).first() || false;
22462         this.component = (this.component && this.component.length === 0) ? false : this.component;
22463         this.hasInput = this.component && this.inputEl().length;
22464         
22465         if (typeof(this.minViewMode === 'string')) {
22466             switch (this.minViewMode) {
22467                 case 'months':
22468                     this.minViewMode = 1;
22469                     break;
22470                 case 'years':
22471                     this.minViewMode = 2;
22472                     break;
22473                 default:
22474                     this.minViewMode = 0;
22475                     break;
22476             }
22477         }
22478         
22479         if (typeof(this.viewMode === 'string')) {
22480             switch (this.viewMode) {
22481                 case 'months':
22482                     this.viewMode = 1;
22483                     break;
22484                 case 'years':
22485                     this.viewMode = 2;
22486                     break;
22487                 default:
22488                     this.viewMode = 0;
22489                     break;
22490             }
22491         }
22492                 
22493         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22494         
22495 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22496         
22497         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22498         
22499         this.picker().on('mousedown', this.onMousedown, this);
22500         this.picker().on('click', this.onClick, this);
22501         
22502         this.picker().addClass('datepicker-dropdown');
22503         
22504         this.startViewMode = this.viewMode;
22505         
22506         if(this.singleMode){
22507             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22508                 v.setVisibilityMode(Roo.Element.DISPLAY);
22509                 v.hide();
22510             });
22511             
22512             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22513                 v.setStyle('width', '189px');
22514             });
22515         }
22516         
22517         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22518             if(!this.calendarWeeks){
22519                 v.remove();
22520                 return;
22521             }
22522             
22523             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22524             v.attr('colspan', function(i, val){
22525                 return parseInt(val) + 1;
22526             });
22527         });
22528                         
22529         
22530         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22531         
22532         this.setStartDate(this.startDate);
22533         this.setEndDate(this.endDate);
22534         
22535         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22536         
22537         this.fillDow();
22538         this.fillMonths();
22539         this.update();
22540         this.showMode();
22541         
22542         if(this.isInline) {
22543             this.showPopup();
22544         }
22545     },
22546     
22547     picker : function()
22548     {
22549         return this.pickerEl;
22550 //        return this.el.select('.datepicker', true).first();
22551     },
22552     
22553     fillDow: function()
22554     {
22555         var dowCnt = this.weekStart;
22556         
22557         var dow = {
22558             tag: 'tr',
22559             cn: [
22560                 
22561             ]
22562         };
22563         
22564         if(this.calendarWeeks){
22565             dow.cn.push({
22566                 tag: 'th',
22567                 cls: 'cw',
22568                 html: '&nbsp;'
22569             })
22570         }
22571         
22572         while (dowCnt < this.weekStart + 7) {
22573             dow.cn.push({
22574                 tag: 'th',
22575                 cls: 'dow',
22576                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22577             });
22578         }
22579         
22580         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22581     },
22582     
22583     fillMonths: function()
22584     {    
22585         var i = 0;
22586         var months = this.picker().select('>.datepicker-months td', true).first();
22587         
22588         months.dom.innerHTML = '';
22589         
22590         while (i < 12) {
22591             var month = {
22592                 tag: 'span',
22593                 cls: 'month',
22594                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22595             };
22596             
22597             months.createChild(month);
22598         }
22599         
22600     },
22601     
22602     update: function()
22603     {
22604         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;
22605         
22606         if (this.date < this.startDate) {
22607             this.viewDate = new Date(this.startDate);
22608         } else if (this.date > this.endDate) {
22609             this.viewDate = new Date(this.endDate);
22610         } else {
22611             this.viewDate = new Date(this.date);
22612         }
22613         
22614         this.fill();
22615     },
22616     
22617     fill: function() 
22618     {
22619         var d = new Date(this.viewDate),
22620                 year = d.getUTCFullYear(),
22621                 month = d.getUTCMonth(),
22622                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22623                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22624                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22625                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22626                 currentDate = this.date && this.date.valueOf(),
22627                 today = this.UTCToday();
22628         
22629         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22630         
22631 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22632         
22633 //        this.picker.select('>tfoot th.today').
22634 //                                              .text(dates[this.language].today)
22635 //                                              .toggle(this.todayBtn !== false);
22636     
22637         this.updateNavArrows();
22638         this.fillMonths();
22639                                                 
22640         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22641         
22642         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22643          
22644         prevMonth.setUTCDate(day);
22645         
22646         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22647         
22648         var nextMonth = new Date(prevMonth);
22649         
22650         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22651         
22652         nextMonth = nextMonth.valueOf();
22653         
22654         var fillMonths = false;
22655         
22656         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22657         
22658         while(prevMonth.valueOf() <= nextMonth) {
22659             var clsName = '';
22660             
22661             if (prevMonth.getUTCDay() === this.weekStart) {
22662                 if(fillMonths){
22663                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22664                 }
22665                     
22666                 fillMonths = {
22667                     tag: 'tr',
22668                     cn: []
22669                 };
22670                 
22671                 if(this.calendarWeeks){
22672                     // ISO 8601: First week contains first thursday.
22673                     // ISO also states week starts on Monday, but we can be more abstract here.
22674                     var
22675                     // Start of current week: based on weekstart/current date
22676                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22677                     // Thursday of this week
22678                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22679                     // First Thursday of year, year from thursday
22680                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22681                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22682                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22683                     
22684                     fillMonths.cn.push({
22685                         tag: 'td',
22686                         cls: 'cw',
22687                         html: calWeek
22688                     });
22689                 }
22690             }
22691             
22692             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22693                 clsName += ' old';
22694             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22695                 clsName += ' new';
22696             }
22697             if (this.todayHighlight &&
22698                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22699                 prevMonth.getUTCMonth() == today.getMonth() &&
22700                 prevMonth.getUTCDate() == today.getDate()) {
22701                 clsName += ' today';
22702             }
22703             
22704             if (currentDate && prevMonth.valueOf() === currentDate) {
22705                 clsName += ' active';
22706             }
22707             
22708             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22709                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22710                     clsName += ' disabled';
22711             }
22712             
22713             fillMonths.cn.push({
22714                 tag: 'td',
22715                 cls: 'day ' + clsName,
22716                 html: prevMonth.getDate()
22717             });
22718             
22719             prevMonth.setDate(prevMonth.getDate()+1);
22720         }
22721           
22722         var currentYear = this.date && this.date.getUTCFullYear();
22723         var currentMonth = this.date && this.date.getUTCMonth();
22724         
22725         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22726         
22727         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22728             v.removeClass('active');
22729             
22730             if(currentYear === year && k === currentMonth){
22731                 v.addClass('active');
22732             }
22733             
22734             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22735                 v.addClass('disabled');
22736             }
22737             
22738         });
22739         
22740         
22741         year = parseInt(year/10, 10) * 10;
22742         
22743         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22744         
22745         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22746         
22747         year -= 1;
22748         for (var i = -1; i < 11; i++) {
22749             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22750                 tag: 'span',
22751                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22752                 html: year
22753             });
22754             
22755             year += 1;
22756         }
22757     },
22758     
22759     showMode: function(dir) 
22760     {
22761         if (dir) {
22762             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22763         }
22764         
22765         Roo.each(this.picker().select('>div',true).elements, function(v){
22766             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22767             v.hide();
22768         });
22769         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22770     },
22771     
22772     place: function()
22773     {
22774         if(this.isInline) {
22775             return;
22776         }
22777         
22778         this.picker().removeClass(['bottom', 'top']);
22779         
22780         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22781             /*
22782              * place to the top of element!
22783              *
22784              */
22785             
22786             this.picker().addClass('top');
22787             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22788             
22789             return;
22790         }
22791         
22792         this.picker().addClass('bottom');
22793         
22794         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22795     },
22796     
22797     parseDate : function(value)
22798     {
22799         if(!value || value instanceof Date){
22800             return value;
22801         }
22802         var v = Date.parseDate(value, this.format);
22803         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22804             v = Date.parseDate(value, 'Y-m-d');
22805         }
22806         if(!v && this.altFormats){
22807             if(!this.altFormatsArray){
22808                 this.altFormatsArray = this.altFormats.split("|");
22809             }
22810             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22811                 v = Date.parseDate(value, this.altFormatsArray[i]);
22812             }
22813         }
22814         return v;
22815     },
22816     
22817     formatDate : function(date, fmt)
22818     {   
22819         return (!date || !(date instanceof Date)) ?
22820         date : date.dateFormat(fmt || this.format);
22821     },
22822     
22823     onFocus : function()
22824     {
22825         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22826         this.showPopup();
22827     },
22828     
22829     onBlur : function()
22830     {
22831         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22832         
22833         var d = this.inputEl().getValue();
22834         
22835         this.setValue(d);
22836                 
22837         this.hidePopup();
22838     },
22839     
22840     showPopup : function()
22841     {
22842         this.picker().show();
22843         this.update();
22844         this.place();
22845         
22846         this.fireEvent('showpopup', this, this.date);
22847     },
22848     
22849     hidePopup : function()
22850     {
22851         if(this.isInline) {
22852             return;
22853         }
22854         this.picker().hide();
22855         this.viewMode = this.startViewMode;
22856         this.showMode();
22857         
22858         this.fireEvent('hidepopup', this, this.date);
22859         
22860     },
22861     
22862     onMousedown: function(e)
22863     {
22864         e.stopPropagation();
22865         e.preventDefault();
22866     },
22867     
22868     keyup: function(e)
22869     {
22870         Roo.bootstrap.DateField.superclass.keyup.call(this);
22871         this.update();
22872     },
22873
22874     setValue: function(v)
22875     {
22876         if(this.fireEvent('beforeselect', this, v) !== false){
22877             var d = new Date(this.parseDate(v) ).clearTime();
22878         
22879             if(isNaN(d.getTime())){
22880                 this.date = this.viewDate = '';
22881                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22882                 return;
22883             }
22884
22885             v = this.formatDate(d);
22886
22887             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22888
22889             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22890
22891             this.update();
22892
22893             this.fireEvent('select', this, this.date);
22894         }
22895     },
22896     
22897     getValue: function()
22898     {
22899         return this.formatDate(this.date);
22900     },
22901     
22902     fireKey: function(e)
22903     {
22904         if (!this.picker().isVisible()){
22905             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22906                 this.showPopup();
22907             }
22908             return;
22909         }
22910         
22911         var dateChanged = false,
22912         dir, day, month,
22913         newDate, newViewDate;
22914         
22915         switch(e.keyCode){
22916             case 27: // escape
22917                 this.hidePopup();
22918                 e.preventDefault();
22919                 break;
22920             case 37: // left
22921             case 39: // right
22922                 if (!this.keyboardNavigation) {
22923                     break;
22924                 }
22925                 dir = e.keyCode == 37 ? -1 : 1;
22926                 
22927                 if (e.ctrlKey){
22928                     newDate = this.moveYear(this.date, dir);
22929                     newViewDate = this.moveYear(this.viewDate, dir);
22930                 } else if (e.shiftKey){
22931                     newDate = this.moveMonth(this.date, dir);
22932                     newViewDate = this.moveMonth(this.viewDate, dir);
22933                 } else {
22934                     newDate = new Date(this.date);
22935                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22936                     newViewDate = new Date(this.viewDate);
22937                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22938                 }
22939                 if (this.dateWithinRange(newDate)){
22940                     this.date = newDate;
22941                     this.viewDate = newViewDate;
22942                     this.setValue(this.formatDate(this.date));
22943 //                    this.update();
22944                     e.preventDefault();
22945                     dateChanged = true;
22946                 }
22947                 break;
22948             case 38: // up
22949             case 40: // down
22950                 if (!this.keyboardNavigation) {
22951                     break;
22952                 }
22953                 dir = e.keyCode == 38 ? -1 : 1;
22954                 if (e.ctrlKey){
22955                     newDate = this.moveYear(this.date, dir);
22956                     newViewDate = this.moveYear(this.viewDate, dir);
22957                 } else if (e.shiftKey){
22958                     newDate = this.moveMonth(this.date, dir);
22959                     newViewDate = this.moveMonth(this.viewDate, dir);
22960                 } else {
22961                     newDate = new Date(this.date);
22962                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22963                     newViewDate = new Date(this.viewDate);
22964                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22965                 }
22966                 if (this.dateWithinRange(newDate)){
22967                     this.date = newDate;
22968                     this.viewDate = newViewDate;
22969                     this.setValue(this.formatDate(this.date));
22970 //                    this.update();
22971                     e.preventDefault();
22972                     dateChanged = true;
22973                 }
22974                 break;
22975             case 13: // enter
22976                 this.setValue(this.formatDate(this.date));
22977                 this.hidePopup();
22978                 e.preventDefault();
22979                 break;
22980             case 9: // tab
22981                 this.setValue(this.formatDate(this.date));
22982                 this.hidePopup();
22983                 break;
22984             case 16: // shift
22985             case 17: // ctrl
22986             case 18: // alt
22987                 break;
22988             default :
22989                 this.hidePopup();
22990                 
22991         }
22992     },
22993     
22994     
22995     onClick: function(e) 
22996     {
22997         e.stopPropagation();
22998         e.preventDefault();
22999         
23000         var target = e.getTarget();
23001         
23002         if(target.nodeName.toLowerCase() === 'i'){
23003             target = Roo.get(target).dom.parentNode;
23004         }
23005         
23006         var nodeName = target.nodeName;
23007         var className = target.className;
23008         var html = target.innerHTML;
23009         //Roo.log(nodeName);
23010         
23011         switch(nodeName.toLowerCase()) {
23012             case 'th':
23013                 switch(className) {
23014                     case 'switch':
23015                         this.showMode(1);
23016                         break;
23017                     case 'prev':
23018                     case 'next':
23019                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23020                         switch(this.viewMode){
23021                                 case 0:
23022                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23023                                         break;
23024                                 case 1:
23025                                 case 2:
23026                                         this.viewDate = this.moveYear(this.viewDate, dir);
23027                                         break;
23028                         }
23029                         this.fill();
23030                         break;
23031                     case 'today':
23032                         var date = new Date();
23033                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23034 //                        this.fill()
23035                         this.setValue(this.formatDate(this.date));
23036                         
23037                         this.hidePopup();
23038                         break;
23039                 }
23040                 break;
23041             case 'span':
23042                 if (className.indexOf('disabled') < 0) {
23043                 if (!this.viewDate) {
23044                     this.viewDate = new Date();
23045                 }
23046                 this.viewDate.setUTCDate(1);
23047                     if (className.indexOf('month') > -1) {
23048                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23049                     } else {
23050                         var year = parseInt(html, 10) || 0;
23051                         this.viewDate.setUTCFullYear(year);
23052                         
23053                     }
23054                     
23055                     if(this.singleMode){
23056                         this.setValue(this.formatDate(this.viewDate));
23057                         this.hidePopup();
23058                         return;
23059                     }
23060                     
23061                     this.showMode(-1);
23062                     this.fill();
23063                 }
23064                 break;
23065                 
23066             case 'td':
23067                 //Roo.log(className);
23068                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23069                     var day = parseInt(html, 10) || 1;
23070                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23071                         month = (this.viewDate || new Date()).getUTCMonth();
23072
23073                     if (className.indexOf('old') > -1) {
23074                         if(month === 0 ){
23075                             month = 11;
23076                             year -= 1;
23077                         }else{
23078                             month -= 1;
23079                         }
23080                     } else if (className.indexOf('new') > -1) {
23081                         if (month == 11) {
23082                             month = 0;
23083                             year += 1;
23084                         } else {
23085                             month += 1;
23086                         }
23087                     }
23088                     //Roo.log([year,month,day]);
23089                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23090                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23091 //                    this.fill();
23092                     //Roo.log(this.formatDate(this.date));
23093                     this.setValue(this.formatDate(this.date));
23094                     this.hidePopup();
23095                 }
23096                 break;
23097         }
23098     },
23099     
23100     setStartDate: function(startDate)
23101     {
23102         this.startDate = startDate || -Infinity;
23103         if (this.startDate !== -Infinity) {
23104             this.startDate = this.parseDate(this.startDate);
23105         }
23106         this.update();
23107         this.updateNavArrows();
23108     },
23109
23110     setEndDate: function(endDate)
23111     {
23112         this.endDate = endDate || Infinity;
23113         if (this.endDate !== Infinity) {
23114             this.endDate = this.parseDate(this.endDate);
23115         }
23116         this.update();
23117         this.updateNavArrows();
23118     },
23119     
23120     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23121     {
23122         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23123         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23124             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23125         }
23126         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23127             return parseInt(d, 10);
23128         });
23129         this.update();
23130         this.updateNavArrows();
23131     },
23132     
23133     updateNavArrows: function() 
23134     {
23135         if(this.singleMode){
23136             return;
23137         }
23138         
23139         var d = new Date(this.viewDate),
23140         year = d.getUTCFullYear(),
23141         month = d.getUTCMonth();
23142         
23143         Roo.each(this.picker().select('.prev', true).elements, function(v){
23144             v.show();
23145             switch (this.viewMode) {
23146                 case 0:
23147
23148                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23149                         v.hide();
23150                     }
23151                     break;
23152                 case 1:
23153                 case 2:
23154                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23155                         v.hide();
23156                     }
23157                     break;
23158             }
23159         });
23160         
23161         Roo.each(this.picker().select('.next', true).elements, function(v){
23162             v.show();
23163             switch (this.viewMode) {
23164                 case 0:
23165
23166                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23167                         v.hide();
23168                     }
23169                     break;
23170                 case 1:
23171                 case 2:
23172                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23173                         v.hide();
23174                     }
23175                     break;
23176             }
23177         })
23178     },
23179     
23180     moveMonth: function(date, dir)
23181     {
23182         if (!dir) {
23183             return date;
23184         }
23185         var new_date = new Date(date.valueOf()),
23186         day = new_date.getUTCDate(),
23187         month = new_date.getUTCMonth(),
23188         mag = Math.abs(dir),
23189         new_month, test;
23190         dir = dir > 0 ? 1 : -1;
23191         if (mag == 1){
23192             test = dir == -1
23193             // If going back one month, make sure month is not current month
23194             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23195             ? function(){
23196                 return new_date.getUTCMonth() == month;
23197             }
23198             // If going forward one month, make sure month is as expected
23199             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23200             : function(){
23201                 return new_date.getUTCMonth() != new_month;
23202             };
23203             new_month = month + dir;
23204             new_date.setUTCMonth(new_month);
23205             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23206             if (new_month < 0 || new_month > 11) {
23207                 new_month = (new_month + 12) % 12;
23208             }
23209         } else {
23210             // For magnitudes >1, move one month at a time...
23211             for (var i=0; i<mag; i++) {
23212                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23213                 new_date = this.moveMonth(new_date, dir);
23214             }
23215             // ...then reset the day, keeping it in the new month
23216             new_month = new_date.getUTCMonth();
23217             new_date.setUTCDate(day);
23218             test = function(){
23219                 return new_month != new_date.getUTCMonth();
23220             };
23221         }
23222         // Common date-resetting loop -- if date is beyond end of month, make it
23223         // end of month
23224         while (test()){
23225             new_date.setUTCDate(--day);
23226             new_date.setUTCMonth(new_month);
23227         }
23228         return new_date;
23229     },
23230
23231     moveYear: function(date, dir)
23232     {
23233         return this.moveMonth(date, dir*12);
23234     },
23235
23236     dateWithinRange: function(date)
23237     {
23238         return date >= this.startDate && date <= this.endDate;
23239     },
23240
23241     
23242     remove: function() 
23243     {
23244         this.picker().remove();
23245     },
23246     
23247     validateValue : function(value)
23248     {
23249         if(this.getVisibilityEl().hasClass('hidden')){
23250             return true;
23251         }
23252         
23253         if(value.length < 1)  {
23254             if(this.allowBlank){
23255                 return true;
23256             }
23257             return false;
23258         }
23259         
23260         if(value.length < this.minLength){
23261             return false;
23262         }
23263         if(value.length > this.maxLength){
23264             return false;
23265         }
23266         if(this.vtype){
23267             var vt = Roo.form.VTypes;
23268             if(!vt[this.vtype](value, this)){
23269                 return false;
23270             }
23271         }
23272         if(typeof this.validator == "function"){
23273             var msg = this.validator(value);
23274             if(msg !== true){
23275                 return false;
23276             }
23277         }
23278         
23279         if(this.regex && !this.regex.test(value)){
23280             return false;
23281         }
23282         
23283         if(typeof(this.parseDate(value)) == 'undefined'){
23284             return false;
23285         }
23286         
23287         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23288             return false;
23289         }      
23290         
23291         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23292             return false;
23293         } 
23294         
23295         
23296         return true;
23297     },
23298     
23299     reset : function()
23300     {
23301         this.date = this.viewDate = '';
23302         
23303         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23304     }
23305    
23306 });
23307
23308 Roo.apply(Roo.bootstrap.DateField,  {
23309     
23310     head : {
23311         tag: 'thead',
23312         cn: [
23313         {
23314             tag: 'tr',
23315             cn: [
23316             {
23317                 tag: 'th',
23318                 cls: 'prev',
23319                 html: '<i class="fa fa-arrow-left"/>'
23320             },
23321             {
23322                 tag: 'th',
23323                 cls: 'switch',
23324                 colspan: '5'
23325             },
23326             {
23327                 tag: 'th',
23328                 cls: 'next',
23329                 html: '<i class="fa fa-arrow-right"/>'
23330             }
23331
23332             ]
23333         }
23334         ]
23335     },
23336     
23337     content : {
23338         tag: 'tbody',
23339         cn: [
23340         {
23341             tag: 'tr',
23342             cn: [
23343             {
23344                 tag: 'td',
23345                 colspan: '7'
23346             }
23347             ]
23348         }
23349         ]
23350     },
23351     
23352     footer : {
23353         tag: 'tfoot',
23354         cn: [
23355         {
23356             tag: 'tr',
23357             cn: [
23358             {
23359                 tag: 'th',
23360                 colspan: '7',
23361                 cls: 'today'
23362             }
23363                     
23364             ]
23365         }
23366         ]
23367     },
23368     
23369     dates:{
23370         en: {
23371             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23372             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23373             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23374             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23375             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23376             today: "Today"
23377         }
23378     },
23379     
23380     modes: [
23381     {
23382         clsName: 'days',
23383         navFnc: 'Month',
23384         navStep: 1
23385     },
23386     {
23387         clsName: 'months',
23388         navFnc: 'FullYear',
23389         navStep: 1
23390     },
23391     {
23392         clsName: 'years',
23393         navFnc: 'FullYear',
23394         navStep: 10
23395     }]
23396 });
23397
23398 Roo.apply(Roo.bootstrap.DateField,  {
23399   
23400     template : {
23401         tag: 'div',
23402         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23403         cn: [
23404         {
23405             tag: 'div',
23406             cls: 'datepicker-days',
23407             cn: [
23408             {
23409                 tag: 'table',
23410                 cls: 'table-condensed',
23411                 cn:[
23412                 Roo.bootstrap.DateField.head,
23413                 {
23414                     tag: 'tbody'
23415                 },
23416                 Roo.bootstrap.DateField.footer
23417                 ]
23418             }
23419             ]
23420         },
23421         {
23422             tag: 'div',
23423             cls: 'datepicker-months',
23424             cn: [
23425             {
23426                 tag: 'table',
23427                 cls: 'table-condensed',
23428                 cn:[
23429                 Roo.bootstrap.DateField.head,
23430                 Roo.bootstrap.DateField.content,
23431                 Roo.bootstrap.DateField.footer
23432                 ]
23433             }
23434             ]
23435         },
23436         {
23437             tag: 'div',
23438             cls: 'datepicker-years',
23439             cn: [
23440             {
23441                 tag: 'table',
23442                 cls: 'table-condensed',
23443                 cn:[
23444                 Roo.bootstrap.DateField.head,
23445                 Roo.bootstrap.DateField.content,
23446                 Roo.bootstrap.DateField.footer
23447                 ]
23448             }
23449             ]
23450         }
23451         ]
23452     }
23453 });
23454
23455  
23456
23457  /*
23458  * - LGPL
23459  *
23460  * TimeField
23461  * 
23462  */
23463
23464 /**
23465  * @class Roo.bootstrap.TimeField
23466  * @extends Roo.bootstrap.Input
23467  * Bootstrap DateField class
23468  * 
23469  * 
23470  * @constructor
23471  * Create a new TimeField
23472  * @param {Object} config The config object
23473  */
23474
23475 Roo.bootstrap.TimeField = function(config){
23476     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23477     this.addEvents({
23478             /**
23479              * @event show
23480              * Fires when this field show.
23481              * @param {Roo.bootstrap.DateField} thisthis
23482              * @param {Mixed} date The date value
23483              */
23484             show : true,
23485             /**
23486              * @event show
23487              * Fires when this field hide.
23488              * @param {Roo.bootstrap.DateField} this
23489              * @param {Mixed} date The date value
23490              */
23491             hide : true,
23492             /**
23493              * @event select
23494              * Fires when select a date.
23495              * @param {Roo.bootstrap.DateField} this
23496              * @param {Mixed} date The date value
23497              */
23498             select : true
23499         });
23500 };
23501
23502 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23503     
23504     /**
23505      * @cfg {String} format
23506      * The default time format string which can be overriden for localization support.  The format must be
23507      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23508      */
23509     format : "H:i",
23510
23511     getAutoCreate : function()
23512     {
23513         this.after = '<i class="fa far fa-clock"></i>';
23514         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23515         
23516          
23517     },
23518     onRender: function(ct, position)
23519     {
23520         
23521         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23522                 
23523         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23524         
23525         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23526         
23527         this.pop = this.picker().select('>.datepicker-time',true).first();
23528         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23529         
23530         this.picker().on('mousedown', this.onMousedown, this);
23531         this.picker().on('click', this.onClick, this);
23532         
23533         this.picker().addClass('datepicker-dropdown');
23534     
23535         this.fillTime();
23536         this.update();
23537             
23538         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23539         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23540         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23541         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23542         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23543         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23544
23545     },
23546     
23547     fireKey: function(e){
23548         if (!this.picker().isVisible()){
23549             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23550                 this.show();
23551             }
23552             return;
23553         }
23554
23555         e.preventDefault();
23556         
23557         switch(e.keyCode){
23558             case 27: // escape
23559                 this.hide();
23560                 break;
23561             case 37: // left
23562             case 39: // right
23563                 this.onTogglePeriod();
23564                 break;
23565             case 38: // up
23566                 this.onIncrementMinutes();
23567                 break;
23568             case 40: // down
23569                 this.onDecrementMinutes();
23570                 break;
23571             case 13: // enter
23572             case 9: // tab
23573                 this.setTime();
23574                 break;
23575         }
23576     },
23577     
23578     onClick: function(e) {
23579         e.stopPropagation();
23580         e.preventDefault();
23581     },
23582     
23583     picker : function()
23584     {
23585         return this.pickerEl;
23586     },
23587     
23588     fillTime: function()
23589     {    
23590         var time = this.pop.select('tbody', true).first();
23591         
23592         time.dom.innerHTML = '';
23593         
23594         time.createChild({
23595             tag: 'tr',
23596             cn: [
23597                 {
23598                     tag: 'td',
23599                     cn: [
23600                         {
23601                             tag: 'a',
23602                             href: '#',
23603                             cls: 'btn',
23604                             cn: [
23605                                 {
23606                                     tag: 'i',
23607                                     cls: 'hours-up fa fas fa-chevron-up'
23608                                 }
23609                             ]
23610                         } 
23611                     ]
23612                 },
23613                 {
23614                     tag: 'td',
23615                     cls: 'separator'
23616                 },
23617                 {
23618                     tag: 'td',
23619                     cn: [
23620                         {
23621                             tag: 'a',
23622                             href: '#',
23623                             cls: 'btn',
23624                             cn: [
23625                                 {
23626                                     tag: 'i',
23627                                     cls: 'minutes-up fa fas fa-chevron-up'
23628                                 }
23629                             ]
23630                         }
23631                     ]
23632                 },
23633                 {
23634                     tag: 'td',
23635                     cls: 'separator'
23636                 }
23637             ]
23638         });
23639         
23640         time.createChild({
23641             tag: 'tr',
23642             cn: [
23643                 {
23644                     tag: 'td',
23645                     cn: [
23646                         {
23647                             tag: 'span',
23648                             cls: 'timepicker-hour',
23649                             html: '00'
23650                         }  
23651                     ]
23652                 },
23653                 {
23654                     tag: 'td',
23655                     cls: 'separator',
23656                     html: ':'
23657                 },
23658                 {
23659                     tag: 'td',
23660                     cn: [
23661                         {
23662                             tag: 'span',
23663                             cls: 'timepicker-minute',
23664                             html: '00'
23665                         }  
23666                     ]
23667                 },
23668                 {
23669                     tag: 'td',
23670                     cls: 'separator'
23671                 },
23672                 {
23673                     tag: 'td',
23674                     cn: [
23675                         {
23676                             tag: 'button',
23677                             type: 'button',
23678                             cls: 'btn btn-primary period',
23679                             html: 'AM'
23680                             
23681                         }
23682                     ]
23683                 }
23684             ]
23685         });
23686         
23687         time.createChild({
23688             tag: 'tr',
23689             cn: [
23690                 {
23691                     tag: 'td',
23692                     cn: [
23693                         {
23694                             tag: 'a',
23695                             href: '#',
23696                             cls: 'btn',
23697                             cn: [
23698                                 {
23699                                     tag: 'span',
23700                                     cls: 'hours-down fa fas fa-chevron-down'
23701                                 }
23702                             ]
23703                         }
23704                     ]
23705                 },
23706                 {
23707                     tag: 'td',
23708                     cls: 'separator'
23709                 },
23710                 {
23711                     tag: 'td',
23712                     cn: [
23713                         {
23714                             tag: 'a',
23715                             href: '#',
23716                             cls: 'btn',
23717                             cn: [
23718                                 {
23719                                     tag: 'span',
23720                                     cls: 'minutes-down fa fas fa-chevron-down'
23721                                 }
23722                             ]
23723                         }
23724                     ]
23725                 },
23726                 {
23727                     tag: 'td',
23728                     cls: 'separator'
23729                 }
23730             ]
23731         });
23732         
23733     },
23734     
23735     update: function()
23736     {
23737         
23738         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23739         
23740         this.fill();
23741     },
23742     
23743     fill: function() 
23744     {
23745         var hours = this.time.getHours();
23746         var minutes = this.time.getMinutes();
23747         var period = 'AM';
23748         
23749         if(hours > 11){
23750             period = 'PM';
23751         }
23752         
23753         if(hours == 0){
23754             hours = 12;
23755         }
23756         
23757         
23758         if(hours > 12){
23759             hours = hours - 12;
23760         }
23761         
23762         if(hours < 10){
23763             hours = '0' + hours;
23764         }
23765         
23766         if(minutes < 10){
23767             minutes = '0' + minutes;
23768         }
23769         
23770         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23771         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23772         this.pop.select('button', true).first().dom.innerHTML = period;
23773         
23774     },
23775     
23776     place: function()
23777     {   
23778         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23779         
23780         var cls = ['bottom'];
23781         
23782         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23783             cls.pop();
23784             cls.push('top');
23785         }
23786         
23787         cls.push('right');
23788         
23789         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23790             cls.pop();
23791             cls.push('left');
23792         }
23793         //this.picker().setXY(20000,20000);
23794         this.picker().addClass(cls.join('-'));
23795         
23796         var _this = this;
23797         
23798         Roo.each(cls, function(c){
23799             if(c == 'bottom'){
23800                 (function() {
23801                  //  
23802                 }).defer(200);
23803                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23804                 //_this.picker().setTop(_this.inputEl().getHeight());
23805                 return;
23806             }
23807             if(c == 'top'){
23808                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23809                 
23810                 //_this.picker().setTop(0 - _this.picker().getHeight());
23811                 return;
23812             }
23813             /*
23814             if(c == 'left'){
23815                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23816                 return;
23817             }
23818             if(c == 'right'){
23819                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23820                 return;
23821             }
23822             */
23823         });
23824         
23825     },
23826   
23827     onFocus : function()
23828     {
23829         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23830         this.show();
23831     },
23832     
23833     onBlur : function()
23834     {
23835         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23836         this.hide();
23837     },
23838     
23839     show : function()
23840     {
23841         this.picker().show();
23842         this.pop.show();
23843         this.update();
23844         this.place();
23845         
23846         this.fireEvent('show', this, this.date);
23847     },
23848     
23849     hide : function()
23850     {
23851         this.picker().hide();
23852         this.pop.hide();
23853         
23854         this.fireEvent('hide', this, this.date);
23855     },
23856     
23857     setTime : function()
23858     {
23859         this.hide();
23860         this.setValue(this.time.format(this.format));
23861         
23862         this.fireEvent('select', this, this.date);
23863         
23864         
23865     },
23866     
23867     onMousedown: function(e){
23868         e.stopPropagation();
23869         e.preventDefault();
23870     },
23871     
23872     onIncrementHours: function()
23873     {
23874         Roo.log('onIncrementHours');
23875         this.time = this.time.add(Date.HOUR, 1);
23876         this.update();
23877         
23878     },
23879     
23880     onDecrementHours: function()
23881     {
23882         Roo.log('onDecrementHours');
23883         this.time = this.time.add(Date.HOUR, -1);
23884         this.update();
23885     },
23886     
23887     onIncrementMinutes: function()
23888     {
23889         Roo.log('onIncrementMinutes');
23890         this.time = this.time.add(Date.MINUTE, 1);
23891         this.update();
23892     },
23893     
23894     onDecrementMinutes: function()
23895     {
23896         Roo.log('onDecrementMinutes');
23897         this.time = this.time.add(Date.MINUTE, -1);
23898         this.update();
23899     },
23900     
23901     onTogglePeriod: function()
23902     {
23903         Roo.log('onTogglePeriod');
23904         this.time = this.time.add(Date.HOUR, 12);
23905         this.update();
23906     }
23907     
23908    
23909 });
23910  
23911
23912 Roo.apply(Roo.bootstrap.TimeField,  {
23913   
23914     template : {
23915         tag: 'div',
23916         cls: 'datepicker dropdown-menu',
23917         cn: [
23918             {
23919                 tag: 'div',
23920                 cls: 'datepicker-time',
23921                 cn: [
23922                 {
23923                     tag: 'table',
23924                     cls: 'table-condensed',
23925                     cn:[
23926                         {
23927                             tag: 'tbody',
23928                             cn: [
23929                                 {
23930                                     tag: 'tr',
23931                                     cn: [
23932                                     {
23933                                         tag: 'td',
23934                                         colspan: '7'
23935                                     }
23936                                     ]
23937                                 }
23938                             ]
23939                         },
23940                         {
23941                             tag: 'tfoot',
23942                             cn: [
23943                                 {
23944                                     tag: 'tr',
23945                                     cn: [
23946                                     {
23947                                         tag: 'th',
23948                                         colspan: '7',
23949                                         cls: '',
23950                                         cn: [
23951                                             {
23952                                                 tag: 'button',
23953                                                 cls: 'btn btn-info ok',
23954                                                 html: 'OK'
23955                                             }
23956                                         ]
23957                                     }
23958                     
23959                                     ]
23960                                 }
23961                             ]
23962                         }
23963                     ]
23964                 }
23965                 ]
23966             }
23967         ]
23968     }
23969 });
23970
23971  
23972
23973  /*
23974  * - LGPL
23975  *
23976  * MonthField
23977  * 
23978  */
23979
23980 /**
23981  * @class Roo.bootstrap.MonthField
23982  * @extends Roo.bootstrap.Input
23983  * Bootstrap MonthField class
23984  * 
23985  * @cfg {String} language default en
23986  * 
23987  * @constructor
23988  * Create a new MonthField
23989  * @param {Object} config The config object
23990  */
23991
23992 Roo.bootstrap.MonthField = function(config){
23993     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23994     
23995     this.addEvents({
23996         /**
23997          * @event show
23998          * Fires when this field show.
23999          * @param {Roo.bootstrap.MonthField} this
24000          * @param {Mixed} date The date value
24001          */
24002         show : true,
24003         /**
24004          * @event show
24005          * Fires when this field hide.
24006          * @param {Roo.bootstrap.MonthField} this
24007          * @param {Mixed} date The date value
24008          */
24009         hide : true,
24010         /**
24011          * @event select
24012          * Fires when select a date.
24013          * @param {Roo.bootstrap.MonthField} this
24014          * @param {String} oldvalue The old value
24015          * @param {String} newvalue The new value
24016          */
24017         select : true
24018     });
24019 };
24020
24021 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24022     
24023     onRender: function(ct, position)
24024     {
24025         
24026         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24027         
24028         this.language = this.language || 'en';
24029         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24030         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24031         
24032         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24033         this.isInline = false;
24034         this.isInput = true;
24035         this.component = this.el.select('.add-on', true).first() || false;
24036         this.component = (this.component && this.component.length === 0) ? false : this.component;
24037         this.hasInput = this.component && this.inputEL().length;
24038         
24039         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24040         
24041         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24042         
24043         this.picker().on('mousedown', this.onMousedown, this);
24044         this.picker().on('click', this.onClick, this);
24045         
24046         this.picker().addClass('datepicker-dropdown');
24047         
24048         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24049             v.setStyle('width', '189px');
24050         });
24051         
24052         this.fillMonths();
24053         
24054         this.update();
24055         
24056         if(this.isInline) {
24057             this.show();
24058         }
24059         
24060     },
24061     
24062     setValue: function(v, suppressEvent)
24063     {   
24064         var o = this.getValue();
24065         
24066         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24067         
24068         this.update();
24069
24070         if(suppressEvent !== true){
24071             this.fireEvent('select', this, o, v);
24072         }
24073         
24074     },
24075     
24076     getValue: function()
24077     {
24078         return this.value;
24079     },
24080     
24081     onClick: function(e) 
24082     {
24083         e.stopPropagation();
24084         e.preventDefault();
24085         
24086         var target = e.getTarget();
24087         
24088         if(target.nodeName.toLowerCase() === 'i'){
24089             target = Roo.get(target).dom.parentNode;
24090         }
24091         
24092         var nodeName = target.nodeName;
24093         var className = target.className;
24094         var html = target.innerHTML;
24095         
24096         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24097             return;
24098         }
24099         
24100         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24101         
24102         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24103         
24104         this.hide();
24105                         
24106     },
24107     
24108     picker : function()
24109     {
24110         return this.pickerEl;
24111     },
24112     
24113     fillMonths: function()
24114     {    
24115         var i = 0;
24116         var months = this.picker().select('>.datepicker-months td', true).first();
24117         
24118         months.dom.innerHTML = '';
24119         
24120         while (i < 12) {
24121             var month = {
24122                 tag: 'span',
24123                 cls: 'month',
24124                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24125             };
24126             
24127             months.createChild(month);
24128         }
24129         
24130     },
24131     
24132     update: function()
24133     {
24134         var _this = this;
24135         
24136         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24137             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24138         }
24139         
24140         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24141             e.removeClass('active');
24142             
24143             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24144                 e.addClass('active');
24145             }
24146         })
24147     },
24148     
24149     place: function()
24150     {
24151         if(this.isInline) {
24152             return;
24153         }
24154         
24155         this.picker().removeClass(['bottom', 'top']);
24156         
24157         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24158             /*
24159              * place to the top of element!
24160              *
24161              */
24162             
24163             this.picker().addClass('top');
24164             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24165             
24166             return;
24167         }
24168         
24169         this.picker().addClass('bottom');
24170         
24171         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24172     },
24173     
24174     onFocus : function()
24175     {
24176         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24177         this.show();
24178     },
24179     
24180     onBlur : function()
24181     {
24182         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24183         
24184         var d = this.inputEl().getValue();
24185         
24186         this.setValue(d);
24187                 
24188         this.hide();
24189     },
24190     
24191     show : function()
24192     {
24193         this.picker().show();
24194         this.picker().select('>.datepicker-months', true).first().show();
24195         this.update();
24196         this.place();
24197         
24198         this.fireEvent('show', this, this.date);
24199     },
24200     
24201     hide : function()
24202     {
24203         if(this.isInline) {
24204             return;
24205         }
24206         this.picker().hide();
24207         this.fireEvent('hide', this, this.date);
24208         
24209     },
24210     
24211     onMousedown: function(e)
24212     {
24213         e.stopPropagation();
24214         e.preventDefault();
24215     },
24216     
24217     keyup: function(e)
24218     {
24219         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24220         this.update();
24221     },
24222
24223     fireKey: function(e)
24224     {
24225         if (!this.picker().isVisible()){
24226             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24227                 this.show();
24228             }
24229             return;
24230         }
24231         
24232         var dir;
24233         
24234         switch(e.keyCode){
24235             case 27: // escape
24236                 this.hide();
24237                 e.preventDefault();
24238                 break;
24239             case 37: // left
24240             case 39: // right
24241                 dir = e.keyCode == 37 ? -1 : 1;
24242                 
24243                 this.vIndex = this.vIndex + dir;
24244                 
24245                 if(this.vIndex < 0){
24246                     this.vIndex = 0;
24247                 }
24248                 
24249                 if(this.vIndex > 11){
24250                     this.vIndex = 11;
24251                 }
24252                 
24253                 if(isNaN(this.vIndex)){
24254                     this.vIndex = 0;
24255                 }
24256                 
24257                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24258                 
24259                 break;
24260             case 38: // up
24261             case 40: // down
24262                 
24263                 dir = e.keyCode == 38 ? -1 : 1;
24264                 
24265                 this.vIndex = this.vIndex + dir * 4;
24266                 
24267                 if(this.vIndex < 0){
24268                     this.vIndex = 0;
24269                 }
24270                 
24271                 if(this.vIndex > 11){
24272                     this.vIndex = 11;
24273                 }
24274                 
24275                 if(isNaN(this.vIndex)){
24276                     this.vIndex = 0;
24277                 }
24278                 
24279                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24280                 break;
24281                 
24282             case 13: // enter
24283                 
24284                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24285                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24286                 }
24287                 
24288                 this.hide();
24289                 e.preventDefault();
24290                 break;
24291             case 9: // tab
24292                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24293                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24294                 }
24295                 this.hide();
24296                 break;
24297             case 16: // shift
24298             case 17: // ctrl
24299             case 18: // alt
24300                 break;
24301             default :
24302                 this.hide();
24303                 
24304         }
24305     },
24306     
24307     remove: function() 
24308     {
24309         this.picker().remove();
24310     }
24311    
24312 });
24313
24314 Roo.apply(Roo.bootstrap.MonthField,  {
24315     
24316     content : {
24317         tag: 'tbody',
24318         cn: [
24319         {
24320             tag: 'tr',
24321             cn: [
24322             {
24323                 tag: 'td',
24324                 colspan: '7'
24325             }
24326             ]
24327         }
24328         ]
24329     },
24330     
24331     dates:{
24332         en: {
24333             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24334             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24335         }
24336     }
24337 });
24338
24339 Roo.apply(Roo.bootstrap.MonthField,  {
24340   
24341     template : {
24342         tag: 'div',
24343         cls: 'datepicker dropdown-menu roo-dynamic',
24344         cn: [
24345             {
24346                 tag: 'div',
24347                 cls: 'datepicker-months',
24348                 cn: [
24349                 {
24350                     tag: 'table',
24351                     cls: 'table-condensed',
24352                     cn:[
24353                         Roo.bootstrap.DateField.content
24354                     ]
24355                 }
24356                 ]
24357             }
24358         ]
24359     }
24360 });
24361
24362  
24363
24364  
24365  /*
24366  * - LGPL
24367  *
24368  * CheckBox
24369  * 
24370  */
24371
24372 /**
24373  * @class Roo.bootstrap.CheckBox
24374  * @extends Roo.bootstrap.Input
24375  * Bootstrap CheckBox class
24376  * 
24377  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24378  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24379  * @cfg {String} boxLabel The text that appears beside the checkbox
24380  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24381  * @cfg {Boolean} checked initnal the element
24382  * @cfg {Boolean} inline inline the element (default false)
24383  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24384  * @cfg {String} tooltip label tooltip
24385  * 
24386  * @constructor
24387  * Create a new CheckBox
24388  * @param {Object} config The config object
24389  */
24390
24391 Roo.bootstrap.CheckBox = function(config){
24392     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24393    
24394     this.addEvents({
24395         /**
24396         * @event check
24397         * Fires when the element is checked or unchecked.
24398         * @param {Roo.bootstrap.CheckBox} this This input
24399         * @param {Boolean} checked The new checked value
24400         */
24401        check : true,
24402        /**
24403         * @event click
24404         * Fires when the element is click.
24405         * @param {Roo.bootstrap.CheckBox} this This input
24406         */
24407        click : true
24408     });
24409     
24410 };
24411
24412 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24413   
24414     inputType: 'checkbox',
24415     inputValue: 1,
24416     valueOff: 0,
24417     boxLabel: false,
24418     checked: false,
24419     weight : false,
24420     inline: false,
24421     tooltip : '',
24422     
24423     // checkbox success does not make any sense really.. 
24424     invalidClass : "",
24425     validClass : "",
24426     
24427     
24428     getAutoCreate : function()
24429     {
24430         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24431         
24432         var id = Roo.id();
24433         
24434         var cfg = {};
24435         
24436         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24437         
24438         if(this.inline){
24439             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24440         }
24441         
24442         var input =  {
24443             tag: 'input',
24444             id : id,
24445             type : this.inputType,
24446             value : this.inputValue,
24447             cls : 'roo-' + this.inputType, //'form-box',
24448             placeholder : this.placeholder || ''
24449             
24450         };
24451         
24452         if(this.inputType != 'radio'){
24453             var hidden =  {
24454                 tag: 'input',
24455                 type : 'hidden',
24456                 cls : 'roo-hidden-value',
24457                 value : this.checked ? this.inputValue : this.valueOff
24458             };
24459         }
24460         
24461             
24462         if (this.weight) { // Validity check?
24463             cfg.cls += " " + this.inputType + "-" + this.weight;
24464         }
24465         
24466         if (this.disabled) {
24467             input.disabled=true;
24468         }
24469         
24470         if(this.checked){
24471             input.checked = this.checked;
24472         }
24473         
24474         if (this.name) {
24475             
24476             input.name = this.name;
24477             
24478             if(this.inputType != 'radio'){
24479                 hidden.name = this.name;
24480                 input.name = '_hidden_' + this.name;
24481             }
24482         }
24483         
24484         if (this.size) {
24485             input.cls += ' input-' + this.size;
24486         }
24487         
24488         var settings=this;
24489         
24490         ['xs','sm','md','lg'].map(function(size){
24491             if (settings[size]) {
24492                 cfg.cls += ' col-' + size + '-' + settings[size];
24493             }
24494         });
24495         
24496         var inputblock = input;
24497          
24498         if (this.before || this.after) {
24499             
24500             inputblock = {
24501                 cls : 'input-group',
24502                 cn :  [] 
24503             };
24504             
24505             if (this.before) {
24506                 inputblock.cn.push({
24507                     tag :'span',
24508                     cls : 'input-group-addon',
24509                     html : this.before
24510                 });
24511             }
24512             
24513             inputblock.cn.push(input);
24514             
24515             if(this.inputType != 'radio'){
24516                 inputblock.cn.push(hidden);
24517             }
24518             
24519             if (this.after) {
24520                 inputblock.cn.push({
24521                     tag :'span',
24522                     cls : 'input-group-addon',
24523                     html : this.after
24524                 });
24525             }
24526             
24527         }
24528         var boxLabelCfg = false;
24529         
24530         if(this.boxLabel){
24531            
24532             boxLabelCfg = {
24533                 tag: 'label',
24534                 //'for': id, // box label is handled by onclick - so no for...
24535                 cls: 'box-label',
24536                 html: this.boxLabel
24537             };
24538             if(this.tooltip){
24539                 boxLabelCfg.tooltip = this.tooltip;
24540             }
24541              
24542         }
24543         
24544         
24545         if (align ==='left' && this.fieldLabel.length) {
24546 //                Roo.log("left and has label");
24547             cfg.cn = [
24548                 {
24549                     tag: 'label',
24550                     'for' :  id,
24551                     cls : 'control-label',
24552                     html : this.fieldLabel
24553                 },
24554                 {
24555                     cls : "", 
24556                     cn: [
24557                         inputblock
24558                     ]
24559                 }
24560             ];
24561             
24562             if (boxLabelCfg) {
24563                 cfg.cn[1].cn.push(boxLabelCfg);
24564             }
24565             
24566             if(this.labelWidth > 12){
24567                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24568             }
24569             
24570             if(this.labelWidth < 13 && this.labelmd == 0){
24571                 this.labelmd = this.labelWidth;
24572             }
24573             
24574             if(this.labellg > 0){
24575                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24576                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24577             }
24578             
24579             if(this.labelmd > 0){
24580                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24581                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24582             }
24583             
24584             if(this.labelsm > 0){
24585                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24586                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24587             }
24588             
24589             if(this.labelxs > 0){
24590                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24591                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24592             }
24593             
24594         } else if ( this.fieldLabel.length) {
24595 //                Roo.log(" label");
24596                 cfg.cn = [
24597                    
24598                     {
24599                         tag: this.boxLabel ? 'span' : 'label',
24600                         'for': id,
24601                         cls: 'control-label box-input-label',
24602                         //cls : 'input-group-addon',
24603                         html : this.fieldLabel
24604                     },
24605                     
24606                     inputblock
24607                     
24608                 ];
24609                 if (boxLabelCfg) {
24610                     cfg.cn.push(boxLabelCfg);
24611                 }
24612
24613         } else {
24614             
24615 //                Roo.log(" no label && no align");
24616                 cfg.cn = [  inputblock ] ;
24617                 if (boxLabelCfg) {
24618                     cfg.cn.push(boxLabelCfg);
24619                 }
24620
24621                 
24622         }
24623         
24624        
24625         
24626         if(this.inputType != 'radio'){
24627             cfg.cn.push(hidden);
24628         }
24629         
24630         return cfg;
24631         
24632     },
24633     
24634     /**
24635      * return the real input element.
24636      */
24637     inputEl: function ()
24638     {
24639         return this.el.select('input.roo-' + this.inputType,true).first();
24640     },
24641     hiddenEl: function ()
24642     {
24643         return this.el.select('input.roo-hidden-value',true).first();
24644     },
24645     
24646     labelEl: function()
24647     {
24648         return this.el.select('label.control-label',true).first();
24649     },
24650     /* depricated... */
24651     
24652     label: function()
24653     {
24654         return this.labelEl();
24655     },
24656     
24657     boxLabelEl: function()
24658     {
24659         return this.el.select('label.box-label',true).first();
24660     },
24661     
24662     initEvents : function()
24663     {
24664 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24665         
24666         this.inputEl().on('click', this.onClick,  this);
24667         
24668         if (this.boxLabel) { 
24669             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24670         }
24671         
24672         this.startValue = this.getValue();
24673         
24674         if(this.groupId){
24675             Roo.bootstrap.CheckBox.register(this);
24676         }
24677     },
24678     
24679     onClick : function(e)
24680     {   
24681         if(this.fireEvent('click', this, e) !== false){
24682             this.setChecked(!this.checked);
24683         }
24684         
24685     },
24686     
24687     setChecked : function(state,suppressEvent)
24688     {
24689         this.startValue = this.getValue();
24690
24691         if(this.inputType == 'radio'){
24692             
24693             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24694                 e.dom.checked = false;
24695             });
24696             
24697             this.inputEl().dom.checked = true;
24698             
24699             this.inputEl().dom.value = this.inputValue;
24700             
24701             if(suppressEvent !== true){
24702                 this.fireEvent('check', this, true);
24703             }
24704             
24705             this.validate();
24706             
24707             return;
24708         }
24709         
24710         this.checked = state;
24711         
24712         this.inputEl().dom.checked = state;
24713         
24714         
24715         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24716         
24717         if(suppressEvent !== true){
24718             this.fireEvent('check', this, state);
24719         }
24720         
24721         this.validate();
24722     },
24723     
24724     getValue : function()
24725     {
24726         if(this.inputType == 'radio'){
24727             return this.getGroupValue();
24728         }
24729         
24730         return this.hiddenEl().dom.value;
24731         
24732     },
24733     
24734     getGroupValue : function()
24735     {
24736         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24737             return '';
24738         }
24739         
24740         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24741     },
24742     
24743     setValue : function(v,suppressEvent)
24744     {
24745         if(this.inputType == 'radio'){
24746             this.setGroupValue(v, suppressEvent);
24747             return;
24748         }
24749         
24750         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24751         
24752         this.validate();
24753     },
24754     
24755     setGroupValue : function(v, suppressEvent)
24756     {
24757         this.startValue = this.getValue();
24758         
24759         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24760             e.dom.checked = false;
24761             
24762             if(e.dom.value == v){
24763                 e.dom.checked = true;
24764             }
24765         });
24766         
24767         if(suppressEvent !== true){
24768             this.fireEvent('check', this, true);
24769         }
24770
24771         this.validate();
24772         
24773         return;
24774     },
24775     
24776     validate : function()
24777     {
24778         if(this.getVisibilityEl().hasClass('hidden')){
24779             return true;
24780         }
24781         
24782         if(
24783                 this.disabled || 
24784                 (this.inputType == 'radio' && this.validateRadio()) ||
24785                 (this.inputType == 'checkbox' && this.validateCheckbox())
24786         ){
24787             this.markValid();
24788             return true;
24789         }
24790         
24791         this.markInvalid();
24792         return false;
24793     },
24794     
24795     validateRadio : function()
24796     {
24797         if(this.getVisibilityEl().hasClass('hidden')){
24798             return true;
24799         }
24800         
24801         if(this.allowBlank){
24802             return true;
24803         }
24804         
24805         var valid = false;
24806         
24807         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24808             if(!e.dom.checked){
24809                 return;
24810             }
24811             
24812             valid = true;
24813             
24814             return false;
24815         });
24816         
24817         return valid;
24818     },
24819     
24820     validateCheckbox : function()
24821     {
24822         if(!this.groupId){
24823             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24824             //return (this.getValue() == this.inputValue) ? true : false;
24825         }
24826         
24827         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24828         
24829         if(!group){
24830             return false;
24831         }
24832         
24833         var r = false;
24834         
24835         for(var i in group){
24836             if(group[i].el.isVisible(true)){
24837                 r = false;
24838                 break;
24839             }
24840             
24841             r = true;
24842         }
24843         
24844         for(var i in group){
24845             if(r){
24846                 break;
24847             }
24848             
24849             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24850         }
24851         
24852         return r;
24853     },
24854     
24855     /**
24856      * Mark this field as valid
24857      */
24858     markValid : function()
24859     {
24860         var _this = this;
24861         
24862         this.fireEvent('valid', this);
24863         
24864         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24865         
24866         if(this.groupId){
24867             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24868         }
24869         
24870         if(label){
24871             label.markValid();
24872         }
24873
24874         if(this.inputType == 'radio'){
24875             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24876                 var fg = e.findParent('.form-group', false, true);
24877                 if (Roo.bootstrap.version == 3) {
24878                     fg.removeClass([_this.invalidClass, _this.validClass]);
24879                     fg.addClass(_this.validClass);
24880                 } else {
24881                     fg.removeClass(['is-valid', 'is-invalid']);
24882                     fg.addClass('is-valid');
24883                 }
24884             });
24885             
24886             return;
24887         }
24888
24889         if(!this.groupId){
24890             var fg = this.el.findParent('.form-group', false, true);
24891             if (Roo.bootstrap.version == 3) {
24892                 fg.removeClass([this.invalidClass, this.validClass]);
24893                 fg.addClass(this.validClass);
24894             } else {
24895                 fg.removeClass(['is-valid', 'is-invalid']);
24896                 fg.addClass('is-valid');
24897             }
24898             return;
24899         }
24900         
24901         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24902         
24903         if(!group){
24904             return;
24905         }
24906         
24907         for(var i in group){
24908             var fg = group[i].el.findParent('.form-group', false, true);
24909             if (Roo.bootstrap.version == 3) {
24910                 fg.removeClass([this.invalidClass, this.validClass]);
24911                 fg.addClass(this.validClass);
24912             } else {
24913                 fg.removeClass(['is-valid', 'is-invalid']);
24914                 fg.addClass('is-valid');
24915             }
24916         }
24917     },
24918     
24919      /**
24920      * Mark this field as invalid
24921      * @param {String} msg The validation message
24922      */
24923     markInvalid : function(msg)
24924     {
24925         if(this.allowBlank){
24926             return;
24927         }
24928         
24929         var _this = this;
24930         
24931         this.fireEvent('invalid', this, msg);
24932         
24933         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24934         
24935         if(this.groupId){
24936             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24937         }
24938         
24939         if(label){
24940             label.markInvalid();
24941         }
24942             
24943         if(this.inputType == 'radio'){
24944             
24945             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24946                 var fg = e.findParent('.form-group', false, true);
24947                 if (Roo.bootstrap.version == 3) {
24948                     fg.removeClass([_this.invalidClass, _this.validClass]);
24949                     fg.addClass(_this.invalidClass);
24950                 } else {
24951                     fg.removeClass(['is-invalid', 'is-valid']);
24952                     fg.addClass('is-invalid');
24953                 }
24954             });
24955             
24956             return;
24957         }
24958         
24959         if(!this.groupId){
24960             var fg = this.el.findParent('.form-group', false, true);
24961             if (Roo.bootstrap.version == 3) {
24962                 fg.removeClass([_this.invalidClass, _this.validClass]);
24963                 fg.addClass(_this.invalidClass);
24964             } else {
24965                 fg.removeClass(['is-invalid', 'is-valid']);
24966                 fg.addClass('is-invalid');
24967             }
24968             return;
24969         }
24970         
24971         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24972         
24973         if(!group){
24974             return;
24975         }
24976         
24977         for(var i in group){
24978             var fg = group[i].el.findParent('.form-group', false, true);
24979             if (Roo.bootstrap.version == 3) {
24980                 fg.removeClass([_this.invalidClass, _this.validClass]);
24981                 fg.addClass(_this.invalidClass);
24982             } else {
24983                 fg.removeClass(['is-invalid', 'is-valid']);
24984                 fg.addClass('is-invalid');
24985             }
24986         }
24987         
24988     },
24989     
24990     clearInvalid : function()
24991     {
24992         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24993         
24994         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24995         
24996         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24997         
24998         if (label && label.iconEl) {
24999             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25000             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25001         }
25002     },
25003     
25004     disable : function()
25005     {
25006         if(this.inputType != 'radio'){
25007             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25008             return;
25009         }
25010         
25011         var _this = this;
25012         
25013         if(this.rendered){
25014             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25015                 _this.getActionEl().addClass(this.disabledClass);
25016                 e.dom.disabled = true;
25017             });
25018         }
25019         
25020         this.disabled = true;
25021         this.fireEvent("disable", this);
25022         return this;
25023     },
25024
25025     enable : function()
25026     {
25027         if(this.inputType != 'radio'){
25028             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25029             return;
25030         }
25031         
25032         var _this = this;
25033         
25034         if(this.rendered){
25035             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25036                 _this.getActionEl().removeClass(this.disabledClass);
25037                 e.dom.disabled = false;
25038             });
25039         }
25040         
25041         this.disabled = false;
25042         this.fireEvent("enable", this);
25043         return this;
25044     },
25045     
25046     setBoxLabel : function(v)
25047     {
25048         this.boxLabel = v;
25049         
25050         if(this.rendered){
25051             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25052         }
25053     }
25054
25055 });
25056
25057 Roo.apply(Roo.bootstrap.CheckBox, {
25058     
25059     groups: {},
25060     
25061      /**
25062     * register a CheckBox Group
25063     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25064     */
25065     register : function(checkbox)
25066     {
25067         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25068             this.groups[checkbox.groupId] = {};
25069         }
25070         
25071         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25072             return;
25073         }
25074         
25075         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25076         
25077     },
25078     /**
25079     * fetch a CheckBox Group based on the group ID
25080     * @param {string} the group ID
25081     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25082     */
25083     get: function(groupId) {
25084         if (typeof(this.groups[groupId]) == 'undefined') {
25085             return false;
25086         }
25087         
25088         return this.groups[groupId] ;
25089     }
25090     
25091     
25092 });
25093 /*
25094  * - LGPL
25095  *
25096  * RadioItem
25097  * 
25098  */
25099
25100 /**
25101  * @class Roo.bootstrap.Radio
25102  * @extends Roo.bootstrap.Component
25103  * Bootstrap Radio class
25104  * @cfg {String} boxLabel - the label associated
25105  * @cfg {String} value - the value of radio
25106  * 
25107  * @constructor
25108  * Create a new Radio
25109  * @param {Object} config The config object
25110  */
25111 Roo.bootstrap.Radio = function(config){
25112     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25113     
25114 };
25115
25116 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25117     
25118     boxLabel : '',
25119     
25120     value : '',
25121     
25122     getAutoCreate : function()
25123     {
25124         var cfg = {
25125             tag : 'div',
25126             cls : 'form-group radio',
25127             cn : [
25128                 {
25129                     tag : 'label',
25130                     cls : 'box-label',
25131                     html : this.boxLabel
25132                 }
25133             ]
25134         };
25135         
25136         return cfg;
25137     },
25138     
25139     initEvents : function() 
25140     {
25141         this.parent().register(this);
25142         
25143         this.el.on('click', this.onClick, this);
25144         
25145     },
25146     
25147     onClick : function(e)
25148     {
25149         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25150             this.setChecked(true);
25151         }
25152     },
25153     
25154     setChecked : function(state, suppressEvent)
25155     {
25156         this.parent().setValue(this.value, suppressEvent);
25157         
25158     },
25159     
25160     setBoxLabel : function(v)
25161     {
25162         this.boxLabel = v;
25163         
25164         if(this.rendered){
25165             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25166         }
25167     }
25168     
25169 });
25170  
25171
25172  /*
25173  * - LGPL
25174  *
25175  * Input
25176  * 
25177  */
25178
25179 /**
25180  * @class Roo.bootstrap.SecurePass
25181  * @extends Roo.bootstrap.Input
25182  * Bootstrap SecurePass class
25183  *
25184  * 
25185  * @constructor
25186  * Create a new SecurePass
25187  * @param {Object} config The config object
25188  */
25189  
25190 Roo.bootstrap.SecurePass = function (config) {
25191     // these go here, so the translation tool can replace them..
25192     this.errors = {
25193         PwdEmpty: "Please type a password, and then retype it to confirm.",
25194         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25195         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25196         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25197         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25198         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25199         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25200         TooWeak: "Your password is Too Weak."
25201     },
25202     this.meterLabel = "Password strength:";
25203     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25204     this.meterClass = [
25205         "roo-password-meter-tooweak", 
25206         "roo-password-meter-weak", 
25207         "roo-password-meter-medium", 
25208         "roo-password-meter-strong", 
25209         "roo-password-meter-grey"
25210     ];
25211     
25212     this.errors = {};
25213     
25214     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25215 }
25216
25217 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25218     /**
25219      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25220      * {
25221      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25222      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25223      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25224      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25225      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25226      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25227      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25228      * })
25229      */
25230     // private
25231     
25232     meterWidth: 300,
25233     errorMsg :'',    
25234     errors: false,
25235     imageRoot: '/',
25236     /**
25237      * @cfg {String/Object} Label for the strength meter (defaults to
25238      * 'Password strength:')
25239      */
25240     // private
25241     meterLabel: '',
25242     /**
25243      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25244      * ['Weak', 'Medium', 'Strong'])
25245      */
25246     // private    
25247     pwdStrengths: false,    
25248     // private
25249     strength: 0,
25250     // private
25251     _lastPwd: null,
25252     // private
25253     kCapitalLetter: 0,
25254     kSmallLetter: 1,
25255     kDigit: 2,
25256     kPunctuation: 3,
25257     
25258     insecure: false,
25259     // private
25260     initEvents: function ()
25261     {
25262         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25263
25264         if (this.el.is('input[type=password]') && Roo.isSafari) {
25265             this.el.on('keydown', this.SafariOnKeyDown, this);
25266         }
25267
25268         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25269     },
25270     // private
25271     onRender: function (ct, position)
25272     {
25273         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25274         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25275         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25276
25277         this.trigger.createChild({
25278                    cn: [
25279                     {
25280                     //id: 'PwdMeter',
25281                     tag: 'div',
25282                     cls: 'roo-password-meter-grey col-xs-12',
25283                     style: {
25284                         //width: 0,
25285                         //width: this.meterWidth + 'px'                                                
25286                         }
25287                     },
25288                     {                            
25289                          cls: 'roo-password-meter-text'                          
25290                     }
25291                 ]            
25292         });
25293
25294          
25295         if (this.hideTrigger) {
25296             this.trigger.setDisplayed(false);
25297         }
25298         this.setSize(this.width || '', this.height || '');
25299     },
25300     // private
25301     onDestroy: function ()
25302     {
25303         if (this.trigger) {
25304             this.trigger.removeAllListeners();
25305             this.trigger.remove();
25306         }
25307         if (this.wrap) {
25308             this.wrap.remove();
25309         }
25310         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25311     },
25312     // private
25313     checkStrength: function ()
25314     {
25315         var pwd = this.inputEl().getValue();
25316         if (pwd == this._lastPwd) {
25317             return;
25318         }
25319
25320         var strength;
25321         if (this.ClientSideStrongPassword(pwd)) {
25322             strength = 3;
25323         } else if (this.ClientSideMediumPassword(pwd)) {
25324             strength = 2;
25325         } else if (this.ClientSideWeakPassword(pwd)) {
25326             strength = 1;
25327         } else {
25328             strength = 0;
25329         }
25330         
25331         Roo.log('strength1: ' + strength);
25332         
25333         //var pm = this.trigger.child('div/div/div').dom;
25334         var pm = this.trigger.child('div/div');
25335         pm.removeClass(this.meterClass);
25336         pm.addClass(this.meterClass[strength]);
25337                 
25338         
25339         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25340                 
25341         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25342         
25343         this._lastPwd = pwd;
25344     },
25345     reset: function ()
25346     {
25347         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25348         
25349         this._lastPwd = '';
25350         
25351         var pm = this.trigger.child('div/div');
25352         pm.removeClass(this.meterClass);
25353         pm.addClass('roo-password-meter-grey');        
25354         
25355         
25356         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25357         
25358         pt.innerHTML = '';
25359         this.inputEl().dom.type='password';
25360     },
25361     // private
25362     validateValue: function (value)
25363     {
25364         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25365             return false;
25366         }
25367         if (value.length == 0) {
25368             if (this.allowBlank) {
25369                 this.clearInvalid();
25370                 return true;
25371             }
25372
25373             this.markInvalid(this.errors.PwdEmpty);
25374             this.errorMsg = this.errors.PwdEmpty;
25375             return false;
25376         }
25377         
25378         if(this.insecure){
25379             return true;
25380         }
25381         
25382         if (!value.match(/[\x21-\x7e]+/)) {
25383             this.markInvalid(this.errors.PwdBadChar);
25384             this.errorMsg = this.errors.PwdBadChar;
25385             return false;
25386         }
25387         if (value.length < 6) {
25388             this.markInvalid(this.errors.PwdShort);
25389             this.errorMsg = this.errors.PwdShort;
25390             return false;
25391         }
25392         if (value.length > 16) {
25393             this.markInvalid(this.errors.PwdLong);
25394             this.errorMsg = this.errors.PwdLong;
25395             return false;
25396         }
25397         var strength;
25398         if (this.ClientSideStrongPassword(value)) {
25399             strength = 3;
25400         } else if (this.ClientSideMediumPassword(value)) {
25401             strength = 2;
25402         } else if (this.ClientSideWeakPassword(value)) {
25403             strength = 1;
25404         } else {
25405             strength = 0;
25406         }
25407
25408         
25409         if (strength < 2) {
25410             //this.markInvalid(this.errors.TooWeak);
25411             this.errorMsg = this.errors.TooWeak;
25412             //return false;
25413         }
25414         
25415         
25416         console.log('strength2: ' + strength);
25417         
25418         //var pm = this.trigger.child('div/div/div').dom;
25419         
25420         var pm = this.trigger.child('div/div');
25421         pm.removeClass(this.meterClass);
25422         pm.addClass(this.meterClass[strength]);
25423                 
25424         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25425                 
25426         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25427         
25428         this.errorMsg = ''; 
25429         return true;
25430     },
25431     // private
25432     CharacterSetChecks: function (type)
25433     {
25434         this.type = type;
25435         this.fResult = false;
25436     },
25437     // private
25438     isctype: function (character, type)
25439     {
25440         switch (type) {  
25441             case this.kCapitalLetter:
25442                 if (character >= 'A' && character <= 'Z') {
25443                     return true;
25444                 }
25445                 break;
25446             
25447             case this.kSmallLetter:
25448                 if (character >= 'a' && character <= 'z') {
25449                     return true;
25450                 }
25451                 break;
25452             
25453             case this.kDigit:
25454                 if (character >= '0' && character <= '9') {
25455                     return true;
25456                 }
25457                 break;
25458             
25459             case this.kPunctuation:
25460                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25461                     return true;
25462                 }
25463                 break;
25464             
25465             default:
25466                 return false;
25467         }
25468
25469     },
25470     // private
25471     IsLongEnough: function (pwd, size)
25472     {
25473         return !(pwd == null || isNaN(size) || pwd.length < size);
25474     },
25475     // private
25476     SpansEnoughCharacterSets: function (word, nb)
25477     {
25478         if (!this.IsLongEnough(word, nb))
25479         {
25480             return false;
25481         }
25482
25483         var characterSetChecks = new Array(
25484             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25485             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25486         );
25487         
25488         for (var index = 0; index < word.length; ++index) {
25489             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25490                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25491                     characterSetChecks[nCharSet].fResult = true;
25492                     break;
25493                 }
25494             }
25495         }
25496
25497         var nCharSets = 0;
25498         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25499             if (characterSetChecks[nCharSet].fResult) {
25500                 ++nCharSets;
25501             }
25502         }
25503
25504         if (nCharSets < nb) {
25505             return false;
25506         }
25507         return true;
25508     },
25509     // private
25510     ClientSideStrongPassword: function (pwd)
25511     {
25512         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25513     },
25514     // private
25515     ClientSideMediumPassword: function (pwd)
25516     {
25517         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25518     },
25519     // private
25520     ClientSideWeakPassword: function (pwd)
25521     {
25522         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25523     }
25524           
25525 })//<script type="text/javascript">
25526
25527 /*
25528  * Based  Ext JS Library 1.1.1
25529  * Copyright(c) 2006-2007, Ext JS, LLC.
25530  * LGPL
25531  *
25532  */
25533  
25534 /**
25535  * @class Roo.HtmlEditorCore
25536  * @extends Roo.Component
25537  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25538  *
25539  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25540  */
25541
25542 Roo.HtmlEditorCore = function(config){
25543     
25544     
25545     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25546     
25547     
25548     this.addEvents({
25549         /**
25550          * @event initialize
25551          * Fires when the editor is fully initialized (including the iframe)
25552          * @param {Roo.HtmlEditorCore} this
25553          */
25554         initialize: true,
25555         /**
25556          * @event activate
25557          * Fires when the editor is first receives the focus. Any insertion must wait
25558          * until after this event.
25559          * @param {Roo.HtmlEditorCore} this
25560          */
25561         activate: true,
25562          /**
25563          * @event beforesync
25564          * Fires before the textarea is updated with content from the editor iframe. Return false
25565          * to cancel the sync.
25566          * @param {Roo.HtmlEditorCore} this
25567          * @param {String} html
25568          */
25569         beforesync: true,
25570          /**
25571          * @event beforepush
25572          * Fires before the iframe editor is updated with content from the textarea. Return false
25573          * to cancel the push.
25574          * @param {Roo.HtmlEditorCore} this
25575          * @param {String} html
25576          */
25577         beforepush: true,
25578          /**
25579          * @event sync
25580          * Fires when the textarea is updated with content from the editor iframe.
25581          * @param {Roo.HtmlEditorCore} this
25582          * @param {String} html
25583          */
25584         sync: true,
25585          /**
25586          * @event push
25587          * Fires when the iframe editor is updated with content from the textarea.
25588          * @param {Roo.HtmlEditorCore} this
25589          * @param {String} html
25590          */
25591         push: true,
25592         
25593         /**
25594          * @event editorevent
25595          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25596          * @param {Roo.HtmlEditorCore} this
25597          */
25598         editorevent: true
25599         
25600     });
25601     
25602     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25603     
25604     // defaults : white / black...
25605     this.applyBlacklists();
25606     
25607     
25608     
25609 };
25610
25611
25612 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25613
25614
25615      /**
25616      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25617      */
25618     
25619     owner : false,
25620     
25621      /**
25622      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25623      *                        Roo.resizable.
25624      */
25625     resizable : false,
25626      /**
25627      * @cfg {Number} height (in pixels)
25628      */   
25629     height: 300,
25630    /**
25631      * @cfg {Number} width (in pixels)
25632      */   
25633     width: 500,
25634     
25635     /**
25636      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25637      * 
25638      */
25639     stylesheets: false,
25640     
25641     /**
25642      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25643      */
25644     allowComments: false,
25645     // id of frame..
25646     frameId: false,
25647     
25648     // private properties
25649     validationEvent : false,
25650     deferHeight: true,
25651     initialized : false,
25652     activated : false,
25653     sourceEditMode : false,
25654     onFocus : Roo.emptyFn,
25655     iframePad:3,
25656     hideMode:'offsets',
25657     
25658     clearUp: true,
25659     
25660     // blacklist + whitelisted elements..
25661     black: false,
25662     white: false,
25663      
25664     bodyCls : '',
25665
25666     /**
25667      * Protected method that will not generally be called directly. It
25668      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25669      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25670      */
25671     getDocMarkup : function(){
25672         // body styles..
25673         var st = '';
25674         
25675         // inherit styels from page...?? 
25676         if (this.stylesheets === false) {
25677             
25678             Roo.get(document.head).select('style').each(function(node) {
25679                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25680             });
25681             
25682             Roo.get(document.head).select('link').each(function(node) { 
25683                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25684             });
25685             
25686         } else if (!this.stylesheets.length) {
25687                 // simple..
25688                 st = '<style type="text/css">' +
25689                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25690                    '</style>';
25691         } else {
25692             for (var i in this.stylesheets) { 
25693                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25694             }
25695             
25696         }
25697         
25698         st +=  '<style type="text/css">' +
25699             'IMG { cursor: pointer } ' +
25700         '</style>';
25701
25702         var cls = 'roo-htmleditor-body';
25703         
25704         if(this.bodyCls.length){
25705             cls += ' ' + this.bodyCls;
25706         }
25707         
25708         return '<html><head>' + st  +
25709             //<style type="text/css">' +
25710             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25711             //'</style>' +
25712             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25713     },
25714
25715     // private
25716     onRender : function(ct, position)
25717     {
25718         var _t = this;
25719         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25720         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25721         
25722         
25723         this.el.dom.style.border = '0 none';
25724         this.el.dom.setAttribute('tabIndex', -1);
25725         this.el.addClass('x-hidden hide');
25726         
25727         
25728         
25729         if(Roo.isIE){ // fix IE 1px bogus margin
25730             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25731         }
25732        
25733         
25734         this.frameId = Roo.id();
25735         
25736          
25737         
25738         var iframe = this.owner.wrap.createChild({
25739             tag: 'iframe',
25740             cls: 'form-control', // bootstrap..
25741             id: this.frameId,
25742             name: this.frameId,
25743             frameBorder : 'no',
25744             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25745         }, this.el
25746         );
25747         
25748         
25749         this.iframe = iframe.dom;
25750
25751          this.assignDocWin();
25752         
25753         this.doc.designMode = 'on';
25754        
25755         this.doc.open();
25756         this.doc.write(this.getDocMarkup());
25757         this.doc.close();
25758
25759         
25760         var task = { // must defer to wait for browser to be ready
25761             run : function(){
25762                 //console.log("run task?" + this.doc.readyState);
25763                 this.assignDocWin();
25764                 if(this.doc.body || this.doc.readyState == 'complete'){
25765                     try {
25766                         this.doc.designMode="on";
25767                     } catch (e) {
25768                         return;
25769                     }
25770                     Roo.TaskMgr.stop(task);
25771                     this.initEditor.defer(10, this);
25772                 }
25773             },
25774             interval : 10,
25775             duration: 10000,
25776             scope: this
25777         };
25778         Roo.TaskMgr.start(task);
25779
25780     },
25781
25782     // private
25783     onResize : function(w, h)
25784     {
25785          Roo.log('resize: ' +w + ',' + h );
25786         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25787         if(!this.iframe){
25788             return;
25789         }
25790         if(typeof w == 'number'){
25791             
25792             this.iframe.style.width = w + 'px';
25793         }
25794         if(typeof h == 'number'){
25795             
25796             this.iframe.style.height = h + 'px';
25797             if(this.doc){
25798                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25799             }
25800         }
25801         
25802     },
25803
25804     /**
25805      * Toggles the editor between standard and source edit mode.
25806      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25807      */
25808     toggleSourceEdit : function(sourceEditMode){
25809         
25810         this.sourceEditMode = sourceEditMode === true;
25811         
25812         if(this.sourceEditMode){
25813  
25814             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25815             
25816         }else{
25817             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25818             //this.iframe.className = '';
25819             this.deferFocus();
25820         }
25821         //this.setSize(this.owner.wrap.getSize());
25822         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25823     },
25824
25825     
25826   
25827
25828     /**
25829      * Protected method that will not generally be called directly. If you need/want
25830      * custom HTML cleanup, this is the method you should override.
25831      * @param {String} html The HTML to be cleaned
25832      * return {String} The cleaned HTML
25833      */
25834     cleanHtml : function(html){
25835         html = String(html);
25836         if(html.length > 5){
25837             if(Roo.isSafari){ // strip safari nonsense
25838                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25839             }
25840         }
25841         if(html == '&nbsp;'){
25842             html = '';
25843         }
25844         return html;
25845     },
25846
25847     /**
25848      * HTML Editor -> Textarea
25849      * Protected method that will not generally be called directly. Syncs the contents
25850      * of the editor iframe with the textarea.
25851      */
25852     syncValue : function(){
25853         if(this.initialized){
25854             var bd = (this.doc.body || this.doc.documentElement);
25855             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25856             var html = bd.innerHTML;
25857             if(Roo.isSafari){
25858                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25859                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25860                 if(m && m[1]){
25861                     html = '<div style="'+m[0]+'">' + html + '</div>';
25862                 }
25863             }
25864             html = this.cleanHtml(html);
25865             // fix up the special chars.. normaly like back quotes in word...
25866             // however we do not want to do this with chinese..
25867             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25868                 
25869                 var cc = match.charCodeAt();
25870
25871                 // Get the character value, handling surrogate pairs
25872                 if (match.length == 2) {
25873                     // It's a surrogate pair, calculate the Unicode code point
25874                     var high = match.charCodeAt(0) - 0xD800;
25875                     var low  = match.charCodeAt(1) - 0xDC00;
25876                     cc = (high * 0x400) + low + 0x10000;
25877                 }  else if (
25878                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25879                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25880                     (cc >= 0xf900 && cc < 0xfb00 )
25881                 ) {
25882                         return match;
25883                 }  
25884          
25885                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25886                 return "&#" + cc + ";";
25887                 
25888                 
25889             });
25890             
25891             
25892              
25893             if(this.owner.fireEvent('beforesync', this, html) !== false){
25894                 this.el.dom.value = html;
25895                 this.owner.fireEvent('sync', this, html);
25896             }
25897         }
25898     },
25899
25900     /**
25901      * Protected method that will not generally be called directly. Pushes the value of the textarea
25902      * into the iframe editor.
25903      */
25904     pushValue : function(){
25905         if(this.initialized){
25906             var v = this.el.dom.value.trim();
25907             
25908 //            if(v.length < 1){
25909 //                v = '&#160;';
25910 //            }
25911             
25912             if(this.owner.fireEvent('beforepush', this, v) !== false){
25913                 var d = (this.doc.body || this.doc.documentElement);
25914                 d.innerHTML = v;
25915                 this.cleanUpPaste();
25916                 this.el.dom.value = d.innerHTML;
25917                 this.owner.fireEvent('push', this, v);
25918             }
25919         }
25920     },
25921
25922     // private
25923     deferFocus : function(){
25924         this.focus.defer(10, this);
25925     },
25926
25927     // doc'ed in Field
25928     focus : function(){
25929         if(this.win && !this.sourceEditMode){
25930             this.win.focus();
25931         }else{
25932             this.el.focus();
25933         }
25934     },
25935     
25936     assignDocWin: function()
25937     {
25938         var iframe = this.iframe;
25939         
25940          if(Roo.isIE){
25941             this.doc = iframe.contentWindow.document;
25942             this.win = iframe.contentWindow;
25943         } else {
25944 //            if (!Roo.get(this.frameId)) {
25945 //                return;
25946 //            }
25947 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25948 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25949             
25950             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25951                 return;
25952             }
25953             
25954             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25955             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25956         }
25957     },
25958     
25959     // private
25960     initEditor : function(){
25961         //console.log("INIT EDITOR");
25962         this.assignDocWin();
25963         
25964         
25965         
25966         this.doc.designMode="on";
25967         this.doc.open();
25968         this.doc.write(this.getDocMarkup());
25969         this.doc.close();
25970         
25971         var dbody = (this.doc.body || this.doc.documentElement);
25972         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25973         // this copies styles from the containing element into thsi one..
25974         // not sure why we need all of this..
25975         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25976         
25977         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25978         //ss['background-attachment'] = 'fixed'; // w3c
25979         dbody.bgProperties = 'fixed'; // ie
25980         //Roo.DomHelper.applyStyles(dbody, ss);
25981         Roo.EventManager.on(this.doc, {
25982             //'mousedown': this.onEditorEvent,
25983             'mouseup': this.onEditorEvent,
25984             'dblclick': this.onEditorEvent,
25985             'click': this.onEditorEvent,
25986             'keyup': this.onEditorEvent,
25987             buffer:100,
25988             scope: this
25989         });
25990         if(Roo.isGecko){
25991             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25992         }
25993         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25994             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25995         }
25996         this.initialized = true;
25997
25998         this.owner.fireEvent('initialize', this);
25999         this.pushValue();
26000     },
26001
26002     // private
26003     onDestroy : function(){
26004         
26005         
26006         
26007         if(this.rendered){
26008             
26009             //for (var i =0; i < this.toolbars.length;i++) {
26010             //    // fixme - ask toolbars for heights?
26011             //    this.toolbars[i].onDestroy();
26012            // }
26013             
26014             //this.wrap.dom.innerHTML = '';
26015             //this.wrap.remove();
26016         }
26017     },
26018
26019     // private
26020     onFirstFocus : function(){
26021         
26022         this.assignDocWin();
26023         
26024         
26025         this.activated = true;
26026          
26027     
26028         if(Roo.isGecko){ // prevent silly gecko errors
26029             this.win.focus();
26030             var s = this.win.getSelection();
26031             if(!s.focusNode || s.focusNode.nodeType != 3){
26032                 var r = s.getRangeAt(0);
26033                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26034                 r.collapse(true);
26035                 this.deferFocus();
26036             }
26037             try{
26038                 this.execCmd('useCSS', true);
26039                 this.execCmd('styleWithCSS', false);
26040             }catch(e){}
26041         }
26042         this.owner.fireEvent('activate', this);
26043     },
26044
26045     // private
26046     adjustFont: function(btn){
26047         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26048         //if(Roo.isSafari){ // safari
26049         //    adjust *= 2;
26050        // }
26051         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26052         if(Roo.isSafari){ // safari
26053             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26054             v =  (v < 10) ? 10 : v;
26055             v =  (v > 48) ? 48 : v;
26056             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26057             
26058         }
26059         
26060         
26061         v = Math.max(1, v+adjust);
26062         
26063         this.execCmd('FontSize', v  );
26064     },
26065
26066     onEditorEvent : function(e)
26067     {
26068         this.owner.fireEvent('editorevent', this, e);
26069       //  this.updateToolbar();
26070         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26071     },
26072
26073     insertTag : function(tg)
26074     {
26075         // could be a bit smarter... -> wrap the current selected tRoo..
26076         if (tg.toLowerCase() == 'span' ||
26077             tg.toLowerCase() == 'code' ||
26078             tg.toLowerCase() == 'sup' ||
26079             tg.toLowerCase() == 'sub' 
26080             ) {
26081             
26082             range = this.createRange(this.getSelection());
26083             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26084             wrappingNode.appendChild(range.extractContents());
26085             range.insertNode(wrappingNode);
26086
26087             return;
26088             
26089             
26090             
26091         }
26092         this.execCmd("formatblock",   tg);
26093         
26094     },
26095     
26096     insertText : function(txt)
26097     {
26098         
26099         
26100         var range = this.createRange();
26101         range.deleteContents();
26102                //alert(Sender.getAttribute('label'));
26103                
26104         range.insertNode(this.doc.createTextNode(txt));
26105     } ,
26106     
26107      
26108
26109     /**
26110      * Executes a Midas editor command on the editor document and performs necessary focus and
26111      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26112      * @param {String} cmd The Midas command
26113      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26114      */
26115     relayCmd : function(cmd, value){
26116         this.win.focus();
26117         this.execCmd(cmd, value);
26118         this.owner.fireEvent('editorevent', this);
26119         //this.updateToolbar();
26120         this.owner.deferFocus();
26121     },
26122
26123     /**
26124      * Executes a Midas editor command directly on the editor document.
26125      * For visual commands, you should use {@link #relayCmd} instead.
26126      * <b>This should only be called after the editor is initialized.</b>
26127      * @param {String} cmd The Midas command
26128      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26129      */
26130     execCmd : function(cmd, value){
26131         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26132         this.syncValue();
26133     },
26134  
26135  
26136    
26137     /**
26138      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26139      * to insert tRoo.
26140      * @param {String} text | dom node.. 
26141      */
26142     insertAtCursor : function(text)
26143     {
26144         
26145         if(!this.activated){
26146             return;
26147         }
26148         /*
26149         if(Roo.isIE){
26150             this.win.focus();
26151             var r = this.doc.selection.createRange();
26152             if(r){
26153                 r.collapse(true);
26154                 r.pasteHTML(text);
26155                 this.syncValue();
26156                 this.deferFocus();
26157             
26158             }
26159             return;
26160         }
26161         */
26162         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26163             this.win.focus();
26164             
26165             
26166             // from jquery ui (MIT licenced)
26167             var range, node;
26168             var win = this.win;
26169             
26170             if (win.getSelection && win.getSelection().getRangeAt) {
26171                 range = win.getSelection().getRangeAt(0);
26172                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26173                 range.insertNode(node);
26174             } else if (win.document.selection && win.document.selection.createRange) {
26175                 // no firefox support
26176                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26177                 win.document.selection.createRange().pasteHTML(txt);
26178             } else {
26179                 // no firefox support
26180                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26181                 this.execCmd('InsertHTML', txt);
26182             } 
26183             
26184             this.syncValue();
26185             
26186             this.deferFocus();
26187         }
26188     },
26189  // private
26190     mozKeyPress : function(e){
26191         if(e.ctrlKey){
26192             var c = e.getCharCode(), cmd;
26193           
26194             if(c > 0){
26195                 c = String.fromCharCode(c).toLowerCase();
26196                 switch(c){
26197                     case 'b':
26198                         cmd = 'bold';
26199                         break;
26200                     case 'i':
26201                         cmd = 'italic';
26202                         break;
26203                     
26204                     case 'u':
26205                         cmd = 'underline';
26206                         break;
26207                     
26208                     case 'v':
26209                         this.cleanUpPaste.defer(100, this);
26210                         return;
26211                         
26212                 }
26213                 if(cmd){
26214                     this.win.focus();
26215                     this.execCmd(cmd);
26216                     this.deferFocus();
26217                     e.preventDefault();
26218                 }
26219                 
26220             }
26221         }
26222     },
26223
26224     // private
26225     fixKeys : function(){ // load time branching for fastest keydown performance
26226         if(Roo.isIE){
26227             return function(e){
26228                 var k = e.getKey(), r;
26229                 if(k == e.TAB){
26230                     e.stopEvent();
26231                     r = this.doc.selection.createRange();
26232                     if(r){
26233                         r.collapse(true);
26234                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26235                         this.deferFocus();
26236                     }
26237                     return;
26238                 }
26239                 
26240                 if(k == e.ENTER){
26241                     r = this.doc.selection.createRange();
26242                     if(r){
26243                         var target = r.parentElement();
26244                         if(!target || target.tagName.toLowerCase() != 'li'){
26245                             e.stopEvent();
26246                             r.pasteHTML('<br />');
26247                             r.collapse(false);
26248                             r.select();
26249                         }
26250                     }
26251                 }
26252                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26253                     this.cleanUpPaste.defer(100, this);
26254                     return;
26255                 }
26256                 
26257                 
26258             };
26259         }else if(Roo.isOpera){
26260             return function(e){
26261                 var k = e.getKey();
26262                 if(k == e.TAB){
26263                     e.stopEvent();
26264                     this.win.focus();
26265                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26266                     this.deferFocus();
26267                 }
26268                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26269                     this.cleanUpPaste.defer(100, this);
26270                     return;
26271                 }
26272                 
26273             };
26274         }else if(Roo.isSafari){
26275             return function(e){
26276                 var k = e.getKey();
26277                 
26278                 if(k == e.TAB){
26279                     e.stopEvent();
26280                     this.execCmd('InsertText','\t');
26281                     this.deferFocus();
26282                     return;
26283                 }
26284                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26285                     this.cleanUpPaste.defer(100, this);
26286                     return;
26287                 }
26288                 
26289              };
26290         }
26291     }(),
26292     
26293     getAllAncestors: function()
26294     {
26295         var p = this.getSelectedNode();
26296         var a = [];
26297         if (!p) {
26298             a.push(p); // push blank onto stack..
26299             p = this.getParentElement();
26300         }
26301         
26302         
26303         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26304             a.push(p);
26305             p = p.parentNode;
26306         }
26307         a.push(this.doc.body);
26308         return a;
26309     },
26310     lastSel : false,
26311     lastSelNode : false,
26312     
26313     
26314     getSelection : function() 
26315     {
26316         this.assignDocWin();
26317         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26318     },
26319     
26320     getSelectedNode: function() 
26321     {
26322         // this may only work on Gecko!!!
26323         
26324         // should we cache this!!!!
26325         
26326         
26327         
26328          
26329         var range = this.createRange(this.getSelection()).cloneRange();
26330         
26331         if (Roo.isIE) {
26332             var parent = range.parentElement();
26333             while (true) {
26334                 var testRange = range.duplicate();
26335                 testRange.moveToElementText(parent);
26336                 if (testRange.inRange(range)) {
26337                     break;
26338                 }
26339                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26340                     break;
26341                 }
26342                 parent = parent.parentElement;
26343             }
26344             return parent;
26345         }
26346         
26347         // is ancestor a text element.
26348         var ac =  range.commonAncestorContainer;
26349         if (ac.nodeType == 3) {
26350             ac = ac.parentNode;
26351         }
26352         
26353         var ar = ac.childNodes;
26354          
26355         var nodes = [];
26356         var other_nodes = [];
26357         var has_other_nodes = false;
26358         for (var i=0;i<ar.length;i++) {
26359             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26360                 continue;
26361             }
26362             // fullly contained node.
26363             
26364             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26365                 nodes.push(ar[i]);
26366                 continue;
26367             }
26368             
26369             // probably selected..
26370             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26371                 other_nodes.push(ar[i]);
26372                 continue;
26373             }
26374             // outer..
26375             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26376                 continue;
26377             }
26378             
26379             
26380             has_other_nodes = true;
26381         }
26382         if (!nodes.length && other_nodes.length) {
26383             nodes= other_nodes;
26384         }
26385         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26386             return false;
26387         }
26388         
26389         return nodes[0];
26390     },
26391     createRange: function(sel)
26392     {
26393         // this has strange effects when using with 
26394         // top toolbar - not sure if it's a great idea.
26395         //this.editor.contentWindow.focus();
26396         if (typeof sel != "undefined") {
26397             try {
26398                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26399             } catch(e) {
26400                 return this.doc.createRange();
26401             }
26402         } else {
26403             return this.doc.createRange();
26404         }
26405     },
26406     getParentElement: function()
26407     {
26408         
26409         this.assignDocWin();
26410         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26411         
26412         var range = this.createRange(sel);
26413          
26414         try {
26415             var p = range.commonAncestorContainer;
26416             while (p.nodeType == 3) { // text node
26417                 p = p.parentNode;
26418             }
26419             return p;
26420         } catch (e) {
26421             return null;
26422         }
26423     
26424     },
26425     /***
26426      *
26427      * Range intersection.. the hard stuff...
26428      *  '-1' = before
26429      *  '0' = hits..
26430      *  '1' = after.
26431      *         [ -- selected range --- ]
26432      *   [fail]                        [fail]
26433      *
26434      *    basically..
26435      *      if end is before start or  hits it. fail.
26436      *      if start is after end or hits it fail.
26437      *
26438      *   if either hits (but other is outside. - then it's not 
26439      *   
26440      *    
26441      **/
26442     
26443     
26444     // @see http://www.thismuchiknow.co.uk/?p=64.
26445     rangeIntersectsNode : function(range, node)
26446     {
26447         var nodeRange = node.ownerDocument.createRange();
26448         try {
26449             nodeRange.selectNode(node);
26450         } catch (e) {
26451             nodeRange.selectNodeContents(node);
26452         }
26453     
26454         var rangeStartRange = range.cloneRange();
26455         rangeStartRange.collapse(true);
26456     
26457         var rangeEndRange = range.cloneRange();
26458         rangeEndRange.collapse(false);
26459     
26460         var nodeStartRange = nodeRange.cloneRange();
26461         nodeStartRange.collapse(true);
26462     
26463         var nodeEndRange = nodeRange.cloneRange();
26464         nodeEndRange.collapse(false);
26465     
26466         return rangeStartRange.compareBoundaryPoints(
26467                  Range.START_TO_START, nodeEndRange) == -1 &&
26468                rangeEndRange.compareBoundaryPoints(
26469                  Range.START_TO_START, nodeStartRange) == 1;
26470         
26471          
26472     },
26473     rangeCompareNode : function(range, node)
26474     {
26475         var nodeRange = node.ownerDocument.createRange();
26476         try {
26477             nodeRange.selectNode(node);
26478         } catch (e) {
26479             nodeRange.selectNodeContents(node);
26480         }
26481         
26482         
26483         range.collapse(true);
26484     
26485         nodeRange.collapse(true);
26486      
26487         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26488         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26489          
26490         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26491         
26492         var nodeIsBefore   =  ss == 1;
26493         var nodeIsAfter    = ee == -1;
26494         
26495         if (nodeIsBefore && nodeIsAfter) {
26496             return 0; // outer
26497         }
26498         if (!nodeIsBefore && nodeIsAfter) {
26499             return 1; //right trailed.
26500         }
26501         
26502         if (nodeIsBefore && !nodeIsAfter) {
26503             return 2;  // left trailed.
26504         }
26505         // fully contined.
26506         return 3;
26507     },
26508
26509     // private? - in a new class?
26510     cleanUpPaste :  function()
26511     {
26512         // cleans up the whole document..
26513         Roo.log('cleanuppaste');
26514         
26515         this.cleanUpChildren(this.doc.body);
26516         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26517         if (clean != this.doc.body.innerHTML) {
26518             this.doc.body.innerHTML = clean;
26519         }
26520         
26521     },
26522     
26523     cleanWordChars : function(input) {// change the chars to hex code
26524         var he = Roo.HtmlEditorCore;
26525         
26526         var output = input;
26527         Roo.each(he.swapCodes, function(sw) { 
26528             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26529             
26530             output = output.replace(swapper, sw[1]);
26531         });
26532         
26533         return output;
26534     },
26535     
26536     
26537     cleanUpChildren : function (n)
26538     {
26539         if (!n.childNodes.length) {
26540             return;
26541         }
26542         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26543            this.cleanUpChild(n.childNodes[i]);
26544         }
26545     },
26546     
26547     
26548         
26549     
26550     cleanUpChild : function (node)
26551     {
26552         var ed = this;
26553         //console.log(node);
26554         if (node.nodeName == "#text") {
26555             // clean up silly Windows -- stuff?
26556             return; 
26557         }
26558         if (node.nodeName == "#comment") {
26559             if (!this.allowComments) {
26560                 node.parentNode.removeChild(node);
26561             }
26562             // clean up silly Windows -- stuff?
26563             return; 
26564         }
26565         var lcname = node.tagName.toLowerCase();
26566         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26567         // whitelist of tags..
26568         
26569         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26570             // remove node.
26571             node.parentNode.removeChild(node);
26572             return;
26573             
26574         }
26575         
26576         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26577         
26578         // spans with no attributes - just remove them..
26579         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26580             remove_keep_children = true;
26581         }
26582         
26583         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26584         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26585         
26586         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26587         //    remove_keep_children = true;
26588         //}
26589         
26590         if (remove_keep_children) {
26591             this.cleanUpChildren(node);
26592             // inserts everything just before this node...
26593             while (node.childNodes.length) {
26594                 var cn = node.childNodes[0];
26595                 node.removeChild(cn);
26596                 node.parentNode.insertBefore(cn, node);
26597             }
26598             node.parentNode.removeChild(node);
26599             return;
26600         }
26601         
26602         if (!node.attributes || !node.attributes.length) {
26603             
26604           
26605             
26606             
26607             this.cleanUpChildren(node);
26608             return;
26609         }
26610         
26611         function cleanAttr(n,v)
26612         {
26613             
26614             if (v.match(/^\./) || v.match(/^\//)) {
26615                 return;
26616             }
26617             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26618                 return;
26619             }
26620             if (v.match(/^#/)) {
26621                 return;
26622             }
26623             if (v.match(/^\{/)) { // allow template editing.
26624                 return;
26625             }
26626 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26627             node.removeAttribute(n);
26628             
26629         }
26630         
26631         var cwhite = this.cwhite;
26632         var cblack = this.cblack;
26633             
26634         function cleanStyle(n,v)
26635         {
26636             if (v.match(/expression/)) { //XSS?? should we even bother..
26637                 node.removeAttribute(n);
26638                 return;
26639             }
26640             
26641             var parts = v.split(/;/);
26642             var clean = [];
26643             
26644             Roo.each(parts, function(p) {
26645                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26646                 if (!p.length) {
26647                     return true;
26648                 }
26649                 var l = p.split(':').shift().replace(/\s+/g,'');
26650                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26651                 
26652                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26653 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26654                     //node.removeAttribute(n);
26655                     return true;
26656                 }
26657                 //Roo.log()
26658                 // only allow 'c whitelisted system attributes'
26659                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26660 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26661                     //node.removeAttribute(n);
26662                     return true;
26663                 }
26664                 
26665                 
26666                  
26667                 
26668                 clean.push(p);
26669                 return true;
26670             });
26671             if (clean.length) { 
26672                 node.setAttribute(n, clean.join(';'));
26673             } else {
26674                 node.removeAttribute(n);
26675             }
26676             
26677         }
26678         
26679         
26680         for (var i = node.attributes.length-1; i > -1 ; i--) {
26681             var a = node.attributes[i];
26682             //console.log(a);
26683             
26684             if (a.name.toLowerCase().substr(0,2)=='on')  {
26685                 node.removeAttribute(a.name);
26686                 continue;
26687             }
26688             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26689                 node.removeAttribute(a.name);
26690                 continue;
26691             }
26692             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26693                 cleanAttr(a.name,a.value); // fixme..
26694                 continue;
26695             }
26696             if (a.name == 'style') {
26697                 cleanStyle(a.name,a.value);
26698                 continue;
26699             }
26700             /// clean up MS crap..
26701             // tecnically this should be a list of valid class'es..
26702             
26703             
26704             if (a.name == 'class') {
26705                 if (a.value.match(/^Mso/)) {
26706                     node.removeAttribute('class');
26707                 }
26708                 
26709                 if (a.value.match(/^body$/)) {
26710                     node.removeAttribute('class');
26711                 }
26712                 continue;
26713             }
26714             
26715             // style cleanup!?
26716             // class cleanup?
26717             
26718         }
26719         
26720         
26721         this.cleanUpChildren(node);
26722         
26723         
26724     },
26725     
26726     /**
26727      * Clean up MS wordisms...
26728      */
26729     cleanWord : function(node)
26730     {
26731         if (!node) {
26732             this.cleanWord(this.doc.body);
26733             return;
26734         }
26735         
26736         if(
26737                 node.nodeName == 'SPAN' &&
26738                 !node.hasAttributes() &&
26739                 node.childNodes.length == 1 &&
26740                 node.firstChild.nodeName == "#text"  
26741         ) {
26742             var textNode = node.firstChild;
26743             node.removeChild(textNode);
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.insertBefore(textNode, node);
26748             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26749                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26750             }
26751             node.parentNode.removeChild(node);
26752         }
26753         
26754         if (node.nodeName == "#text") {
26755             // clean up silly Windows -- stuff?
26756             return; 
26757         }
26758         if (node.nodeName == "#comment") {
26759             node.parentNode.removeChild(node);
26760             // clean up silly Windows -- stuff?
26761             return; 
26762         }
26763         
26764         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26765             node.parentNode.removeChild(node);
26766             return;
26767         }
26768         //Roo.log(node.tagName);
26769         // remove - but keep children..
26770         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26771             //Roo.log('-- removed');
26772             while (node.childNodes.length) {
26773                 var cn = node.childNodes[0];
26774                 node.removeChild(cn);
26775                 node.parentNode.insertBefore(cn, node);
26776                 // move node to parent - and clean it..
26777                 this.cleanWord(cn);
26778             }
26779             node.parentNode.removeChild(node);
26780             /// no need to iterate chidlren = it's got none..
26781             //this.iterateChildren(node, this.cleanWord);
26782             return;
26783         }
26784         // clean styles
26785         if (node.className.length) {
26786             
26787             var cn = node.className.split(/\W+/);
26788             var cna = [];
26789             Roo.each(cn, function(cls) {
26790                 if (cls.match(/Mso[a-zA-Z]+/)) {
26791                     return;
26792                 }
26793                 cna.push(cls);
26794             });
26795             node.className = cna.length ? cna.join(' ') : '';
26796             if (!cna.length) {
26797                 node.removeAttribute("class");
26798             }
26799         }
26800         
26801         if (node.hasAttribute("lang")) {
26802             node.removeAttribute("lang");
26803         }
26804         
26805         if (node.hasAttribute("style")) {
26806             
26807             var styles = node.getAttribute("style").split(";");
26808             var nstyle = [];
26809             Roo.each(styles, function(s) {
26810                 if (!s.match(/:/)) {
26811                     return;
26812                 }
26813                 var kv = s.split(":");
26814                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26815                     return;
26816                 }
26817                 // what ever is left... we allow.
26818                 nstyle.push(s);
26819             });
26820             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26821             if (!nstyle.length) {
26822                 node.removeAttribute('style');
26823             }
26824         }
26825         this.iterateChildren(node, this.cleanWord);
26826         
26827         
26828         
26829     },
26830     /**
26831      * iterateChildren of a Node, calling fn each time, using this as the scole..
26832      * @param {DomNode} node node to iterate children of.
26833      * @param {Function} fn method of this class to call on each item.
26834      */
26835     iterateChildren : function(node, fn)
26836     {
26837         if (!node.childNodes.length) {
26838                 return;
26839         }
26840         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26841            fn.call(this, node.childNodes[i])
26842         }
26843     },
26844     
26845     
26846     /**
26847      * cleanTableWidths.
26848      *
26849      * Quite often pasting from word etc.. results in tables with column and widths.
26850      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26851      *
26852      */
26853     cleanTableWidths : function(node)
26854     {
26855          
26856          
26857         if (!node) {
26858             this.cleanTableWidths(this.doc.body);
26859             return;
26860         }
26861         
26862         // ignore list...
26863         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26864             return; 
26865         }
26866         Roo.log(node.tagName);
26867         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26868             this.iterateChildren(node, this.cleanTableWidths);
26869             return;
26870         }
26871         if (node.hasAttribute('width')) {
26872             node.removeAttribute('width');
26873         }
26874         
26875          
26876         if (node.hasAttribute("style")) {
26877             // pretty basic...
26878             
26879             var styles = node.getAttribute("style").split(";");
26880             var nstyle = [];
26881             Roo.each(styles, function(s) {
26882                 if (!s.match(/:/)) {
26883                     return;
26884                 }
26885                 var kv = s.split(":");
26886                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26887                     return;
26888                 }
26889                 // what ever is left... we allow.
26890                 nstyle.push(s);
26891             });
26892             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26893             if (!nstyle.length) {
26894                 node.removeAttribute('style');
26895             }
26896         }
26897         
26898         this.iterateChildren(node, this.cleanTableWidths);
26899         
26900         
26901     },
26902     
26903     
26904     
26905     
26906     domToHTML : function(currentElement, depth, nopadtext) {
26907         
26908         depth = depth || 0;
26909         nopadtext = nopadtext || false;
26910     
26911         if (!currentElement) {
26912             return this.domToHTML(this.doc.body);
26913         }
26914         
26915         //Roo.log(currentElement);
26916         var j;
26917         var allText = false;
26918         var nodeName = currentElement.nodeName;
26919         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26920         
26921         if  (nodeName == '#text') {
26922             
26923             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26924         }
26925         
26926         
26927         var ret = '';
26928         if (nodeName != 'BODY') {
26929              
26930             var i = 0;
26931             // Prints the node tagName, such as <A>, <IMG>, etc
26932             if (tagName) {
26933                 var attr = [];
26934                 for(i = 0; i < currentElement.attributes.length;i++) {
26935                     // quoting?
26936                     var aname = currentElement.attributes.item(i).name;
26937                     if (!currentElement.attributes.item(i).value.length) {
26938                         continue;
26939                     }
26940                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26941                 }
26942                 
26943                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26944             } 
26945             else {
26946                 
26947                 // eack
26948             }
26949         } else {
26950             tagName = false;
26951         }
26952         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26953             return ret;
26954         }
26955         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26956             nopadtext = true;
26957         }
26958         
26959         
26960         // Traverse the tree
26961         i = 0;
26962         var currentElementChild = currentElement.childNodes.item(i);
26963         var allText = true;
26964         var innerHTML  = '';
26965         lastnode = '';
26966         while (currentElementChild) {
26967             // Formatting code (indent the tree so it looks nice on the screen)
26968             var nopad = nopadtext;
26969             if (lastnode == 'SPAN') {
26970                 nopad  = true;
26971             }
26972             // text
26973             if  (currentElementChild.nodeName == '#text') {
26974                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26975                 toadd = nopadtext ? toadd : toadd.trim();
26976                 if (!nopad && toadd.length > 80) {
26977                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26978                 }
26979                 innerHTML  += toadd;
26980                 
26981                 i++;
26982                 currentElementChild = currentElement.childNodes.item(i);
26983                 lastNode = '';
26984                 continue;
26985             }
26986             allText = false;
26987             
26988             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26989                 
26990             // Recursively traverse the tree structure of the child node
26991             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26992             lastnode = currentElementChild.nodeName;
26993             i++;
26994             currentElementChild=currentElement.childNodes.item(i);
26995         }
26996         
26997         ret += innerHTML;
26998         
26999         if (!allText) {
27000                 // The remaining code is mostly for formatting the tree
27001             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27002         }
27003         
27004         
27005         if (tagName) {
27006             ret+= "</"+tagName+">";
27007         }
27008         return ret;
27009         
27010     },
27011         
27012     applyBlacklists : function()
27013     {
27014         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27015         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27016         
27017         this.white = [];
27018         this.black = [];
27019         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27020             if (b.indexOf(tag) > -1) {
27021                 return;
27022             }
27023             this.white.push(tag);
27024             
27025         }, this);
27026         
27027         Roo.each(w, function(tag) {
27028             if (b.indexOf(tag) > -1) {
27029                 return;
27030             }
27031             if (this.white.indexOf(tag) > -1) {
27032                 return;
27033             }
27034             this.white.push(tag);
27035             
27036         }, this);
27037         
27038         
27039         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27040             if (w.indexOf(tag) > -1) {
27041                 return;
27042             }
27043             this.black.push(tag);
27044             
27045         }, this);
27046         
27047         Roo.each(b, function(tag) {
27048             if (w.indexOf(tag) > -1) {
27049                 return;
27050             }
27051             if (this.black.indexOf(tag) > -1) {
27052                 return;
27053             }
27054             this.black.push(tag);
27055             
27056         }, this);
27057         
27058         
27059         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27060         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27061         
27062         this.cwhite = [];
27063         this.cblack = [];
27064         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27065             if (b.indexOf(tag) > -1) {
27066                 return;
27067             }
27068             this.cwhite.push(tag);
27069             
27070         }, this);
27071         
27072         Roo.each(w, function(tag) {
27073             if (b.indexOf(tag) > -1) {
27074                 return;
27075             }
27076             if (this.cwhite.indexOf(tag) > -1) {
27077                 return;
27078             }
27079             this.cwhite.push(tag);
27080             
27081         }, this);
27082         
27083         
27084         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27085             if (w.indexOf(tag) > -1) {
27086                 return;
27087             }
27088             this.cblack.push(tag);
27089             
27090         }, this);
27091         
27092         Roo.each(b, function(tag) {
27093             if (w.indexOf(tag) > -1) {
27094                 return;
27095             }
27096             if (this.cblack.indexOf(tag) > -1) {
27097                 return;
27098             }
27099             this.cblack.push(tag);
27100             
27101         }, this);
27102     },
27103     
27104     setStylesheets : function(stylesheets)
27105     {
27106         if(typeof(stylesheets) == 'string'){
27107             Roo.get(this.iframe.contentDocument.head).createChild({
27108                 tag : 'link',
27109                 rel : 'stylesheet',
27110                 type : 'text/css',
27111                 href : stylesheets
27112             });
27113             
27114             return;
27115         }
27116         var _this = this;
27117      
27118         Roo.each(stylesheets, function(s) {
27119             if(!s.length){
27120                 return;
27121             }
27122             
27123             Roo.get(_this.iframe.contentDocument.head).createChild({
27124                 tag : 'link',
27125                 rel : 'stylesheet',
27126                 type : 'text/css',
27127                 href : s
27128             });
27129         });
27130
27131         
27132     },
27133     
27134     removeStylesheets : function()
27135     {
27136         var _this = this;
27137         
27138         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27139             s.remove();
27140         });
27141     },
27142     
27143     setStyle : function(style)
27144     {
27145         Roo.get(this.iframe.contentDocument.head).createChild({
27146             tag : 'style',
27147             type : 'text/css',
27148             html : style
27149         });
27150
27151         return;
27152     }
27153     
27154     // hide stuff that is not compatible
27155     /**
27156      * @event blur
27157      * @hide
27158      */
27159     /**
27160      * @event change
27161      * @hide
27162      */
27163     /**
27164      * @event focus
27165      * @hide
27166      */
27167     /**
27168      * @event specialkey
27169      * @hide
27170      */
27171     /**
27172      * @cfg {String} fieldClass @hide
27173      */
27174     /**
27175      * @cfg {String} focusClass @hide
27176      */
27177     /**
27178      * @cfg {String} autoCreate @hide
27179      */
27180     /**
27181      * @cfg {String} inputType @hide
27182      */
27183     /**
27184      * @cfg {String} invalidClass @hide
27185      */
27186     /**
27187      * @cfg {String} invalidText @hide
27188      */
27189     /**
27190      * @cfg {String} msgFx @hide
27191      */
27192     /**
27193      * @cfg {String} validateOnBlur @hide
27194      */
27195 });
27196
27197 Roo.HtmlEditorCore.white = [
27198         'area', 'br', 'img', 'input', 'hr', 'wbr',
27199         
27200        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27201        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27202        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27203        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27204        'table',   'ul',         'xmp', 
27205        
27206        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27207       'thead',   'tr', 
27208      
27209       'dir', 'menu', 'ol', 'ul', 'dl',
27210        
27211       'embed',  'object'
27212 ];
27213
27214
27215 Roo.HtmlEditorCore.black = [
27216     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27217         'applet', // 
27218         'base',   'basefont', 'bgsound', 'blink',  'body', 
27219         'frame',  'frameset', 'head',    'html',   'ilayer', 
27220         'iframe', 'layer',  'link',     'meta',    'object',   
27221         'script', 'style' ,'title',  'xml' // clean later..
27222 ];
27223 Roo.HtmlEditorCore.clean = [
27224     'script', 'style', 'title', 'xml'
27225 ];
27226 Roo.HtmlEditorCore.remove = [
27227     'font'
27228 ];
27229 // attributes..
27230
27231 Roo.HtmlEditorCore.ablack = [
27232     'on'
27233 ];
27234     
27235 Roo.HtmlEditorCore.aclean = [ 
27236     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27237 ];
27238
27239 // protocols..
27240 Roo.HtmlEditorCore.pwhite= [
27241         'http',  'https',  'mailto'
27242 ];
27243
27244 // white listed style attributes.
27245 Roo.HtmlEditorCore.cwhite= [
27246       //  'text-align', /// default is to allow most things..
27247       
27248          
27249 //        'font-size'//??
27250 ];
27251
27252 // black listed style attributes.
27253 Roo.HtmlEditorCore.cblack= [
27254       //  'font-size' -- this can be set by the project 
27255 ];
27256
27257
27258 Roo.HtmlEditorCore.swapCodes   =[ 
27259     [    8211, "&#8211;" ], 
27260     [    8212, "&#8212;" ], 
27261     [    8216,  "'" ],  
27262     [    8217, "'" ],  
27263     [    8220, '"' ],  
27264     [    8221, '"' ],  
27265     [    8226, "*" ],  
27266     [    8230, "..." ]
27267 ]; 
27268
27269     /*
27270  * - LGPL
27271  *
27272  * HtmlEditor
27273  * 
27274  */
27275
27276 /**
27277  * @class Roo.bootstrap.HtmlEditor
27278  * @extends Roo.bootstrap.TextArea
27279  * Bootstrap HtmlEditor class
27280
27281  * @constructor
27282  * Create a new HtmlEditor
27283  * @param {Object} config The config object
27284  */
27285
27286 Roo.bootstrap.HtmlEditor = function(config){
27287     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27288     if (!this.toolbars) {
27289         this.toolbars = [];
27290     }
27291     
27292     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27293     this.addEvents({
27294             /**
27295              * @event initialize
27296              * Fires when the editor is fully initialized (including the iframe)
27297              * @param {HtmlEditor} this
27298              */
27299             initialize: true,
27300             /**
27301              * @event activate
27302              * Fires when the editor is first receives the focus. Any insertion must wait
27303              * until after this event.
27304              * @param {HtmlEditor} this
27305              */
27306             activate: true,
27307              /**
27308              * @event beforesync
27309              * Fires before the textarea is updated with content from the editor iframe. Return false
27310              * to cancel the sync.
27311              * @param {HtmlEditor} this
27312              * @param {String} html
27313              */
27314             beforesync: true,
27315              /**
27316              * @event beforepush
27317              * Fires before the iframe editor is updated with content from the textarea. Return false
27318              * to cancel the push.
27319              * @param {HtmlEditor} this
27320              * @param {String} html
27321              */
27322             beforepush: true,
27323              /**
27324              * @event sync
27325              * Fires when the textarea is updated with content from the editor iframe.
27326              * @param {HtmlEditor} this
27327              * @param {String} html
27328              */
27329             sync: true,
27330              /**
27331              * @event push
27332              * Fires when the iframe editor is updated with content from the textarea.
27333              * @param {HtmlEditor} this
27334              * @param {String} html
27335              */
27336             push: true,
27337              /**
27338              * @event editmodechange
27339              * Fires when the editor switches edit modes
27340              * @param {HtmlEditor} this
27341              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27342              */
27343             editmodechange: true,
27344             /**
27345              * @event editorevent
27346              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27347              * @param {HtmlEditor} this
27348              */
27349             editorevent: true,
27350             /**
27351              * @event firstfocus
27352              * Fires when on first focus - needed by toolbars..
27353              * @param {HtmlEditor} this
27354              */
27355             firstfocus: true,
27356             /**
27357              * @event autosave
27358              * Auto save the htmlEditor value as a file into Events
27359              * @param {HtmlEditor} this
27360              */
27361             autosave: true,
27362             /**
27363              * @event savedpreview
27364              * preview the saved version of htmlEditor
27365              * @param {HtmlEditor} this
27366              */
27367             savedpreview: true
27368         });
27369 };
27370
27371
27372 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27373     
27374     
27375       /**
27376      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27377      */
27378     toolbars : false,
27379     
27380      /**
27381     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27382     */
27383     btns : [],
27384    
27385      /**
27386      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27387      *                        Roo.resizable.
27388      */
27389     resizable : false,
27390      /**
27391      * @cfg {Number} height (in pixels)
27392      */   
27393     height: 300,
27394    /**
27395      * @cfg {Number} width (in pixels)
27396      */   
27397     width: false,
27398     
27399     /**
27400      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27401      * 
27402      */
27403     stylesheets: false,
27404     
27405     // id of frame..
27406     frameId: false,
27407     
27408     // private properties
27409     validationEvent : false,
27410     deferHeight: true,
27411     initialized : false,
27412     activated : false,
27413     
27414     onFocus : Roo.emptyFn,
27415     iframePad:3,
27416     hideMode:'offsets',
27417     
27418     tbContainer : false,
27419     
27420     bodyCls : '',
27421     
27422     toolbarContainer :function() {
27423         return this.wrap.select('.x-html-editor-tb',true).first();
27424     },
27425
27426     /**
27427      * Protected method that will not generally be called directly. It
27428      * is called when the editor creates its toolbar. Override this method if you need to
27429      * add custom toolbar buttons.
27430      * @param {HtmlEditor} editor
27431      */
27432     createToolbar : function(){
27433         Roo.log('renewing');
27434         Roo.log("create toolbars");
27435         
27436         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27437         this.toolbars[0].render(this.toolbarContainer());
27438         
27439         return;
27440         
27441 //        if (!editor.toolbars || !editor.toolbars.length) {
27442 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27443 //        }
27444 //        
27445 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27446 //            editor.toolbars[i] = Roo.factory(
27447 //                    typeof(editor.toolbars[i]) == 'string' ?
27448 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27449 //                Roo.bootstrap.HtmlEditor);
27450 //            editor.toolbars[i].init(editor);
27451 //        }
27452     },
27453
27454      
27455     // private
27456     onRender : function(ct, position)
27457     {
27458        // Roo.log("Call onRender: " + this.xtype);
27459         var _t = this;
27460         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27461       
27462         this.wrap = this.inputEl().wrap({
27463             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27464         });
27465         
27466         this.editorcore.onRender(ct, position);
27467          
27468         if (this.resizable) {
27469             this.resizeEl = new Roo.Resizable(this.wrap, {
27470                 pinned : true,
27471                 wrap: true,
27472                 dynamic : true,
27473                 minHeight : this.height,
27474                 height: this.height,
27475                 handles : this.resizable,
27476                 width: this.width,
27477                 listeners : {
27478                     resize : function(r, w, h) {
27479                         _t.onResize(w,h); // -something
27480                     }
27481                 }
27482             });
27483             
27484         }
27485         this.createToolbar(this);
27486        
27487         
27488         if(!this.width && this.resizable){
27489             this.setSize(this.wrap.getSize());
27490         }
27491         if (this.resizeEl) {
27492             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27493             // should trigger onReize..
27494         }
27495         
27496     },
27497
27498     // private
27499     onResize : function(w, h)
27500     {
27501         Roo.log('resize: ' +w + ',' + h );
27502         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27503         var ew = false;
27504         var eh = false;
27505         
27506         if(this.inputEl() ){
27507             if(typeof w == 'number'){
27508                 var aw = w - this.wrap.getFrameWidth('lr');
27509                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27510                 ew = aw;
27511             }
27512             if(typeof h == 'number'){
27513                  var tbh = -11;  // fixme it needs to tool bar size!
27514                 for (var i =0; i < this.toolbars.length;i++) {
27515                     // fixme - ask toolbars for heights?
27516                     tbh += this.toolbars[i].el.getHeight();
27517                     //if (this.toolbars[i].footer) {
27518                     //    tbh += this.toolbars[i].footer.el.getHeight();
27519                     //}
27520                 }
27521               
27522                 
27523                 
27524                 
27525                 
27526                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27527                 ah -= 5; // knock a few pixes off for look..
27528                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27529                 var eh = ah;
27530             }
27531         }
27532         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27533         this.editorcore.onResize(ew,eh);
27534         
27535     },
27536
27537     /**
27538      * Toggles the editor between standard and source edit mode.
27539      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27540      */
27541     toggleSourceEdit : function(sourceEditMode)
27542     {
27543         this.editorcore.toggleSourceEdit(sourceEditMode);
27544         
27545         if(this.editorcore.sourceEditMode){
27546             Roo.log('editor - showing textarea');
27547             
27548 //            Roo.log('in');
27549 //            Roo.log(this.syncValue());
27550             this.syncValue();
27551             this.inputEl().removeClass(['hide', 'x-hidden']);
27552             this.inputEl().dom.removeAttribute('tabIndex');
27553             this.inputEl().focus();
27554         }else{
27555             Roo.log('editor - hiding textarea');
27556 //            Roo.log('out')
27557 //            Roo.log(this.pushValue()); 
27558             this.pushValue();
27559             
27560             this.inputEl().addClass(['hide', 'x-hidden']);
27561             this.inputEl().dom.setAttribute('tabIndex', -1);
27562             //this.deferFocus();
27563         }
27564          
27565         if(this.resizable){
27566             this.setSize(this.wrap.getSize());
27567         }
27568         
27569         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27570     },
27571  
27572     // private (for BoxComponent)
27573     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27574
27575     // private (for BoxComponent)
27576     getResizeEl : function(){
27577         return this.wrap;
27578     },
27579
27580     // private (for BoxComponent)
27581     getPositionEl : function(){
27582         return this.wrap;
27583     },
27584
27585     // private
27586     initEvents : function(){
27587         this.originalValue = this.getValue();
27588     },
27589
27590 //    /**
27591 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27592 //     * @method
27593 //     */
27594 //    markInvalid : Roo.emptyFn,
27595 //    /**
27596 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27597 //     * @method
27598 //     */
27599 //    clearInvalid : Roo.emptyFn,
27600
27601     setValue : function(v){
27602         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27603         this.editorcore.pushValue();
27604     },
27605
27606      
27607     // private
27608     deferFocus : function(){
27609         this.focus.defer(10, this);
27610     },
27611
27612     // doc'ed in Field
27613     focus : function(){
27614         this.editorcore.focus();
27615         
27616     },
27617       
27618
27619     // private
27620     onDestroy : function(){
27621         
27622         
27623         
27624         if(this.rendered){
27625             
27626             for (var i =0; i < this.toolbars.length;i++) {
27627                 // fixme - ask toolbars for heights?
27628                 this.toolbars[i].onDestroy();
27629             }
27630             
27631             this.wrap.dom.innerHTML = '';
27632             this.wrap.remove();
27633         }
27634     },
27635
27636     // private
27637     onFirstFocus : function(){
27638         //Roo.log("onFirstFocus");
27639         this.editorcore.onFirstFocus();
27640          for (var i =0; i < this.toolbars.length;i++) {
27641             this.toolbars[i].onFirstFocus();
27642         }
27643         
27644     },
27645     
27646     // private
27647     syncValue : function()
27648     {   
27649         this.editorcore.syncValue();
27650     },
27651     
27652     pushValue : function()
27653     {   
27654         this.editorcore.pushValue();
27655     }
27656      
27657     
27658     // hide stuff that is not compatible
27659     /**
27660      * @event blur
27661      * @hide
27662      */
27663     /**
27664      * @event change
27665      * @hide
27666      */
27667     /**
27668      * @event focus
27669      * @hide
27670      */
27671     /**
27672      * @event specialkey
27673      * @hide
27674      */
27675     /**
27676      * @cfg {String} fieldClass @hide
27677      */
27678     /**
27679      * @cfg {String} focusClass @hide
27680      */
27681     /**
27682      * @cfg {String} autoCreate @hide
27683      */
27684     /**
27685      * @cfg {String} inputType @hide
27686      */
27687      
27688     /**
27689      * @cfg {String} invalidText @hide
27690      */
27691     /**
27692      * @cfg {String} msgFx @hide
27693      */
27694     /**
27695      * @cfg {String} validateOnBlur @hide
27696      */
27697 });
27698  
27699     
27700    
27701    
27702    
27703       
27704 Roo.namespace('Roo.bootstrap.htmleditor');
27705 /**
27706  * @class Roo.bootstrap.HtmlEditorToolbar1
27707  * Basic Toolbar
27708  * 
27709  * @example
27710  * Usage:
27711  *
27712  new Roo.bootstrap.HtmlEditor({
27713     ....
27714     toolbars : [
27715         new Roo.bootstrap.HtmlEditorToolbar1({
27716             disable : { fonts: 1 , format: 1, ..., ... , ...],
27717             btns : [ .... ]
27718         })
27719     }
27720      
27721  * 
27722  * @cfg {Object} disable List of elements to disable..
27723  * @cfg {Array} btns List of additional buttons.
27724  * 
27725  * 
27726  * NEEDS Extra CSS? 
27727  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27728  */
27729  
27730 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27731 {
27732     
27733     Roo.apply(this, config);
27734     
27735     // default disabled, based on 'good practice'..
27736     this.disable = this.disable || {};
27737     Roo.applyIf(this.disable, {
27738         fontSize : true,
27739         colors : true,
27740         specialElements : true
27741     });
27742     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27743     
27744     this.editor = config.editor;
27745     this.editorcore = config.editor.editorcore;
27746     
27747     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27748     
27749     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27750     // dont call parent... till later.
27751 }
27752 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27753      
27754     bar : true,
27755     
27756     editor : false,
27757     editorcore : false,
27758     
27759     
27760     formats : [
27761         "p" ,  
27762         "h1","h2","h3","h4","h5","h6", 
27763         "pre", "code", 
27764         "abbr", "acronym", "address", "cite", "samp", "var",
27765         'div','span'
27766     ],
27767     
27768     onRender : function(ct, position)
27769     {
27770        // Roo.log("Call onRender: " + this.xtype);
27771         
27772        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27773        Roo.log(this.el);
27774        this.el.dom.style.marginBottom = '0';
27775        var _this = this;
27776        var editorcore = this.editorcore;
27777        var editor= this.editor;
27778        
27779        var children = [];
27780        var btn = function(id,cmd , toggle, handler, html){
27781        
27782             var  event = toggle ? 'toggle' : 'click';
27783        
27784             var a = {
27785                 size : 'sm',
27786                 xtype: 'Button',
27787                 xns: Roo.bootstrap,
27788                 //glyphicon : id,
27789                 fa: id,
27790                 cmd : id || cmd,
27791                 enableToggle:toggle !== false,
27792                 html : html || '',
27793                 pressed : toggle ? false : null,
27794                 listeners : {}
27795             };
27796             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27797                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27798             };
27799             children.push(a);
27800             return a;
27801        }
27802        
27803     //    var cb_box = function...
27804         
27805         var style = {
27806                 xtype: 'Button',
27807                 size : 'sm',
27808                 xns: Roo.bootstrap,
27809                 fa : 'font',
27810                 //html : 'submit'
27811                 menu : {
27812                     xtype: 'Menu',
27813                     xns: Roo.bootstrap,
27814                     items:  []
27815                 }
27816         };
27817         Roo.each(this.formats, function(f) {
27818             style.menu.items.push({
27819                 xtype :'MenuItem',
27820                 xns: Roo.bootstrap,
27821                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27822                 tagname : f,
27823                 listeners : {
27824                     click : function()
27825                     {
27826                         editorcore.insertTag(this.tagname);
27827                         editor.focus();
27828                     }
27829                 }
27830                 
27831             });
27832         });
27833         children.push(style);   
27834         
27835         btn('bold',false,true);
27836         btn('italic',false,true);
27837         btn('align-left', 'justifyleft',true);
27838         btn('align-center', 'justifycenter',true);
27839         btn('align-right' , 'justifyright',true);
27840         btn('link', false, false, function(btn) {
27841             //Roo.log("create link?");
27842             var url = prompt(this.createLinkText, this.defaultLinkValue);
27843             if(url && url != 'http:/'+'/'){
27844                 this.editorcore.relayCmd('createlink', url);
27845             }
27846         }),
27847         btn('list','insertunorderedlist',true);
27848         btn('pencil', false,true, function(btn){
27849                 Roo.log(this);
27850                 this.toggleSourceEdit(btn.pressed);
27851         });
27852         
27853         if (this.editor.btns.length > 0) {
27854             for (var i = 0; i<this.editor.btns.length; i++) {
27855                 children.push(this.editor.btns[i]);
27856             }
27857         }
27858         
27859         /*
27860         var cog = {
27861                 xtype: 'Button',
27862                 size : 'sm',
27863                 xns: Roo.bootstrap,
27864                 glyphicon : 'cog',
27865                 //html : 'submit'
27866                 menu : {
27867                     xtype: 'Menu',
27868                     xns: Roo.bootstrap,
27869                     items:  []
27870                 }
27871         };
27872         
27873         cog.menu.items.push({
27874             xtype :'MenuItem',
27875             xns: Roo.bootstrap,
27876             html : Clean styles,
27877             tagname : f,
27878             listeners : {
27879                 click : function()
27880                 {
27881                     editorcore.insertTag(this.tagname);
27882                     editor.focus();
27883                 }
27884             }
27885             
27886         });
27887        */
27888         
27889          
27890        this.xtype = 'NavSimplebar';
27891         
27892         for(var i=0;i< children.length;i++) {
27893             
27894             this.buttons.add(this.addxtypeChild(children[i]));
27895             
27896         }
27897         
27898         editor.on('editorevent', this.updateToolbar, this);
27899     },
27900     onBtnClick : function(id)
27901     {
27902        this.editorcore.relayCmd(id);
27903        this.editorcore.focus();
27904     },
27905     
27906     /**
27907      * Protected method that will not generally be called directly. It triggers
27908      * a toolbar update by reading the markup state of the current selection in the editor.
27909      */
27910     updateToolbar: function(){
27911
27912         if(!this.editorcore.activated){
27913             this.editor.onFirstFocus(); // is this neeed?
27914             return;
27915         }
27916
27917         var btns = this.buttons; 
27918         var doc = this.editorcore.doc;
27919         btns.get('bold').setActive(doc.queryCommandState('bold'));
27920         btns.get('italic').setActive(doc.queryCommandState('italic'));
27921         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27922         
27923         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27924         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27925         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27926         
27927         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27928         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27929          /*
27930         
27931         var ans = this.editorcore.getAllAncestors();
27932         if (this.formatCombo) {
27933             
27934             
27935             var store = this.formatCombo.store;
27936             this.formatCombo.setValue("");
27937             for (var i =0; i < ans.length;i++) {
27938                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27939                     // select it..
27940                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27941                     break;
27942                 }
27943             }
27944         }
27945         
27946         
27947         
27948         // hides menus... - so this cant be on a menu...
27949         Roo.bootstrap.MenuMgr.hideAll();
27950         */
27951         Roo.bootstrap.MenuMgr.hideAll();
27952         //this.editorsyncValue();
27953     },
27954     onFirstFocus: function() {
27955         this.buttons.each(function(item){
27956            item.enable();
27957         });
27958     },
27959     toggleSourceEdit : function(sourceEditMode){
27960         
27961           
27962         if(sourceEditMode){
27963             Roo.log("disabling buttons");
27964            this.buttons.each( function(item){
27965                 if(item.cmd != 'pencil'){
27966                     item.disable();
27967                 }
27968             });
27969           
27970         }else{
27971             Roo.log("enabling buttons");
27972             if(this.editorcore.initialized){
27973                 this.buttons.each( function(item){
27974                     item.enable();
27975                 });
27976             }
27977             
27978         }
27979         Roo.log("calling toggole on editor");
27980         // tell the editor that it's been pressed..
27981         this.editor.toggleSourceEdit(sourceEditMode);
27982        
27983     }
27984 });
27985
27986
27987
27988
27989  
27990 /*
27991  * - LGPL
27992  */
27993
27994 /**
27995  * @class Roo.bootstrap.Markdown
27996  * @extends Roo.bootstrap.TextArea
27997  * Bootstrap Showdown editable area
27998  * @cfg {string} content
27999  * 
28000  * @constructor
28001  * Create a new Showdown
28002  */
28003
28004 Roo.bootstrap.Markdown = function(config){
28005     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28006    
28007 };
28008
28009 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28010     
28011     editing :false,
28012     
28013     initEvents : function()
28014     {
28015         
28016         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28017         this.markdownEl = this.el.createChild({
28018             cls : 'roo-markdown-area'
28019         });
28020         this.inputEl().addClass('d-none');
28021         if (this.getValue() == '') {
28022             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28023             
28024         } else {
28025             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28026         }
28027         this.markdownEl.on('click', this.toggleTextEdit, this);
28028         this.on('blur', this.toggleTextEdit, this);
28029         this.on('specialkey', this.resizeTextArea, this);
28030     },
28031     
28032     toggleTextEdit : function()
28033     {
28034         var sh = this.markdownEl.getHeight();
28035         this.inputEl().addClass('d-none');
28036         this.markdownEl.addClass('d-none');
28037         if (!this.editing) {
28038             // show editor?
28039             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28040             this.inputEl().removeClass('d-none');
28041             this.inputEl().focus();
28042             this.editing = true;
28043             return;
28044         }
28045         // show showdown...
28046         this.updateMarkdown();
28047         this.markdownEl.removeClass('d-none');
28048         this.editing = false;
28049         return;
28050     },
28051     updateMarkdown : function()
28052     {
28053         if (this.getValue() == '') {
28054             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28055             return;
28056         }
28057  
28058         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28059     },
28060     
28061     resizeTextArea: function () {
28062         
28063         var sh = 100;
28064         Roo.log([sh, this.getValue().split("\n").length * 30]);
28065         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28066     },
28067     setValue : function(val)
28068     {
28069         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28070         if (!this.editing) {
28071             this.updateMarkdown();
28072         }
28073         
28074     },
28075     focus : function()
28076     {
28077         if (!this.editing) {
28078             this.toggleTextEdit();
28079         }
28080         
28081     }
28082
28083
28084 });/*
28085  * Based on:
28086  * Ext JS Library 1.1.1
28087  * Copyright(c) 2006-2007, Ext JS, LLC.
28088  *
28089  * Originally Released Under LGPL - original licence link has changed is not relivant.
28090  *
28091  * Fork - LGPL
28092  * <script type="text/javascript">
28093  */
28094  
28095 /**
28096  * @class Roo.bootstrap.PagingToolbar
28097  * @extends Roo.bootstrap.NavSimplebar
28098  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28099  * @constructor
28100  * Create a new PagingToolbar
28101  * @param {Object} config The config object
28102  * @param {Roo.data.Store} store
28103  */
28104 Roo.bootstrap.PagingToolbar = function(config)
28105 {
28106     // old args format still supported... - xtype is prefered..
28107         // created from xtype...
28108     
28109     this.ds = config.dataSource;
28110     
28111     if (config.store && !this.ds) {
28112         this.store= Roo.factory(config.store, Roo.data);
28113         this.ds = this.store;
28114         this.ds.xmodule = this.xmodule || false;
28115     }
28116     
28117     this.toolbarItems = [];
28118     if (config.items) {
28119         this.toolbarItems = config.items;
28120     }
28121     
28122     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28123     
28124     this.cursor = 0;
28125     
28126     if (this.ds) { 
28127         this.bind(this.ds);
28128     }
28129     
28130     if (Roo.bootstrap.version == 4) {
28131         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28132     } else {
28133         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28134     }
28135     
28136 };
28137
28138 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28139     /**
28140      * @cfg {Roo.data.Store} dataSource
28141      * The underlying data store providing the paged data
28142      */
28143     /**
28144      * @cfg {String/HTMLElement/Element} container
28145      * container The id or element that will contain the toolbar
28146      */
28147     /**
28148      * @cfg {Boolean} displayInfo
28149      * True to display the displayMsg (defaults to false)
28150      */
28151     /**
28152      * @cfg {Number} pageSize
28153      * The number of records to display per page (defaults to 20)
28154      */
28155     pageSize: 20,
28156     /**
28157      * @cfg {String} displayMsg
28158      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28159      */
28160     displayMsg : 'Displaying {0} - {1} of {2}',
28161     /**
28162      * @cfg {String} emptyMsg
28163      * The message to display when no records are found (defaults to "No data to display")
28164      */
28165     emptyMsg : 'No data to display',
28166     /**
28167      * Customizable piece of the default paging text (defaults to "Page")
28168      * @type String
28169      */
28170     beforePageText : "Page",
28171     /**
28172      * Customizable piece of the default paging text (defaults to "of %0")
28173      * @type String
28174      */
28175     afterPageText : "of {0}",
28176     /**
28177      * Customizable piece of the default paging text (defaults to "First Page")
28178      * @type String
28179      */
28180     firstText : "First Page",
28181     /**
28182      * Customizable piece of the default paging text (defaults to "Previous Page")
28183      * @type String
28184      */
28185     prevText : "Previous Page",
28186     /**
28187      * Customizable piece of the default paging text (defaults to "Next Page")
28188      * @type String
28189      */
28190     nextText : "Next Page",
28191     /**
28192      * Customizable piece of the default paging text (defaults to "Last Page")
28193      * @type String
28194      */
28195     lastText : "Last Page",
28196     /**
28197      * Customizable piece of the default paging text (defaults to "Refresh")
28198      * @type String
28199      */
28200     refreshText : "Refresh",
28201
28202     buttons : false,
28203     // private
28204     onRender : function(ct, position) 
28205     {
28206         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28207         this.navgroup.parentId = this.id;
28208         this.navgroup.onRender(this.el, null);
28209         // add the buttons to the navgroup
28210         
28211         if(this.displayInfo){
28212             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28213             this.displayEl = this.el.select('.x-paging-info', true).first();
28214 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28215 //            this.displayEl = navel.el.select('span',true).first();
28216         }
28217         
28218         var _this = this;
28219         
28220         if(this.buttons){
28221             Roo.each(_this.buttons, function(e){ // this might need to use render????
28222                Roo.factory(e).render(_this.el);
28223             });
28224         }
28225             
28226         Roo.each(_this.toolbarItems, function(e) {
28227             _this.navgroup.addItem(e);
28228         });
28229         
28230         
28231         this.first = this.navgroup.addItem({
28232             tooltip: this.firstText,
28233             cls: "prev btn-outline-secondary",
28234             html : ' <i class="fa fa-step-backward"></i>',
28235             disabled: true,
28236             preventDefault: true,
28237             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28238         });
28239         
28240         this.prev =  this.navgroup.addItem({
28241             tooltip: this.prevText,
28242             cls: "prev btn-outline-secondary",
28243             html : ' <i class="fa fa-backward"></i>',
28244             disabled: true,
28245             preventDefault: true,
28246             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28247         });
28248     //this.addSeparator();
28249         
28250         
28251         var field = this.navgroup.addItem( {
28252             tagtype : 'span',
28253             cls : 'x-paging-position  btn-outline-secondary',
28254              disabled: true,
28255             html : this.beforePageText  +
28256                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28257                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28258          } ); //?? escaped?
28259         
28260         this.field = field.el.select('input', true).first();
28261         this.field.on("keydown", this.onPagingKeydown, this);
28262         this.field.on("focus", function(){this.dom.select();});
28263     
28264     
28265         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28266         //this.field.setHeight(18);
28267         //this.addSeparator();
28268         this.next = this.navgroup.addItem({
28269             tooltip: this.nextText,
28270             cls: "next btn-outline-secondary",
28271             html : ' <i class="fa fa-forward"></i>',
28272             disabled: true,
28273             preventDefault: true,
28274             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28275         });
28276         this.last = this.navgroup.addItem({
28277             tooltip: this.lastText,
28278             html : ' <i class="fa fa-step-forward"></i>',
28279             cls: "next btn-outline-secondary",
28280             disabled: true,
28281             preventDefault: true,
28282             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28283         });
28284     //this.addSeparator();
28285         this.loading = this.navgroup.addItem({
28286             tooltip: this.refreshText,
28287             cls: "btn-outline-secondary",
28288             html : ' <i class="fa fa-refresh"></i>',
28289             preventDefault: true,
28290             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28291         });
28292         
28293     },
28294
28295     // private
28296     updateInfo : function(){
28297         if(this.displayEl){
28298             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28299             var msg = count == 0 ?
28300                 this.emptyMsg :
28301                 String.format(
28302                     this.displayMsg,
28303                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28304                 );
28305             this.displayEl.update(msg);
28306         }
28307     },
28308
28309     // private
28310     onLoad : function(ds, r, o)
28311     {
28312         this.cursor = o.params && o.params.start ? o.params.start : 0;
28313         
28314         var d = this.getPageData(),
28315             ap = d.activePage,
28316             ps = d.pages;
28317         
28318         
28319         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28320         this.field.dom.value = ap;
28321         this.first.setDisabled(ap == 1);
28322         this.prev.setDisabled(ap == 1);
28323         this.next.setDisabled(ap == ps);
28324         this.last.setDisabled(ap == ps);
28325         this.loading.enable();
28326         this.updateInfo();
28327     },
28328
28329     // private
28330     getPageData : function(){
28331         var total = this.ds.getTotalCount();
28332         return {
28333             total : total,
28334             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28335             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28336         };
28337     },
28338
28339     // private
28340     onLoadError : function(){
28341         this.loading.enable();
28342     },
28343
28344     // private
28345     onPagingKeydown : function(e){
28346         var k = e.getKey();
28347         var d = this.getPageData();
28348         if(k == e.RETURN){
28349             var v = this.field.dom.value, pageNum;
28350             if(!v || isNaN(pageNum = parseInt(v, 10))){
28351                 this.field.dom.value = d.activePage;
28352                 return;
28353             }
28354             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28355             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28356             e.stopEvent();
28357         }
28358         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))
28359         {
28360           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28361           this.field.dom.value = pageNum;
28362           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28363           e.stopEvent();
28364         }
28365         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28366         {
28367           var v = this.field.dom.value, pageNum; 
28368           var increment = (e.shiftKey) ? 10 : 1;
28369           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28370                 increment *= -1;
28371           }
28372           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28373             this.field.dom.value = d.activePage;
28374             return;
28375           }
28376           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28377           {
28378             this.field.dom.value = parseInt(v, 10) + increment;
28379             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28380             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28381           }
28382           e.stopEvent();
28383         }
28384     },
28385
28386     // private
28387     beforeLoad : function(){
28388         if(this.loading){
28389             this.loading.disable();
28390         }
28391     },
28392
28393     // private
28394     onClick : function(which){
28395         
28396         var ds = this.ds;
28397         if (!ds) {
28398             return;
28399         }
28400         
28401         switch(which){
28402             case "first":
28403                 ds.load({params:{start: 0, limit: this.pageSize}});
28404             break;
28405             case "prev":
28406                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28407             break;
28408             case "next":
28409                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28410             break;
28411             case "last":
28412                 var total = ds.getTotalCount();
28413                 var extra = total % this.pageSize;
28414                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28415                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28416             break;
28417             case "refresh":
28418                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28419             break;
28420         }
28421     },
28422
28423     /**
28424      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28425      * @param {Roo.data.Store} store The data store to unbind
28426      */
28427     unbind : function(ds){
28428         ds.un("beforeload", this.beforeLoad, this);
28429         ds.un("load", this.onLoad, this);
28430         ds.un("loadexception", this.onLoadError, this);
28431         ds.un("remove", this.updateInfo, this);
28432         ds.un("add", this.updateInfo, this);
28433         this.ds = undefined;
28434     },
28435
28436     /**
28437      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28438      * @param {Roo.data.Store} store The data store to bind
28439      */
28440     bind : function(ds){
28441         ds.on("beforeload", this.beforeLoad, this);
28442         ds.on("load", this.onLoad, this);
28443         ds.on("loadexception", this.onLoadError, this);
28444         ds.on("remove", this.updateInfo, this);
28445         ds.on("add", this.updateInfo, this);
28446         this.ds = ds;
28447     }
28448 });/*
28449  * - LGPL
28450  *
28451  * element
28452  * 
28453  */
28454
28455 /**
28456  * @class Roo.bootstrap.MessageBar
28457  * @extends Roo.bootstrap.Component
28458  * Bootstrap MessageBar class
28459  * @cfg {String} html contents of the MessageBar
28460  * @cfg {String} weight (info | success | warning | danger) default info
28461  * @cfg {String} beforeClass insert the bar before the given class
28462  * @cfg {Boolean} closable (true | false) default false
28463  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28464  * 
28465  * @constructor
28466  * Create a new Element
28467  * @param {Object} config The config object
28468  */
28469
28470 Roo.bootstrap.MessageBar = function(config){
28471     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28472 };
28473
28474 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28475     
28476     html: '',
28477     weight: 'info',
28478     closable: false,
28479     fixed: false,
28480     beforeClass: 'bootstrap-sticky-wrap',
28481     
28482     getAutoCreate : function(){
28483         
28484         var cfg = {
28485             tag: 'div',
28486             cls: 'alert alert-dismissable alert-' + this.weight,
28487             cn: [
28488                 {
28489                     tag: 'span',
28490                     cls: 'message',
28491                     html: this.html || ''
28492                 }
28493             ]
28494         };
28495         
28496         if(this.fixed){
28497             cfg.cls += ' alert-messages-fixed';
28498         }
28499         
28500         if(this.closable){
28501             cfg.cn.push({
28502                 tag: 'button',
28503                 cls: 'close',
28504                 html: 'x'
28505             });
28506         }
28507         
28508         return cfg;
28509     },
28510     
28511     onRender : function(ct, position)
28512     {
28513         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28514         
28515         if(!this.el){
28516             var cfg = Roo.apply({},  this.getAutoCreate());
28517             cfg.id = Roo.id();
28518             
28519             if (this.cls) {
28520                 cfg.cls += ' ' + this.cls;
28521             }
28522             if (this.style) {
28523                 cfg.style = this.style;
28524             }
28525             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28526             
28527             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28528         }
28529         
28530         this.el.select('>button.close').on('click', this.hide, this);
28531         
28532     },
28533     
28534     show : function()
28535     {
28536         if (!this.rendered) {
28537             this.render();
28538         }
28539         
28540         this.el.show();
28541         
28542         this.fireEvent('show', this);
28543         
28544     },
28545     
28546     hide : function()
28547     {
28548         if (!this.rendered) {
28549             this.render();
28550         }
28551         
28552         this.el.hide();
28553         
28554         this.fireEvent('hide', this);
28555     },
28556     
28557     update : function()
28558     {
28559 //        var e = this.el.dom.firstChild;
28560 //        
28561 //        if(this.closable){
28562 //            e = e.nextSibling;
28563 //        }
28564 //        
28565 //        e.data = this.html || '';
28566
28567         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28568     }
28569    
28570 });
28571
28572  
28573
28574      /*
28575  * - LGPL
28576  *
28577  * Graph
28578  * 
28579  */
28580
28581
28582 /**
28583  * @class Roo.bootstrap.Graph
28584  * @extends Roo.bootstrap.Component
28585  * Bootstrap Graph class
28586 > Prameters
28587  -sm {number} sm 4
28588  -md {number} md 5
28589  @cfg {String} graphtype  bar | vbar | pie
28590  @cfg {number} g_x coodinator | centre x (pie)
28591  @cfg {number} g_y coodinator | centre y (pie)
28592  @cfg {number} g_r radius (pie)
28593  @cfg {number} g_height height of the chart (respected by all elements in the set)
28594  @cfg {number} g_width width of the chart (respected by all elements in the set)
28595  @cfg {Object} title The title of the chart
28596     
28597  -{Array}  values
28598  -opts (object) options for the chart 
28599      o {
28600      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28601      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28602      o vgutter (number)
28603      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.
28604      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28605      o to
28606      o stretch (boolean)
28607      o }
28608  -opts (object) options for the pie
28609      o{
28610      o cut
28611      o startAngle (number)
28612      o endAngle (number)
28613      } 
28614  *
28615  * @constructor
28616  * Create a new Input
28617  * @param {Object} config The config object
28618  */
28619
28620 Roo.bootstrap.Graph = function(config){
28621     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28622     
28623     this.addEvents({
28624         // img events
28625         /**
28626          * @event click
28627          * The img click event for the img.
28628          * @param {Roo.EventObject} e
28629          */
28630         "click" : true
28631     });
28632 };
28633
28634 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28635     
28636     sm: 4,
28637     md: 5,
28638     graphtype: 'bar',
28639     g_height: 250,
28640     g_width: 400,
28641     g_x: 50,
28642     g_y: 50,
28643     g_r: 30,
28644     opts:{
28645         //g_colors: this.colors,
28646         g_type: 'soft',
28647         g_gutter: '20%'
28648
28649     },
28650     title : false,
28651
28652     getAutoCreate : function(){
28653         
28654         var cfg = {
28655             tag: 'div',
28656             html : null
28657         };
28658         
28659         
28660         return  cfg;
28661     },
28662
28663     onRender : function(ct,position){
28664         
28665         
28666         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28667         
28668         if (typeof(Raphael) == 'undefined') {
28669             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28670             return;
28671         }
28672         
28673         this.raphael = Raphael(this.el.dom);
28674         
28675                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28676                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28677                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28678                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28679                 /*
28680                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28681                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28682                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28683                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28684                 
28685                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28686                 r.barchart(330, 10, 300, 220, data1);
28687                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28688                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28689                 */
28690                 
28691                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28692                 // r.barchart(30, 30, 560, 250,  xdata, {
28693                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28694                 //     axis : "0 0 1 1",
28695                 //     axisxlabels :  xdata
28696                 //     //yvalues : cols,
28697                    
28698                 // });
28699 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28700 //        
28701 //        this.load(null,xdata,{
28702 //                axis : "0 0 1 1",
28703 //                axisxlabels :  xdata
28704 //                });
28705
28706     },
28707
28708     load : function(graphtype,xdata,opts)
28709     {
28710         this.raphael.clear();
28711         if(!graphtype) {
28712             graphtype = this.graphtype;
28713         }
28714         if(!opts){
28715             opts = this.opts;
28716         }
28717         var r = this.raphael,
28718             fin = function () {
28719                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28720             },
28721             fout = function () {
28722                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28723             },
28724             pfin = function() {
28725                 this.sector.stop();
28726                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28727
28728                 if (this.label) {
28729                     this.label[0].stop();
28730                     this.label[0].attr({ r: 7.5 });
28731                     this.label[1].attr({ "font-weight": 800 });
28732                 }
28733             },
28734             pfout = function() {
28735                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28736
28737                 if (this.label) {
28738                     this.label[0].animate({ r: 5 }, 500, "bounce");
28739                     this.label[1].attr({ "font-weight": 400 });
28740                 }
28741             };
28742
28743         switch(graphtype){
28744             case 'bar':
28745                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28746                 break;
28747             case 'hbar':
28748                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28749                 break;
28750             case 'pie':
28751 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28752 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28753 //            
28754                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28755                 
28756                 break;
28757
28758         }
28759         
28760         if(this.title){
28761             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28762         }
28763         
28764     },
28765     
28766     setTitle: function(o)
28767     {
28768         this.title = o;
28769     },
28770     
28771     initEvents: function() {
28772         
28773         if(!this.href){
28774             this.el.on('click', this.onClick, this);
28775         }
28776     },
28777     
28778     onClick : function(e)
28779     {
28780         Roo.log('img onclick');
28781         this.fireEvent('click', this, e);
28782     }
28783    
28784 });
28785
28786  
28787 /*
28788  * - LGPL
28789  *
28790  * numberBox
28791  * 
28792  */
28793 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28794
28795 /**
28796  * @class Roo.bootstrap.dash.NumberBox
28797  * @extends Roo.bootstrap.Component
28798  * Bootstrap NumberBox class
28799  * @cfg {String} headline Box headline
28800  * @cfg {String} content Box content
28801  * @cfg {String} icon Box icon
28802  * @cfg {String} footer Footer text
28803  * @cfg {String} fhref Footer href
28804  * 
28805  * @constructor
28806  * Create a new NumberBox
28807  * @param {Object} config The config object
28808  */
28809
28810
28811 Roo.bootstrap.dash.NumberBox = function(config){
28812     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28813     
28814 };
28815
28816 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28817     
28818     headline : '',
28819     content : '',
28820     icon : '',
28821     footer : '',
28822     fhref : '',
28823     ficon : '',
28824     
28825     getAutoCreate : function(){
28826         
28827         var cfg = {
28828             tag : 'div',
28829             cls : 'small-box ',
28830             cn : [
28831                 {
28832                     tag : 'div',
28833                     cls : 'inner',
28834                     cn :[
28835                         {
28836                             tag : 'h3',
28837                             cls : 'roo-headline',
28838                             html : this.headline
28839                         },
28840                         {
28841                             tag : 'p',
28842                             cls : 'roo-content',
28843                             html : this.content
28844                         }
28845                     ]
28846                 }
28847             ]
28848         };
28849         
28850         if(this.icon){
28851             cfg.cn.push({
28852                 tag : 'div',
28853                 cls : 'icon',
28854                 cn :[
28855                     {
28856                         tag : 'i',
28857                         cls : 'ion ' + this.icon
28858                     }
28859                 ]
28860             });
28861         }
28862         
28863         if(this.footer){
28864             var footer = {
28865                 tag : 'a',
28866                 cls : 'small-box-footer',
28867                 href : this.fhref || '#',
28868                 html : this.footer
28869             };
28870             
28871             cfg.cn.push(footer);
28872             
28873         }
28874         
28875         return  cfg;
28876     },
28877
28878     onRender : function(ct,position){
28879         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28880
28881
28882        
28883                 
28884     },
28885
28886     setHeadline: function (value)
28887     {
28888         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28889     },
28890     
28891     setFooter: function (value, href)
28892     {
28893         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28894         
28895         if(href){
28896             this.el.select('a.small-box-footer',true).first().attr('href', href);
28897         }
28898         
28899     },
28900
28901     setContent: function (value)
28902     {
28903         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28904     },
28905
28906     initEvents: function() 
28907     {   
28908         
28909     }
28910     
28911 });
28912
28913  
28914 /*
28915  * - LGPL
28916  *
28917  * TabBox
28918  * 
28919  */
28920 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28921
28922 /**
28923  * @class Roo.bootstrap.dash.TabBox
28924  * @extends Roo.bootstrap.Component
28925  * Bootstrap TabBox class
28926  * @cfg {String} title Title of the TabBox
28927  * @cfg {String} icon Icon of the TabBox
28928  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28929  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28930  * 
28931  * @constructor
28932  * Create a new TabBox
28933  * @param {Object} config The config object
28934  */
28935
28936
28937 Roo.bootstrap.dash.TabBox = function(config){
28938     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28939     this.addEvents({
28940         // raw events
28941         /**
28942          * @event addpane
28943          * When a pane is added
28944          * @param {Roo.bootstrap.dash.TabPane} pane
28945          */
28946         "addpane" : true,
28947         /**
28948          * @event activatepane
28949          * When a pane is activated
28950          * @param {Roo.bootstrap.dash.TabPane} pane
28951          */
28952         "activatepane" : true
28953         
28954          
28955     });
28956     
28957     this.panes = [];
28958 };
28959
28960 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28961
28962     title : '',
28963     icon : false,
28964     showtabs : true,
28965     tabScrollable : false,
28966     
28967     getChildContainer : function()
28968     {
28969         return this.el.select('.tab-content', true).first();
28970     },
28971     
28972     getAutoCreate : function(){
28973         
28974         var header = {
28975             tag: 'li',
28976             cls: 'pull-left header',
28977             html: this.title,
28978             cn : []
28979         };
28980         
28981         if(this.icon){
28982             header.cn.push({
28983                 tag: 'i',
28984                 cls: 'fa ' + this.icon
28985             });
28986         }
28987         
28988         var h = {
28989             tag: 'ul',
28990             cls: 'nav nav-tabs pull-right',
28991             cn: [
28992                 header
28993             ]
28994         };
28995         
28996         if(this.tabScrollable){
28997             h = {
28998                 tag: 'div',
28999                 cls: 'tab-header',
29000                 cn: [
29001                     {
29002                         tag: 'ul',
29003                         cls: 'nav nav-tabs pull-right',
29004                         cn: [
29005                             header
29006                         ]
29007                     }
29008                 ]
29009             };
29010         }
29011         
29012         var cfg = {
29013             tag: 'div',
29014             cls: 'nav-tabs-custom',
29015             cn: [
29016                 h,
29017                 {
29018                     tag: 'div',
29019                     cls: 'tab-content no-padding',
29020                     cn: []
29021                 }
29022             ]
29023         };
29024
29025         return  cfg;
29026     },
29027     initEvents : function()
29028     {
29029         //Roo.log('add add pane handler');
29030         this.on('addpane', this.onAddPane, this);
29031     },
29032      /**
29033      * Updates the box title
29034      * @param {String} html to set the title to.
29035      */
29036     setTitle : function(value)
29037     {
29038         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29039     },
29040     onAddPane : function(pane)
29041     {
29042         this.panes.push(pane);
29043         //Roo.log('addpane');
29044         //Roo.log(pane);
29045         // tabs are rendere left to right..
29046         if(!this.showtabs){
29047             return;
29048         }
29049         
29050         var ctr = this.el.select('.nav-tabs', true).first();
29051          
29052          
29053         var existing = ctr.select('.nav-tab',true);
29054         var qty = existing.getCount();;
29055         
29056         
29057         var tab = ctr.createChild({
29058             tag : 'li',
29059             cls : 'nav-tab' + (qty ? '' : ' active'),
29060             cn : [
29061                 {
29062                     tag : 'a',
29063                     href:'#',
29064                     html : pane.title
29065                 }
29066             ]
29067         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29068         pane.tab = tab;
29069         
29070         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29071         if (!qty) {
29072             pane.el.addClass('active');
29073         }
29074         
29075                 
29076     },
29077     onTabClick : function(ev,un,ob,pane)
29078     {
29079         //Roo.log('tab - prev default');
29080         ev.preventDefault();
29081         
29082         
29083         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29084         pane.tab.addClass('active');
29085         //Roo.log(pane.title);
29086         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29087         // technically we should have a deactivate event.. but maybe add later.
29088         // and it should not de-activate the selected tab...
29089         this.fireEvent('activatepane', pane);
29090         pane.el.addClass('active');
29091         pane.fireEvent('activate');
29092         
29093         
29094     },
29095     
29096     getActivePane : function()
29097     {
29098         var r = false;
29099         Roo.each(this.panes, function(p) {
29100             if(p.el.hasClass('active')){
29101                 r = p;
29102                 return false;
29103             }
29104             
29105             return;
29106         });
29107         
29108         return r;
29109     }
29110     
29111     
29112 });
29113
29114  
29115 /*
29116  * - LGPL
29117  *
29118  * Tab pane
29119  * 
29120  */
29121 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29122 /**
29123  * @class Roo.bootstrap.TabPane
29124  * @extends Roo.bootstrap.Component
29125  * Bootstrap TabPane class
29126  * @cfg {Boolean} active (false | true) Default false
29127  * @cfg {String} title title of panel
29128
29129  * 
29130  * @constructor
29131  * Create a new TabPane
29132  * @param {Object} config The config object
29133  */
29134
29135 Roo.bootstrap.dash.TabPane = function(config){
29136     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29137     
29138     this.addEvents({
29139         // raw events
29140         /**
29141          * @event activate
29142          * When a pane is activated
29143          * @param {Roo.bootstrap.dash.TabPane} pane
29144          */
29145         "activate" : true
29146          
29147     });
29148 };
29149
29150 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29151     
29152     active : false,
29153     title : '',
29154     
29155     // the tabBox that this is attached to.
29156     tab : false,
29157      
29158     getAutoCreate : function() 
29159     {
29160         var cfg = {
29161             tag: 'div',
29162             cls: 'tab-pane'
29163         };
29164         
29165         if(this.active){
29166             cfg.cls += ' active';
29167         }
29168         
29169         return cfg;
29170     },
29171     initEvents  : function()
29172     {
29173         //Roo.log('trigger add pane handler');
29174         this.parent().fireEvent('addpane', this)
29175     },
29176     
29177      /**
29178      * Updates the tab title 
29179      * @param {String} html to set the title to.
29180      */
29181     setTitle: function(str)
29182     {
29183         if (!this.tab) {
29184             return;
29185         }
29186         this.title = str;
29187         this.tab.select('a', true).first().dom.innerHTML = str;
29188         
29189     }
29190     
29191     
29192     
29193 });
29194
29195  
29196
29197
29198  /*
29199  * - LGPL
29200  *
29201  * menu
29202  * 
29203  */
29204 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29205
29206 /**
29207  * @class Roo.bootstrap.menu.Menu
29208  * @extends Roo.bootstrap.Component
29209  * Bootstrap Menu class - container for Menu
29210  * @cfg {String} html Text of the menu
29211  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29212  * @cfg {String} icon Font awesome icon
29213  * @cfg {String} pos Menu align to (top | bottom) default bottom
29214  * 
29215  * 
29216  * @constructor
29217  * Create a new Menu
29218  * @param {Object} config The config object
29219  */
29220
29221
29222 Roo.bootstrap.menu.Menu = function(config){
29223     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29224     
29225     this.addEvents({
29226         /**
29227          * @event beforeshow
29228          * Fires before this menu is displayed
29229          * @param {Roo.bootstrap.menu.Menu} this
29230          */
29231         beforeshow : true,
29232         /**
29233          * @event beforehide
29234          * Fires before this menu is hidden
29235          * @param {Roo.bootstrap.menu.Menu} this
29236          */
29237         beforehide : true,
29238         /**
29239          * @event show
29240          * Fires after this menu is displayed
29241          * @param {Roo.bootstrap.menu.Menu} this
29242          */
29243         show : true,
29244         /**
29245          * @event hide
29246          * Fires after this menu is hidden
29247          * @param {Roo.bootstrap.menu.Menu} this
29248          */
29249         hide : true,
29250         /**
29251          * @event click
29252          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29253          * @param {Roo.bootstrap.menu.Menu} this
29254          * @param {Roo.EventObject} e
29255          */
29256         click : true
29257     });
29258     
29259 };
29260
29261 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29262     
29263     submenu : false,
29264     html : '',
29265     weight : 'default',
29266     icon : false,
29267     pos : 'bottom',
29268     
29269     
29270     getChildContainer : function() {
29271         if(this.isSubMenu){
29272             return this.el;
29273         }
29274         
29275         return this.el.select('ul.dropdown-menu', true).first();  
29276     },
29277     
29278     getAutoCreate : function()
29279     {
29280         var text = [
29281             {
29282                 tag : 'span',
29283                 cls : 'roo-menu-text',
29284                 html : this.html
29285             }
29286         ];
29287         
29288         if(this.icon){
29289             text.unshift({
29290                 tag : 'i',
29291                 cls : 'fa ' + this.icon
29292             })
29293         }
29294         
29295         
29296         var cfg = {
29297             tag : 'div',
29298             cls : 'btn-group',
29299             cn : [
29300                 {
29301                     tag : 'button',
29302                     cls : 'dropdown-button btn btn-' + this.weight,
29303                     cn : text
29304                 },
29305                 {
29306                     tag : 'button',
29307                     cls : 'dropdown-toggle btn btn-' + this.weight,
29308                     cn : [
29309                         {
29310                             tag : 'span',
29311                             cls : 'caret'
29312                         }
29313                     ]
29314                 },
29315                 {
29316                     tag : 'ul',
29317                     cls : 'dropdown-menu'
29318                 }
29319             ]
29320             
29321         };
29322         
29323         if(this.pos == 'top'){
29324             cfg.cls += ' dropup';
29325         }
29326         
29327         if(this.isSubMenu){
29328             cfg = {
29329                 tag : 'ul',
29330                 cls : 'dropdown-menu'
29331             }
29332         }
29333         
29334         return cfg;
29335     },
29336     
29337     onRender : function(ct, position)
29338     {
29339         this.isSubMenu = ct.hasClass('dropdown-submenu');
29340         
29341         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29342     },
29343     
29344     initEvents : function() 
29345     {
29346         if(this.isSubMenu){
29347             return;
29348         }
29349         
29350         this.hidden = true;
29351         
29352         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29353         this.triggerEl.on('click', this.onTriggerPress, this);
29354         
29355         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29356         this.buttonEl.on('click', this.onClick, this);
29357         
29358     },
29359     
29360     list : function()
29361     {
29362         if(this.isSubMenu){
29363             return this.el;
29364         }
29365         
29366         return this.el.select('ul.dropdown-menu', true).first();
29367     },
29368     
29369     onClick : function(e)
29370     {
29371         this.fireEvent("click", this, e);
29372     },
29373     
29374     onTriggerPress  : function(e)
29375     {   
29376         if (this.isVisible()) {
29377             this.hide();
29378         } else {
29379             this.show();
29380         }
29381     },
29382     
29383     isVisible : function(){
29384         return !this.hidden;
29385     },
29386     
29387     show : function()
29388     {
29389         this.fireEvent("beforeshow", this);
29390         
29391         this.hidden = false;
29392         this.el.addClass('open');
29393         
29394         Roo.get(document).on("mouseup", this.onMouseUp, this);
29395         
29396         this.fireEvent("show", this);
29397         
29398         
29399     },
29400     
29401     hide : function()
29402     {
29403         this.fireEvent("beforehide", this);
29404         
29405         this.hidden = true;
29406         this.el.removeClass('open');
29407         
29408         Roo.get(document).un("mouseup", this.onMouseUp);
29409         
29410         this.fireEvent("hide", this);
29411     },
29412     
29413     onMouseUp : function()
29414     {
29415         this.hide();
29416     }
29417     
29418 });
29419
29420  
29421  /*
29422  * - LGPL
29423  *
29424  * menu item
29425  * 
29426  */
29427 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29428
29429 /**
29430  * @class Roo.bootstrap.menu.Item
29431  * @extends Roo.bootstrap.Component
29432  * Bootstrap MenuItem class
29433  * @cfg {Boolean} submenu (true | false) default false
29434  * @cfg {String} html text of the item
29435  * @cfg {String} href the link
29436  * @cfg {Boolean} disable (true | false) default false
29437  * @cfg {Boolean} preventDefault (true | false) default true
29438  * @cfg {String} icon Font awesome icon
29439  * @cfg {String} pos Submenu align to (left | right) default right 
29440  * 
29441  * 
29442  * @constructor
29443  * Create a new Item
29444  * @param {Object} config The config object
29445  */
29446
29447
29448 Roo.bootstrap.menu.Item = function(config){
29449     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29450     this.addEvents({
29451         /**
29452          * @event mouseover
29453          * Fires when the mouse is hovering over this menu
29454          * @param {Roo.bootstrap.menu.Item} this
29455          * @param {Roo.EventObject} e
29456          */
29457         mouseover : true,
29458         /**
29459          * @event mouseout
29460          * Fires when the mouse exits this menu
29461          * @param {Roo.bootstrap.menu.Item} this
29462          * @param {Roo.EventObject} e
29463          */
29464         mouseout : true,
29465         // raw events
29466         /**
29467          * @event click
29468          * The raw click event for the entire grid.
29469          * @param {Roo.EventObject} e
29470          */
29471         click : true
29472     });
29473 };
29474
29475 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29476     
29477     submenu : false,
29478     href : '',
29479     html : '',
29480     preventDefault: true,
29481     disable : false,
29482     icon : false,
29483     pos : 'right',
29484     
29485     getAutoCreate : function()
29486     {
29487         var text = [
29488             {
29489                 tag : 'span',
29490                 cls : 'roo-menu-item-text',
29491                 html : this.html
29492             }
29493         ];
29494         
29495         if(this.icon){
29496             text.unshift({
29497                 tag : 'i',
29498                 cls : 'fa ' + this.icon
29499             })
29500         }
29501         
29502         var cfg = {
29503             tag : 'li',
29504             cn : [
29505                 {
29506                     tag : 'a',
29507                     href : this.href || '#',
29508                     cn : text
29509                 }
29510             ]
29511         };
29512         
29513         if(this.disable){
29514             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29515         }
29516         
29517         if(this.submenu){
29518             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29519             
29520             if(this.pos == 'left'){
29521                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29522             }
29523         }
29524         
29525         return cfg;
29526     },
29527     
29528     initEvents : function() 
29529     {
29530         this.el.on('mouseover', this.onMouseOver, this);
29531         this.el.on('mouseout', this.onMouseOut, this);
29532         
29533         this.el.select('a', true).first().on('click', this.onClick, this);
29534         
29535     },
29536     
29537     onClick : function(e)
29538     {
29539         if(this.preventDefault){
29540             e.preventDefault();
29541         }
29542         
29543         this.fireEvent("click", this, e);
29544     },
29545     
29546     onMouseOver : function(e)
29547     {
29548         if(this.submenu && this.pos == 'left'){
29549             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29550         }
29551         
29552         this.fireEvent("mouseover", this, e);
29553     },
29554     
29555     onMouseOut : function(e)
29556     {
29557         this.fireEvent("mouseout", this, e);
29558     }
29559 });
29560
29561  
29562
29563  /*
29564  * - LGPL
29565  *
29566  * menu separator
29567  * 
29568  */
29569 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29570
29571 /**
29572  * @class Roo.bootstrap.menu.Separator
29573  * @extends Roo.bootstrap.Component
29574  * Bootstrap Separator class
29575  * 
29576  * @constructor
29577  * Create a new Separator
29578  * @param {Object} config The config object
29579  */
29580
29581
29582 Roo.bootstrap.menu.Separator = function(config){
29583     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29584 };
29585
29586 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29587     
29588     getAutoCreate : function(){
29589         var cfg = {
29590             tag : 'li',
29591             cls: 'dropdown-divider divider'
29592         };
29593         
29594         return cfg;
29595     }
29596    
29597 });
29598
29599  
29600
29601  /*
29602  * - LGPL
29603  *
29604  * Tooltip
29605  * 
29606  */
29607
29608 /**
29609  * @class Roo.bootstrap.Tooltip
29610  * Bootstrap Tooltip class
29611  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29612  * to determine which dom element triggers the tooltip.
29613  * 
29614  * It needs to add support for additional attributes like tooltip-position
29615  * 
29616  * @constructor
29617  * Create a new Toolti
29618  * @param {Object} config The config object
29619  */
29620
29621 Roo.bootstrap.Tooltip = function(config){
29622     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29623     
29624     this.alignment = Roo.bootstrap.Tooltip.alignment;
29625     
29626     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29627         this.alignment = config.alignment;
29628     }
29629     
29630 };
29631
29632 Roo.apply(Roo.bootstrap.Tooltip, {
29633     /**
29634      * @function init initialize tooltip monitoring.
29635      * @static
29636      */
29637     currentEl : false,
29638     currentTip : false,
29639     currentRegion : false,
29640     
29641     //  init : delay?
29642     
29643     init : function()
29644     {
29645         Roo.get(document).on('mouseover', this.enter ,this);
29646         Roo.get(document).on('mouseout', this.leave, this);
29647          
29648         
29649         this.currentTip = new Roo.bootstrap.Tooltip();
29650     },
29651     
29652     enter : function(ev)
29653     {
29654         var dom = ev.getTarget();
29655         
29656         //Roo.log(['enter',dom]);
29657         var el = Roo.fly(dom);
29658         if (this.currentEl) {
29659             //Roo.log(dom);
29660             //Roo.log(this.currentEl);
29661             //Roo.log(this.currentEl.contains(dom));
29662             if (this.currentEl == el) {
29663                 return;
29664             }
29665             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29666                 return;
29667             }
29668
29669         }
29670         
29671         if (this.currentTip.el) {
29672             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29673         }    
29674         //Roo.log(ev);
29675         
29676         if(!el || el.dom == document){
29677             return;
29678         }
29679         
29680         var bindEl = el; 
29681         var pel = false;
29682         if (!el.attr('tooltip')) {
29683             pel = el.findParent("[tooltip]");
29684             if (pel) {
29685                 bindEl = Roo.get(pel);
29686             }
29687         }
29688         
29689        
29690         
29691         // you can not look for children, as if el is the body.. then everythign is the child..
29692         if (!pel && !el.attr('tooltip')) { //
29693             if (!el.select("[tooltip]").elements.length) {
29694                 return;
29695             }
29696             // is the mouse over this child...?
29697             bindEl = el.select("[tooltip]").first();
29698             var xy = ev.getXY();
29699             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29700                 //Roo.log("not in region.");
29701                 return;
29702             }
29703             //Roo.log("child element over..");
29704             
29705         }
29706         this.currentEl = el;
29707         this.currentTip.bind(bindEl);
29708         this.currentRegion = Roo.lib.Region.getRegion(dom);
29709         this.currentTip.enter();
29710         
29711     },
29712     leave : function(ev)
29713     {
29714         var dom = ev.getTarget();
29715         //Roo.log(['leave',dom]);
29716         if (!this.currentEl) {
29717             return;
29718         }
29719         
29720         
29721         if (dom != this.currentEl.dom) {
29722             return;
29723         }
29724         var xy = ev.getXY();
29725         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29726             return;
29727         }
29728         // only activate leave if mouse cursor is outside... bounding box..
29729         
29730         
29731         
29732         
29733         if (this.currentTip) {
29734             this.currentTip.leave();
29735         }
29736         //Roo.log('clear currentEl');
29737         this.currentEl = false;
29738         
29739         
29740     },
29741     alignment : {
29742         'left' : ['r-l', [-2,0], 'right'],
29743         'right' : ['l-r', [2,0], 'left'],
29744         'bottom' : ['t-b', [0,2], 'top'],
29745         'top' : [ 'b-t', [0,-2], 'bottom']
29746     }
29747     
29748 });
29749
29750
29751 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29752     
29753     
29754     bindEl : false,
29755     
29756     delay : null, // can be { show : 300 , hide: 500}
29757     
29758     timeout : null,
29759     
29760     hoverState : null, //???
29761     
29762     placement : 'bottom', 
29763     
29764     alignment : false,
29765     
29766     getAutoCreate : function(){
29767     
29768         var cfg = {
29769            cls : 'tooltip',   
29770            role : 'tooltip',
29771            cn : [
29772                 {
29773                     cls : 'tooltip-arrow arrow'
29774                 },
29775                 {
29776                     cls : 'tooltip-inner'
29777                 }
29778            ]
29779         };
29780         
29781         return cfg;
29782     },
29783     bind : function(el)
29784     {
29785         this.bindEl = el;
29786     },
29787     
29788     initEvents : function()
29789     {
29790         this.arrowEl = this.el.select('.arrow', true).first();
29791         this.innerEl = this.el.select('.tooltip-inner', true).first();
29792     },
29793     
29794     enter : function () {
29795        
29796         if (this.timeout != null) {
29797             clearTimeout(this.timeout);
29798         }
29799         
29800         this.hoverState = 'in';
29801          //Roo.log("enter - show");
29802         if (!this.delay || !this.delay.show) {
29803             this.show();
29804             return;
29805         }
29806         var _t = this;
29807         this.timeout = setTimeout(function () {
29808             if (_t.hoverState == 'in') {
29809                 _t.show();
29810             }
29811         }, this.delay.show);
29812     },
29813     leave : function()
29814     {
29815         clearTimeout(this.timeout);
29816     
29817         this.hoverState = 'out';
29818          if (!this.delay || !this.delay.hide) {
29819             this.hide();
29820             return;
29821         }
29822        
29823         var _t = this;
29824         this.timeout = setTimeout(function () {
29825             //Roo.log("leave - timeout");
29826             
29827             if (_t.hoverState == 'out') {
29828                 _t.hide();
29829                 Roo.bootstrap.Tooltip.currentEl = false;
29830             }
29831         }, delay);
29832     },
29833     
29834     show : function (msg)
29835     {
29836         if (!this.el) {
29837             this.render(document.body);
29838         }
29839         // set content.
29840         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29841         
29842         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29843         
29844         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29845         
29846         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29847                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29848         
29849         var placement = typeof this.placement == 'function' ?
29850             this.placement.call(this, this.el, on_el) :
29851             this.placement;
29852             
29853         var autoToken = /\s?auto?\s?/i;
29854         var autoPlace = autoToken.test(placement);
29855         if (autoPlace) {
29856             placement = placement.replace(autoToken, '') || 'top';
29857         }
29858         
29859         //this.el.detach()
29860         //this.el.setXY([0,0]);
29861         this.el.show();
29862         //this.el.dom.style.display='block';
29863         
29864         //this.el.appendTo(on_el);
29865         
29866         var p = this.getPosition();
29867         var box = this.el.getBox();
29868         
29869         if (autoPlace) {
29870             // fixme..
29871         }
29872         
29873         var align = this.alignment[placement];
29874         
29875         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29876         
29877         if(placement == 'top' || placement == 'bottom'){
29878             if(xy[0] < 0){
29879                 placement = 'right';
29880             }
29881             
29882             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29883                 placement = 'left';
29884             }
29885             
29886             var scroll = Roo.select('body', true).first().getScroll();
29887             
29888             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29889                 placement = 'top';
29890             }
29891             
29892             align = this.alignment[placement];
29893             
29894             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29895             
29896         }
29897         
29898         var elems = document.getElementsByTagName('div');
29899         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29900         for (var i = 0; i < elems.length; i++) {
29901           var zindex = Number.parseInt(
29902                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29903                 10
29904           );
29905           if (zindex > highest) {
29906             highest = zindex;
29907           }
29908         }
29909         
29910         
29911         
29912         this.el.dom.style.zIndex = highest;
29913         
29914         this.el.alignTo(this.bindEl, align[0],align[1]);
29915         //var arrow = this.el.select('.arrow',true).first();
29916         //arrow.set(align[2], 
29917         
29918         this.el.addClass(placement);
29919         this.el.addClass("bs-tooltip-"+ placement);
29920         
29921         this.el.addClass('in fade show');
29922         
29923         this.hoverState = null;
29924         
29925         if (this.el.hasClass('fade')) {
29926             // fade it?
29927         }
29928         
29929         
29930         
29931         
29932         
29933     },
29934     hide : function()
29935     {
29936          
29937         if (!this.el) {
29938             return;
29939         }
29940         //this.el.setXY([0,0]);
29941         this.el.removeClass(['show', 'in']);
29942         //this.el.hide();
29943         
29944     }
29945     
29946 });
29947  
29948
29949  /*
29950  * - LGPL
29951  *
29952  * Location Picker
29953  * 
29954  */
29955
29956 /**
29957  * @class Roo.bootstrap.LocationPicker
29958  * @extends Roo.bootstrap.Component
29959  * Bootstrap LocationPicker class
29960  * @cfg {Number} latitude Position when init default 0
29961  * @cfg {Number} longitude Position when init default 0
29962  * @cfg {Number} zoom default 15
29963  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29964  * @cfg {Boolean} mapTypeControl default false
29965  * @cfg {Boolean} disableDoubleClickZoom default false
29966  * @cfg {Boolean} scrollwheel default true
29967  * @cfg {Boolean} streetViewControl default false
29968  * @cfg {Number} radius default 0
29969  * @cfg {String} locationName
29970  * @cfg {Boolean} draggable default true
29971  * @cfg {Boolean} enableAutocomplete default false
29972  * @cfg {Boolean} enableReverseGeocode default true
29973  * @cfg {String} markerTitle
29974  * 
29975  * @constructor
29976  * Create a new LocationPicker
29977  * @param {Object} config The config object
29978  */
29979
29980
29981 Roo.bootstrap.LocationPicker = function(config){
29982     
29983     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29984     
29985     this.addEvents({
29986         /**
29987          * @event initial
29988          * Fires when the picker initialized.
29989          * @param {Roo.bootstrap.LocationPicker} this
29990          * @param {Google Location} location
29991          */
29992         initial : true,
29993         /**
29994          * @event positionchanged
29995          * Fires when the picker position changed.
29996          * @param {Roo.bootstrap.LocationPicker} this
29997          * @param {Google Location} location
29998          */
29999         positionchanged : true,
30000         /**
30001          * @event resize
30002          * Fires when the map resize.
30003          * @param {Roo.bootstrap.LocationPicker} this
30004          */
30005         resize : true,
30006         /**
30007          * @event show
30008          * Fires when the map show.
30009          * @param {Roo.bootstrap.LocationPicker} this
30010          */
30011         show : true,
30012         /**
30013          * @event hide
30014          * Fires when the map hide.
30015          * @param {Roo.bootstrap.LocationPicker} this
30016          */
30017         hide : true,
30018         /**
30019          * @event mapClick
30020          * Fires when click the map.
30021          * @param {Roo.bootstrap.LocationPicker} this
30022          * @param {Map event} e
30023          */
30024         mapClick : true,
30025         /**
30026          * @event mapRightClick
30027          * Fires when right click the map.
30028          * @param {Roo.bootstrap.LocationPicker} this
30029          * @param {Map event} e
30030          */
30031         mapRightClick : true,
30032         /**
30033          * @event markerClick
30034          * Fires when click the marker.
30035          * @param {Roo.bootstrap.LocationPicker} this
30036          * @param {Map event} e
30037          */
30038         markerClick : true,
30039         /**
30040          * @event markerRightClick
30041          * Fires when right click the marker.
30042          * @param {Roo.bootstrap.LocationPicker} this
30043          * @param {Map event} e
30044          */
30045         markerRightClick : true,
30046         /**
30047          * @event OverlayViewDraw
30048          * Fires when OverlayView Draw
30049          * @param {Roo.bootstrap.LocationPicker} this
30050          */
30051         OverlayViewDraw : true,
30052         /**
30053          * @event OverlayViewOnAdd
30054          * Fires when OverlayView Draw
30055          * @param {Roo.bootstrap.LocationPicker} this
30056          */
30057         OverlayViewOnAdd : true,
30058         /**
30059          * @event OverlayViewOnRemove
30060          * Fires when OverlayView Draw
30061          * @param {Roo.bootstrap.LocationPicker} this
30062          */
30063         OverlayViewOnRemove : true,
30064         /**
30065          * @event OverlayViewShow
30066          * Fires when OverlayView Draw
30067          * @param {Roo.bootstrap.LocationPicker} this
30068          * @param {Pixel} cpx
30069          */
30070         OverlayViewShow : true,
30071         /**
30072          * @event OverlayViewHide
30073          * Fires when OverlayView Draw
30074          * @param {Roo.bootstrap.LocationPicker} this
30075          */
30076         OverlayViewHide : true,
30077         /**
30078          * @event loadexception
30079          * Fires when load google lib failed.
30080          * @param {Roo.bootstrap.LocationPicker} this
30081          */
30082         loadexception : true
30083     });
30084         
30085 };
30086
30087 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30088     
30089     gMapContext: false,
30090     
30091     latitude: 0,
30092     longitude: 0,
30093     zoom: 15,
30094     mapTypeId: false,
30095     mapTypeControl: false,
30096     disableDoubleClickZoom: false,
30097     scrollwheel: true,
30098     streetViewControl: false,
30099     radius: 0,
30100     locationName: '',
30101     draggable: true,
30102     enableAutocomplete: false,
30103     enableReverseGeocode: true,
30104     markerTitle: '',
30105     
30106     getAutoCreate: function()
30107     {
30108
30109         var cfg = {
30110             tag: 'div',
30111             cls: 'roo-location-picker'
30112         };
30113         
30114         return cfg
30115     },
30116     
30117     initEvents: function(ct, position)
30118     {       
30119         if(!this.el.getWidth() || this.isApplied()){
30120             return;
30121         }
30122         
30123         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30124         
30125         this.initial();
30126     },
30127     
30128     initial: function()
30129     {
30130         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30131             this.fireEvent('loadexception', this);
30132             return;
30133         }
30134         
30135         if(!this.mapTypeId){
30136             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30137         }
30138         
30139         this.gMapContext = this.GMapContext();
30140         
30141         this.initOverlayView();
30142         
30143         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30144         
30145         var _this = this;
30146                 
30147         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30148             _this.setPosition(_this.gMapContext.marker.position);
30149         });
30150         
30151         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30152             _this.fireEvent('mapClick', this, event);
30153             
30154         });
30155
30156         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30157             _this.fireEvent('mapRightClick', this, event);
30158             
30159         });
30160         
30161         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30162             _this.fireEvent('markerClick', this, event);
30163             
30164         });
30165
30166         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30167             _this.fireEvent('markerRightClick', this, event);
30168             
30169         });
30170         
30171         this.setPosition(this.gMapContext.location);
30172         
30173         this.fireEvent('initial', this, this.gMapContext.location);
30174     },
30175     
30176     initOverlayView: function()
30177     {
30178         var _this = this;
30179         
30180         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30181             
30182             draw: function()
30183             {
30184                 _this.fireEvent('OverlayViewDraw', _this);
30185             },
30186             
30187             onAdd: function()
30188             {
30189                 _this.fireEvent('OverlayViewOnAdd', _this);
30190             },
30191             
30192             onRemove: function()
30193             {
30194                 _this.fireEvent('OverlayViewOnRemove', _this);
30195             },
30196             
30197             show: function(cpx)
30198             {
30199                 _this.fireEvent('OverlayViewShow', _this, cpx);
30200             },
30201             
30202             hide: function()
30203             {
30204                 _this.fireEvent('OverlayViewHide', _this);
30205             }
30206             
30207         });
30208     },
30209     
30210     fromLatLngToContainerPixel: function(event)
30211     {
30212         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30213     },
30214     
30215     isApplied: function() 
30216     {
30217         return this.getGmapContext() == false ? false : true;
30218     },
30219     
30220     getGmapContext: function() 
30221     {
30222         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30223     },
30224     
30225     GMapContext: function() 
30226     {
30227         var position = new google.maps.LatLng(this.latitude, this.longitude);
30228         
30229         var _map = new google.maps.Map(this.el.dom, {
30230             center: position,
30231             zoom: this.zoom,
30232             mapTypeId: this.mapTypeId,
30233             mapTypeControl: this.mapTypeControl,
30234             disableDoubleClickZoom: this.disableDoubleClickZoom,
30235             scrollwheel: this.scrollwheel,
30236             streetViewControl: this.streetViewControl,
30237             locationName: this.locationName,
30238             draggable: this.draggable,
30239             enableAutocomplete: this.enableAutocomplete,
30240             enableReverseGeocode: this.enableReverseGeocode
30241         });
30242         
30243         var _marker = new google.maps.Marker({
30244             position: position,
30245             map: _map,
30246             title: this.markerTitle,
30247             draggable: this.draggable
30248         });
30249         
30250         return {
30251             map: _map,
30252             marker: _marker,
30253             circle: null,
30254             location: position,
30255             radius: this.radius,
30256             locationName: this.locationName,
30257             addressComponents: {
30258                 formatted_address: null,
30259                 addressLine1: null,
30260                 addressLine2: null,
30261                 streetName: null,
30262                 streetNumber: null,
30263                 city: null,
30264                 district: null,
30265                 state: null,
30266                 stateOrProvince: null
30267             },
30268             settings: this,
30269             domContainer: this.el.dom,
30270             geodecoder: new google.maps.Geocoder()
30271         };
30272     },
30273     
30274     drawCircle: function(center, radius, options) 
30275     {
30276         if (this.gMapContext.circle != null) {
30277             this.gMapContext.circle.setMap(null);
30278         }
30279         if (radius > 0) {
30280             radius *= 1;
30281             options = Roo.apply({}, options, {
30282                 strokeColor: "#0000FF",
30283                 strokeOpacity: .35,
30284                 strokeWeight: 2,
30285                 fillColor: "#0000FF",
30286                 fillOpacity: .2
30287             });
30288             
30289             options.map = this.gMapContext.map;
30290             options.radius = radius;
30291             options.center = center;
30292             this.gMapContext.circle = new google.maps.Circle(options);
30293             return this.gMapContext.circle;
30294         }
30295         
30296         return null;
30297     },
30298     
30299     setPosition: function(location) 
30300     {
30301         this.gMapContext.location = location;
30302         this.gMapContext.marker.setPosition(location);
30303         this.gMapContext.map.panTo(location);
30304         this.drawCircle(location, this.gMapContext.radius, {});
30305         
30306         var _this = this;
30307         
30308         if (this.gMapContext.settings.enableReverseGeocode) {
30309             this.gMapContext.geodecoder.geocode({
30310                 latLng: this.gMapContext.location
30311             }, function(results, status) {
30312                 
30313                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30314                     _this.gMapContext.locationName = results[0].formatted_address;
30315                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30316                     
30317                     _this.fireEvent('positionchanged', this, location);
30318                 }
30319             });
30320             
30321             return;
30322         }
30323         
30324         this.fireEvent('positionchanged', this, location);
30325     },
30326     
30327     resize: function()
30328     {
30329         google.maps.event.trigger(this.gMapContext.map, "resize");
30330         
30331         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30332         
30333         this.fireEvent('resize', this);
30334     },
30335     
30336     setPositionByLatLng: function(latitude, longitude)
30337     {
30338         this.setPosition(new google.maps.LatLng(latitude, longitude));
30339     },
30340     
30341     getCurrentPosition: function() 
30342     {
30343         return {
30344             latitude: this.gMapContext.location.lat(),
30345             longitude: this.gMapContext.location.lng()
30346         };
30347     },
30348     
30349     getAddressName: function() 
30350     {
30351         return this.gMapContext.locationName;
30352     },
30353     
30354     getAddressComponents: function() 
30355     {
30356         return this.gMapContext.addressComponents;
30357     },
30358     
30359     address_component_from_google_geocode: function(address_components) 
30360     {
30361         var result = {};
30362         
30363         for (var i = 0; i < address_components.length; i++) {
30364             var component = address_components[i];
30365             if (component.types.indexOf("postal_code") >= 0) {
30366                 result.postalCode = component.short_name;
30367             } else if (component.types.indexOf("street_number") >= 0) {
30368                 result.streetNumber = component.short_name;
30369             } else if (component.types.indexOf("route") >= 0) {
30370                 result.streetName = component.short_name;
30371             } else if (component.types.indexOf("neighborhood") >= 0) {
30372                 result.city = component.short_name;
30373             } else if (component.types.indexOf("locality") >= 0) {
30374                 result.city = component.short_name;
30375             } else if (component.types.indexOf("sublocality") >= 0) {
30376                 result.district = component.short_name;
30377             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30378                 result.stateOrProvince = component.short_name;
30379             } else if (component.types.indexOf("country") >= 0) {
30380                 result.country = component.short_name;
30381             }
30382         }
30383         
30384         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30385         result.addressLine2 = "";
30386         return result;
30387     },
30388     
30389     setZoomLevel: function(zoom)
30390     {
30391         this.gMapContext.map.setZoom(zoom);
30392     },
30393     
30394     show: function()
30395     {
30396         if(!this.el){
30397             return;
30398         }
30399         
30400         this.el.show();
30401         
30402         this.resize();
30403         
30404         this.fireEvent('show', this);
30405     },
30406     
30407     hide: function()
30408     {
30409         if(!this.el){
30410             return;
30411         }
30412         
30413         this.el.hide();
30414         
30415         this.fireEvent('hide', this);
30416     }
30417     
30418 });
30419
30420 Roo.apply(Roo.bootstrap.LocationPicker, {
30421     
30422     OverlayView : function(map, options)
30423     {
30424         options = options || {};
30425         
30426         this.setMap(map);
30427     }
30428     
30429     
30430 });/**
30431  * @class Roo.bootstrap.Alert
30432  * @extends Roo.bootstrap.Component
30433  * Bootstrap Alert class - shows an alert area box
30434  * eg
30435  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30436   Enter a valid email address
30437 </div>
30438  * @licence LGPL
30439  * @cfg {String} title The title of alert
30440  * @cfg {String} html The content of alert
30441  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30442  * @cfg {String} fa font-awesomeicon
30443  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30444  * @cfg {Boolean} close true to show a x closer
30445  * 
30446  * 
30447  * @constructor
30448  * Create a new alert
30449  * @param {Object} config The config object
30450  */
30451
30452
30453 Roo.bootstrap.Alert = function(config){
30454     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30455     
30456 };
30457
30458 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30459     
30460     title: '',
30461     html: '',
30462     weight: false,
30463     fa: false,
30464     faicon: false, // BC
30465     close : false,
30466     
30467     
30468     getAutoCreate : function()
30469     {
30470         
30471         var cfg = {
30472             tag : 'div',
30473             cls : 'alert',
30474             cn : [
30475                 {
30476                     tag: 'button',
30477                     type :  "button",
30478                     cls: "close",
30479                     html : '×',
30480                     style : this.close ? '' : 'display:none'
30481                 },
30482                 {
30483                     tag : 'i',
30484                     cls : 'roo-alert-icon'
30485                     
30486                 },
30487                 {
30488                     tag : 'b',
30489                     cls : 'roo-alert-title',
30490                     html : this.title
30491                 },
30492                 {
30493                     tag : 'span',
30494                     cls : 'roo-alert-text',
30495                     html : this.html
30496                 }
30497             ]
30498         };
30499         
30500         if(this.faicon){
30501             cfg.cn[0].cls += ' fa ' + this.faicon;
30502         }
30503         if(this.fa){
30504             cfg.cn[0].cls += ' fa ' + this.fa;
30505         }
30506         
30507         if(this.weight){
30508             cfg.cls += ' alert-' + this.weight;
30509         }
30510         
30511         return cfg;
30512     },
30513     
30514     initEvents: function() 
30515     {
30516         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30517         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30518         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30519         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30520         if (this.seconds > 0) {
30521             this.hide.defer(this.seconds, this);
30522         }
30523     },
30524     /**
30525      * Set the Title Message HTML
30526      * @param {String} html
30527      */
30528     setTitle : function(str)
30529     {
30530         this.titleEl.dom.innerHTML = str;
30531     },
30532      
30533      /**
30534      * Set the Body Message HTML
30535      * @param {String} html
30536      */
30537     setHtml : function(str)
30538     {
30539         this.htmlEl.dom.innerHTML = str;
30540     },
30541     /**
30542      * Set the Weight of the alert
30543      * @param {String} (success|info|warning|danger) weight
30544      */
30545     
30546     setWeight : function(weight)
30547     {
30548         if(this.weight){
30549             this.el.removeClass('alert-' + this.weight);
30550         }
30551         
30552         this.weight = weight;
30553         
30554         this.el.addClass('alert-' + this.weight);
30555     },
30556       /**
30557      * Set the Icon of the alert
30558      * @param {String} see fontawsome names (name without the 'fa-' bit)
30559      */
30560     setIcon : function(icon)
30561     {
30562         if(this.faicon){
30563             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30564         }
30565         
30566         this.faicon = icon;
30567         
30568         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30569     },
30570     /**
30571      * Hide the Alert
30572      */
30573     hide: function() 
30574     {
30575         this.el.hide();   
30576     },
30577     /**
30578      * Show the Alert
30579      */
30580     show: function() 
30581     {  
30582         this.el.show();   
30583     }
30584     
30585 });
30586
30587  
30588 /*
30589 * Licence: LGPL
30590 */
30591
30592 /**
30593  * @class Roo.bootstrap.UploadCropbox
30594  * @extends Roo.bootstrap.Component
30595  * Bootstrap UploadCropbox class
30596  * @cfg {String} emptyText show when image has been loaded
30597  * @cfg {String} rotateNotify show when image too small to rotate
30598  * @cfg {Number} errorTimeout default 3000
30599  * @cfg {Number} minWidth default 300
30600  * @cfg {Number} minHeight default 300
30601  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30602  * @cfg {Boolean} isDocument (true|false) default false
30603  * @cfg {String} url action url
30604  * @cfg {String} paramName default 'imageUpload'
30605  * @cfg {String} method default POST
30606  * @cfg {Boolean} loadMask (true|false) default true
30607  * @cfg {Boolean} loadingText default 'Loading...'
30608  * 
30609  * @constructor
30610  * Create a new UploadCropbox
30611  * @param {Object} config The config object
30612  */
30613
30614 Roo.bootstrap.UploadCropbox = function(config){
30615     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30616     
30617     this.addEvents({
30618         /**
30619          * @event beforeselectfile
30620          * Fire before select file
30621          * @param {Roo.bootstrap.UploadCropbox} this
30622          */
30623         "beforeselectfile" : true,
30624         /**
30625          * @event initial
30626          * Fire after initEvent
30627          * @param {Roo.bootstrap.UploadCropbox} this
30628          */
30629         "initial" : true,
30630         /**
30631          * @event crop
30632          * Fire after initEvent
30633          * @param {Roo.bootstrap.UploadCropbox} this
30634          * @param {String} data
30635          */
30636         "crop" : true,
30637         /**
30638          * @event prepare
30639          * Fire when preparing the file data
30640          * @param {Roo.bootstrap.UploadCropbox} this
30641          * @param {Object} file
30642          */
30643         "prepare" : true,
30644         /**
30645          * @event exception
30646          * Fire when get exception
30647          * @param {Roo.bootstrap.UploadCropbox} this
30648          * @param {XMLHttpRequest} xhr
30649          */
30650         "exception" : true,
30651         /**
30652          * @event beforeloadcanvas
30653          * Fire before load the canvas
30654          * @param {Roo.bootstrap.UploadCropbox} this
30655          * @param {String} src
30656          */
30657         "beforeloadcanvas" : true,
30658         /**
30659          * @event trash
30660          * Fire when trash image
30661          * @param {Roo.bootstrap.UploadCropbox} this
30662          */
30663         "trash" : true,
30664         /**
30665          * @event download
30666          * Fire when download the image
30667          * @param {Roo.bootstrap.UploadCropbox} this
30668          */
30669         "download" : true,
30670         /**
30671          * @event footerbuttonclick
30672          * Fire when footerbuttonclick
30673          * @param {Roo.bootstrap.UploadCropbox} this
30674          * @param {String} type
30675          */
30676         "footerbuttonclick" : true,
30677         /**
30678          * @event resize
30679          * Fire when resize
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          */
30682         "resize" : true,
30683         /**
30684          * @event rotate
30685          * Fire when rotate the image
30686          * @param {Roo.bootstrap.UploadCropbox} this
30687          * @param {String} pos
30688          */
30689         "rotate" : true,
30690         /**
30691          * @event inspect
30692          * Fire when inspect the file
30693          * @param {Roo.bootstrap.UploadCropbox} this
30694          * @param {Object} file
30695          */
30696         "inspect" : true,
30697         /**
30698          * @event upload
30699          * Fire when xhr upload the file
30700          * @param {Roo.bootstrap.UploadCropbox} this
30701          * @param {Object} data
30702          */
30703         "upload" : true,
30704         /**
30705          * @event arrange
30706          * Fire when arrange the file data
30707          * @param {Roo.bootstrap.UploadCropbox} this
30708          * @param {Object} formData
30709          */
30710         "arrange" : true
30711     });
30712     
30713     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30714 };
30715
30716 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30717     
30718     emptyText : 'Click to upload image',
30719     rotateNotify : 'Image is too small to rotate',
30720     errorTimeout : 3000,
30721     scale : 0,
30722     baseScale : 1,
30723     rotate : 0,
30724     dragable : false,
30725     pinching : false,
30726     mouseX : 0,
30727     mouseY : 0,
30728     cropData : false,
30729     minWidth : 300,
30730     minHeight : 300,
30731     file : false,
30732     exif : {},
30733     baseRotate : 1,
30734     cropType : 'image/jpeg',
30735     buttons : false,
30736     canvasLoaded : false,
30737     isDocument : false,
30738     method : 'POST',
30739     paramName : 'imageUpload',
30740     loadMask : true,
30741     loadingText : 'Loading...',
30742     maskEl : false,
30743     
30744     getAutoCreate : function()
30745     {
30746         var cfg = {
30747             tag : 'div',
30748             cls : 'roo-upload-cropbox',
30749             cn : [
30750                 {
30751                     tag : 'input',
30752                     cls : 'roo-upload-cropbox-selector',
30753                     type : 'file'
30754                 },
30755                 {
30756                     tag : 'div',
30757                     cls : 'roo-upload-cropbox-body',
30758                     style : 'cursor:pointer',
30759                     cn : [
30760                         {
30761                             tag : 'div',
30762                             cls : 'roo-upload-cropbox-preview'
30763                         },
30764                         {
30765                             tag : 'div',
30766                             cls : 'roo-upload-cropbox-thumb'
30767                         },
30768                         {
30769                             tag : 'div',
30770                             cls : 'roo-upload-cropbox-empty-notify',
30771                             html : this.emptyText
30772                         },
30773                         {
30774                             tag : 'div',
30775                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30776                             html : this.rotateNotify
30777                         }
30778                     ]
30779                 },
30780                 {
30781                     tag : 'div',
30782                     cls : 'roo-upload-cropbox-footer',
30783                     cn : {
30784                         tag : 'div',
30785                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30786                         cn : []
30787                     }
30788                 }
30789             ]
30790         };
30791         
30792         return cfg;
30793     },
30794     
30795     onRender : function(ct, position)
30796     {
30797         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30798         
30799         if (this.buttons.length) {
30800             
30801             Roo.each(this.buttons, function(bb) {
30802                 
30803                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30804                 
30805                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30806                 
30807             }, this);
30808         }
30809         
30810         if(this.loadMask){
30811             this.maskEl = this.el;
30812         }
30813     },
30814     
30815     initEvents : function()
30816     {
30817         this.urlAPI = (window.createObjectURL && window) || 
30818                                 (window.URL && URL.revokeObjectURL && URL) || 
30819                                 (window.webkitURL && webkitURL);
30820                         
30821         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30822         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823         
30824         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30825         this.selectorEl.hide();
30826         
30827         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30828         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30829         
30830         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30831         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30832         this.thumbEl.hide();
30833         
30834         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30835         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30836         
30837         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30838         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30839         this.errorEl.hide();
30840         
30841         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30842         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30843         this.footerEl.hide();
30844         
30845         this.setThumbBoxSize();
30846         
30847         this.bind();
30848         
30849         this.resize();
30850         
30851         this.fireEvent('initial', this);
30852     },
30853
30854     bind : function()
30855     {
30856         var _this = this;
30857         
30858         window.addEventListener("resize", function() { _this.resize(); } );
30859         
30860         this.bodyEl.on('click', this.beforeSelectFile, this);
30861         
30862         if(Roo.isTouch){
30863             this.bodyEl.on('touchstart', this.onTouchStart, this);
30864             this.bodyEl.on('touchmove', this.onTouchMove, this);
30865             this.bodyEl.on('touchend', this.onTouchEnd, this);
30866         }
30867         
30868         if(!Roo.isTouch){
30869             this.bodyEl.on('mousedown', this.onMouseDown, this);
30870             this.bodyEl.on('mousemove', this.onMouseMove, this);
30871             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30872             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30873             Roo.get(document).on('mouseup', this.onMouseUp, this);
30874         }
30875         
30876         this.selectorEl.on('change', this.onFileSelected, this);
30877     },
30878     
30879     reset : function()
30880     {    
30881         this.scale = 0;
30882         this.baseScale = 1;
30883         this.rotate = 0;
30884         this.baseRotate = 1;
30885         this.dragable = false;
30886         this.pinching = false;
30887         this.mouseX = 0;
30888         this.mouseY = 0;
30889         this.cropData = false;
30890         this.notifyEl.dom.innerHTML = this.emptyText;
30891         
30892         this.selectorEl.dom.value = '';
30893         
30894     },
30895     
30896     resize : function()
30897     {
30898         if(this.fireEvent('resize', this) != false){
30899             this.setThumbBoxPosition();
30900             this.setCanvasPosition();
30901         }
30902     },
30903     
30904     onFooterButtonClick : function(e, el, o, type)
30905     {
30906         switch (type) {
30907             case 'rotate-left' :
30908                 this.onRotateLeft(e);
30909                 break;
30910             case 'rotate-right' :
30911                 this.onRotateRight(e);
30912                 break;
30913             case 'picture' :
30914                 this.beforeSelectFile(e);
30915                 break;
30916             case 'trash' :
30917                 this.trash(e);
30918                 break;
30919             case 'crop' :
30920                 this.crop(e);
30921                 break;
30922             case 'download' :
30923                 this.download(e);
30924                 break;
30925             default :
30926                 break;
30927         }
30928         
30929         this.fireEvent('footerbuttonclick', this, type);
30930     },
30931     
30932     beforeSelectFile : function(e)
30933     {
30934         e.preventDefault();
30935         
30936         if(this.fireEvent('beforeselectfile', this) != false){
30937             this.selectorEl.dom.click();
30938         }
30939     },
30940     
30941     onFileSelected : function(e)
30942     {
30943         e.preventDefault();
30944         
30945         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30946             return;
30947         }
30948         
30949         var file = this.selectorEl.dom.files[0];
30950         
30951         if(this.fireEvent('inspect', this, file) != false){
30952             this.prepare(file);
30953         }
30954         
30955     },
30956     
30957     trash : function(e)
30958     {
30959         this.fireEvent('trash', this);
30960     },
30961     
30962     download : function(e)
30963     {
30964         this.fireEvent('download', this);
30965     },
30966     
30967     loadCanvas : function(src)
30968     {   
30969         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30970             
30971             this.reset();
30972             
30973             this.imageEl = document.createElement('img');
30974             
30975             var _this = this;
30976             
30977             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30978             
30979             this.imageEl.src = src;
30980         }
30981     },
30982     
30983     onLoadCanvas : function()
30984     {   
30985         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30986         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30987         
30988         this.bodyEl.un('click', this.beforeSelectFile, this);
30989         
30990         this.notifyEl.hide();
30991         this.thumbEl.show();
30992         this.footerEl.show();
30993         
30994         this.baseRotateLevel();
30995         
30996         if(this.isDocument){
30997             this.setThumbBoxSize();
30998         }
30999         
31000         this.setThumbBoxPosition();
31001         
31002         this.baseScaleLevel();
31003         
31004         this.draw();
31005         
31006         this.resize();
31007         
31008         this.canvasLoaded = true;
31009         
31010         if(this.loadMask){
31011             this.maskEl.unmask();
31012         }
31013         
31014     },
31015     
31016     setCanvasPosition : function()
31017     {   
31018         if(!this.canvasEl){
31019             return;
31020         }
31021         
31022         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31023         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31024         
31025         this.previewEl.setLeft(pw);
31026         this.previewEl.setTop(ph);
31027         
31028     },
31029     
31030     onMouseDown : function(e)
31031     {   
31032         e.stopEvent();
31033         
31034         this.dragable = true;
31035         this.pinching = false;
31036         
31037         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31038             this.dragable = false;
31039             return;
31040         }
31041         
31042         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31043         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31044         
31045     },
31046     
31047     onMouseMove : function(e)
31048     {   
31049         e.stopEvent();
31050         
31051         if(!this.canvasLoaded){
31052             return;
31053         }
31054         
31055         if (!this.dragable){
31056             return;
31057         }
31058         
31059         var minX = Math.ceil(this.thumbEl.getLeft(true));
31060         var minY = Math.ceil(this.thumbEl.getTop(true));
31061         
31062         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31063         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31064         
31065         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31066         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31067         
31068         x = x - this.mouseX;
31069         y = y - this.mouseY;
31070         
31071         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31072         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31073         
31074         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31075         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31076         
31077         this.previewEl.setLeft(bgX);
31078         this.previewEl.setTop(bgY);
31079         
31080         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31081         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31082     },
31083     
31084     onMouseUp : function(e)
31085     {   
31086         e.stopEvent();
31087         
31088         this.dragable = false;
31089     },
31090     
31091     onMouseWheel : function(e)
31092     {   
31093         e.stopEvent();
31094         
31095         this.startScale = this.scale;
31096         
31097         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31098         
31099         if(!this.zoomable()){
31100             this.scale = this.startScale;
31101             return;
31102         }
31103         
31104         this.draw();
31105         
31106         return;
31107     },
31108     
31109     zoomable : function()
31110     {
31111         var minScale = this.thumbEl.getWidth() / this.minWidth;
31112         
31113         if(this.minWidth < this.minHeight){
31114             minScale = this.thumbEl.getHeight() / this.minHeight;
31115         }
31116         
31117         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31118         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31119         
31120         if(
31121                 this.isDocument &&
31122                 (this.rotate == 0 || this.rotate == 180) && 
31123                 (
31124                     width > this.imageEl.OriginWidth || 
31125                     height > this.imageEl.OriginHeight ||
31126                     (width < this.minWidth && height < this.minHeight)
31127                 )
31128         ){
31129             return false;
31130         }
31131         
31132         if(
31133                 this.isDocument &&
31134                 (this.rotate == 90 || this.rotate == 270) && 
31135                 (
31136                     width > this.imageEl.OriginWidth || 
31137                     height > this.imageEl.OriginHeight ||
31138                     (width < this.minHeight && height < this.minWidth)
31139                 )
31140         ){
31141             return false;
31142         }
31143         
31144         if(
31145                 !this.isDocument &&
31146                 (this.rotate == 0 || this.rotate == 180) && 
31147                 (
31148                     width < this.minWidth || 
31149                     width > this.imageEl.OriginWidth || 
31150                     height < this.minHeight || 
31151                     height > this.imageEl.OriginHeight
31152                 )
31153         ){
31154             return false;
31155         }
31156         
31157         if(
31158                 !this.isDocument &&
31159                 (this.rotate == 90 || this.rotate == 270) && 
31160                 (
31161                     width < this.minHeight || 
31162                     width > this.imageEl.OriginWidth || 
31163                     height < this.minWidth || 
31164                     height > this.imageEl.OriginHeight
31165                 )
31166         ){
31167             return false;
31168         }
31169         
31170         return true;
31171         
31172     },
31173     
31174     onRotateLeft : function(e)
31175     {   
31176         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31177             
31178             var minScale = this.thumbEl.getWidth() / this.minWidth;
31179             
31180             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31181             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31182             
31183             this.startScale = this.scale;
31184             
31185             while (this.getScaleLevel() < minScale){
31186             
31187                 this.scale = this.scale + 1;
31188                 
31189                 if(!this.zoomable()){
31190                     break;
31191                 }
31192                 
31193                 if(
31194                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31195                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31196                 ){
31197                     continue;
31198                 }
31199                 
31200                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31201
31202                 this.draw();
31203                 
31204                 return;
31205             }
31206             
31207             this.scale = this.startScale;
31208             
31209             this.onRotateFail();
31210             
31211             return false;
31212         }
31213         
31214         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31215
31216         if(this.isDocument){
31217             this.setThumbBoxSize();
31218             this.setThumbBoxPosition();
31219             this.setCanvasPosition();
31220         }
31221         
31222         this.draw();
31223         
31224         this.fireEvent('rotate', this, 'left');
31225         
31226     },
31227     
31228     onRotateRight : function(e)
31229     {
31230         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31231             
31232             var minScale = this.thumbEl.getWidth() / this.minWidth;
31233         
31234             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31235             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31236             
31237             this.startScale = this.scale;
31238             
31239             while (this.getScaleLevel() < minScale){
31240             
31241                 this.scale = this.scale + 1;
31242                 
31243                 if(!this.zoomable()){
31244                     break;
31245                 }
31246                 
31247                 if(
31248                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31249                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31250                 ){
31251                     continue;
31252                 }
31253                 
31254                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31255
31256                 this.draw();
31257                 
31258                 return;
31259             }
31260             
31261             this.scale = this.startScale;
31262             
31263             this.onRotateFail();
31264             
31265             return false;
31266         }
31267         
31268         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31269
31270         if(this.isDocument){
31271             this.setThumbBoxSize();
31272             this.setThumbBoxPosition();
31273             this.setCanvasPosition();
31274         }
31275         
31276         this.draw();
31277         
31278         this.fireEvent('rotate', this, 'right');
31279     },
31280     
31281     onRotateFail : function()
31282     {
31283         this.errorEl.show(true);
31284         
31285         var _this = this;
31286         
31287         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31288     },
31289     
31290     draw : function()
31291     {
31292         this.previewEl.dom.innerHTML = '';
31293         
31294         var canvasEl = document.createElement("canvas");
31295         
31296         var contextEl = canvasEl.getContext("2d");
31297         
31298         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31299         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31300         var center = this.imageEl.OriginWidth / 2;
31301         
31302         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31303             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31304             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31305             center = this.imageEl.OriginHeight / 2;
31306         }
31307         
31308         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31309         
31310         contextEl.translate(center, center);
31311         contextEl.rotate(this.rotate * Math.PI / 180);
31312
31313         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31314         
31315         this.canvasEl = document.createElement("canvas");
31316         
31317         this.contextEl = this.canvasEl.getContext("2d");
31318         
31319         switch (this.rotate) {
31320             case 0 :
31321                 
31322                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31323                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31324                 
31325                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326                 
31327                 break;
31328             case 90 : 
31329                 
31330                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31331                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31332                 
31333                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31334                     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);
31335                     break;
31336                 }
31337                 
31338                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31339                 
31340                 break;
31341             case 180 :
31342                 
31343                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31344                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31345                 
31346                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31347                     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);
31348                     break;
31349                 }
31350                 
31351                 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);
31352                 
31353                 break;
31354             case 270 :
31355                 
31356                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31357                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31358         
31359                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31360                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31361                     break;
31362                 }
31363                 
31364                 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);
31365                 
31366                 break;
31367             default : 
31368                 break;
31369         }
31370         
31371         this.previewEl.appendChild(this.canvasEl);
31372         
31373         this.setCanvasPosition();
31374     },
31375     
31376     crop : function()
31377     {
31378         if(!this.canvasLoaded){
31379             return;
31380         }
31381         
31382         var imageCanvas = document.createElement("canvas");
31383         
31384         var imageContext = imageCanvas.getContext("2d");
31385         
31386         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31387         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31388         
31389         var center = imageCanvas.width / 2;
31390         
31391         imageContext.translate(center, center);
31392         
31393         imageContext.rotate(this.rotate * Math.PI / 180);
31394         
31395         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31396         
31397         var canvas = document.createElement("canvas");
31398         
31399         var context = canvas.getContext("2d");
31400                 
31401         canvas.width = this.minWidth;
31402         canvas.height = this.minHeight;
31403
31404         switch (this.rotate) {
31405             case 0 :
31406                 
31407                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31408                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31409                 
31410                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31411                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31412                 
31413                 var targetWidth = this.minWidth - 2 * x;
31414                 var targetHeight = this.minHeight - 2 * y;
31415                 
31416                 var scale = 1;
31417                 
31418                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31419                     scale = targetWidth / width;
31420                 }
31421                 
31422                 if(x > 0 && y == 0){
31423                     scale = targetHeight / height;
31424                 }
31425                 
31426                 if(x > 0 && y > 0){
31427                     scale = targetWidth / width;
31428                     
31429                     if(width < height){
31430                         scale = targetHeight / height;
31431                     }
31432                 }
31433                 
31434                 context.scale(scale, scale);
31435                 
31436                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31437                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31438
31439                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31440                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31441
31442                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31443                 
31444                 break;
31445             case 90 : 
31446                 
31447                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31448                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31449                 
31450                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31451                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31452                 
31453                 var targetWidth = this.minWidth - 2 * x;
31454                 var targetHeight = this.minHeight - 2 * y;
31455                 
31456                 var scale = 1;
31457                 
31458                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31459                     scale = targetWidth / width;
31460                 }
31461                 
31462                 if(x > 0 && y == 0){
31463                     scale = targetHeight / height;
31464                 }
31465                 
31466                 if(x > 0 && y > 0){
31467                     scale = targetWidth / width;
31468                     
31469                     if(width < height){
31470                         scale = targetHeight / height;
31471                     }
31472                 }
31473                 
31474                 context.scale(scale, scale);
31475                 
31476                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31477                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31478
31479                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31480                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31481                 
31482                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31483                 
31484                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31485                 
31486                 break;
31487             case 180 :
31488                 
31489                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31490                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31491                 
31492                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31493                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31494                 
31495                 var targetWidth = this.minWidth - 2 * x;
31496                 var targetHeight = this.minHeight - 2 * y;
31497                 
31498                 var scale = 1;
31499                 
31500                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31501                     scale = targetWidth / width;
31502                 }
31503                 
31504                 if(x > 0 && y == 0){
31505                     scale = targetHeight / height;
31506                 }
31507                 
31508                 if(x > 0 && y > 0){
31509                     scale = targetWidth / width;
31510                     
31511                     if(width < height){
31512                         scale = targetHeight / height;
31513                     }
31514                 }
31515                 
31516                 context.scale(scale, scale);
31517                 
31518                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31519                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31520
31521                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31522                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31523
31524                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31525                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31526                 
31527                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31528                 
31529                 break;
31530             case 270 :
31531                 
31532                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31533                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31534                 
31535                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31536                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31537                 
31538                 var targetWidth = this.minWidth - 2 * x;
31539                 var targetHeight = this.minHeight - 2 * y;
31540                 
31541                 var scale = 1;
31542                 
31543                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31544                     scale = targetWidth / width;
31545                 }
31546                 
31547                 if(x > 0 && y == 0){
31548                     scale = targetHeight / height;
31549                 }
31550                 
31551                 if(x > 0 && y > 0){
31552                     scale = targetWidth / width;
31553                     
31554                     if(width < height){
31555                         scale = targetHeight / height;
31556                     }
31557                 }
31558                 
31559                 context.scale(scale, scale);
31560                 
31561                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31562                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31563
31564                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31565                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31566                 
31567                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31568                 
31569                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31570                 
31571                 break;
31572             default : 
31573                 break;
31574         }
31575         
31576         this.cropData = canvas.toDataURL(this.cropType);
31577         
31578         if(this.fireEvent('crop', this, this.cropData) !== false){
31579             this.process(this.file, this.cropData);
31580         }
31581         
31582         return;
31583         
31584     },
31585     
31586     setThumbBoxSize : function()
31587     {
31588         var width, height;
31589         
31590         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31591             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31592             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31593             
31594             this.minWidth = width;
31595             this.minHeight = height;
31596             
31597             if(this.rotate == 90 || this.rotate == 270){
31598                 this.minWidth = height;
31599                 this.minHeight = width;
31600             }
31601         }
31602         
31603         height = 300;
31604         width = Math.ceil(this.minWidth * height / this.minHeight);
31605         
31606         if(this.minWidth > this.minHeight){
31607             width = 300;
31608             height = Math.ceil(this.minHeight * width / this.minWidth);
31609         }
31610         
31611         this.thumbEl.setStyle({
31612             width : width + 'px',
31613             height : height + 'px'
31614         });
31615
31616         return;
31617             
31618     },
31619     
31620     setThumbBoxPosition : function()
31621     {
31622         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31623         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31624         
31625         this.thumbEl.setLeft(x);
31626         this.thumbEl.setTop(y);
31627         
31628     },
31629     
31630     baseRotateLevel : function()
31631     {
31632         this.baseRotate = 1;
31633         
31634         if(
31635                 typeof(this.exif) != 'undefined' &&
31636                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31637                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31638         ){
31639             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31640         }
31641         
31642         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31643         
31644     },
31645     
31646     baseScaleLevel : function()
31647     {
31648         var width, height;
31649         
31650         if(this.isDocument){
31651             
31652             if(this.baseRotate == 6 || this.baseRotate == 8){
31653             
31654                 height = this.thumbEl.getHeight();
31655                 this.baseScale = height / this.imageEl.OriginWidth;
31656
31657                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31658                     width = this.thumbEl.getWidth();
31659                     this.baseScale = width / this.imageEl.OriginHeight;
31660                 }
31661
31662                 return;
31663             }
31664
31665             height = this.thumbEl.getHeight();
31666             this.baseScale = height / this.imageEl.OriginHeight;
31667
31668             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31669                 width = this.thumbEl.getWidth();
31670                 this.baseScale = width / this.imageEl.OriginWidth;
31671             }
31672
31673             return;
31674         }
31675         
31676         if(this.baseRotate == 6 || this.baseRotate == 8){
31677             
31678             width = this.thumbEl.getHeight();
31679             this.baseScale = width / this.imageEl.OriginHeight;
31680             
31681             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31682                 height = this.thumbEl.getWidth();
31683                 this.baseScale = height / this.imageEl.OriginHeight;
31684             }
31685             
31686             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31687                 height = this.thumbEl.getWidth();
31688                 this.baseScale = height / this.imageEl.OriginHeight;
31689                 
31690                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31691                     width = this.thumbEl.getHeight();
31692                     this.baseScale = width / this.imageEl.OriginWidth;
31693                 }
31694             }
31695             
31696             return;
31697         }
31698         
31699         width = this.thumbEl.getWidth();
31700         this.baseScale = width / this.imageEl.OriginWidth;
31701         
31702         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31703             height = this.thumbEl.getHeight();
31704             this.baseScale = height / this.imageEl.OriginHeight;
31705         }
31706         
31707         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31708             
31709             height = this.thumbEl.getHeight();
31710             this.baseScale = height / this.imageEl.OriginHeight;
31711             
31712             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31713                 width = this.thumbEl.getWidth();
31714                 this.baseScale = width / this.imageEl.OriginWidth;
31715             }
31716             
31717         }
31718         
31719         return;
31720     },
31721     
31722     getScaleLevel : function()
31723     {
31724         return this.baseScale * Math.pow(1.1, this.scale);
31725     },
31726     
31727     onTouchStart : function(e)
31728     {
31729         if(!this.canvasLoaded){
31730             this.beforeSelectFile(e);
31731             return;
31732         }
31733         
31734         var touches = e.browserEvent.touches;
31735         
31736         if(!touches){
31737             return;
31738         }
31739         
31740         if(touches.length == 1){
31741             this.onMouseDown(e);
31742             return;
31743         }
31744         
31745         if(touches.length != 2){
31746             return;
31747         }
31748         
31749         var coords = [];
31750         
31751         for(var i = 0, finger; finger = touches[i]; i++){
31752             coords.push(finger.pageX, finger.pageY);
31753         }
31754         
31755         var x = Math.pow(coords[0] - coords[2], 2);
31756         var y = Math.pow(coords[1] - coords[3], 2);
31757         
31758         this.startDistance = Math.sqrt(x + y);
31759         
31760         this.startScale = this.scale;
31761         
31762         this.pinching = true;
31763         this.dragable = false;
31764         
31765     },
31766     
31767     onTouchMove : function(e)
31768     {
31769         if(!this.pinching && !this.dragable){
31770             return;
31771         }
31772         
31773         var touches = e.browserEvent.touches;
31774         
31775         if(!touches){
31776             return;
31777         }
31778         
31779         if(this.dragable){
31780             this.onMouseMove(e);
31781             return;
31782         }
31783         
31784         var coords = [];
31785         
31786         for(var i = 0, finger; finger = touches[i]; i++){
31787             coords.push(finger.pageX, finger.pageY);
31788         }
31789         
31790         var x = Math.pow(coords[0] - coords[2], 2);
31791         var y = Math.pow(coords[1] - coords[3], 2);
31792         
31793         this.endDistance = Math.sqrt(x + y);
31794         
31795         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31796         
31797         if(!this.zoomable()){
31798             this.scale = this.startScale;
31799             return;
31800         }
31801         
31802         this.draw();
31803         
31804     },
31805     
31806     onTouchEnd : function(e)
31807     {
31808         this.pinching = false;
31809         this.dragable = false;
31810         
31811     },
31812     
31813     process : function(file, crop)
31814     {
31815         if(this.loadMask){
31816             this.maskEl.mask(this.loadingText);
31817         }
31818         
31819         this.xhr = new XMLHttpRequest();
31820         
31821         file.xhr = this.xhr;
31822
31823         this.xhr.open(this.method, this.url, true);
31824         
31825         var headers = {
31826             "Accept": "application/json",
31827             "Cache-Control": "no-cache",
31828             "X-Requested-With": "XMLHttpRequest"
31829         };
31830         
31831         for (var headerName in headers) {
31832             var headerValue = headers[headerName];
31833             if (headerValue) {
31834                 this.xhr.setRequestHeader(headerName, headerValue);
31835             }
31836         }
31837         
31838         var _this = this;
31839         
31840         this.xhr.onload = function()
31841         {
31842             _this.xhrOnLoad(_this.xhr);
31843         }
31844         
31845         this.xhr.onerror = function()
31846         {
31847             _this.xhrOnError(_this.xhr);
31848         }
31849         
31850         var formData = new FormData();
31851
31852         formData.append('returnHTML', 'NO');
31853         
31854         if(crop){
31855             formData.append('crop', crop);
31856         }
31857         
31858         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31859             formData.append(this.paramName, file, file.name);
31860         }
31861         
31862         if(typeof(file.filename) != 'undefined'){
31863             formData.append('filename', file.filename);
31864         }
31865         
31866         if(typeof(file.mimetype) != 'undefined'){
31867             formData.append('mimetype', file.mimetype);
31868         }
31869         
31870         if(this.fireEvent('arrange', this, formData) != false){
31871             this.xhr.send(formData);
31872         };
31873     },
31874     
31875     xhrOnLoad : function(xhr)
31876     {
31877         if(this.loadMask){
31878             this.maskEl.unmask();
31879         }
31880         
31881         if (xhr.readyState !== 4) {
31882             this.fireEvent('exception', this, xhr);
31883             return;
31884         }
31885
31886         var response = Roo.decode(xhr.responseText);
31887         
31888         if(!response.success){
31889             this.fireEvent('exception', this, xhr);
31890             return;
31891         }
31892         
31893         var response = Roo.decode(xhr.responseText);
31894         
31895         this.fireEvent('upload', this, response);
31896         
31897     },
31898     
31899     xhrOnError : function()
31900     {
31901         if(this.loadMask){
31902             this.maskEl.unmask();
31903         }
31904         
31905         Roo.log('xhr on error');
31906         
31907         var response = Roo.decode(xhr.responseText);
31908           
31909         Roo.log(response);
31910         
31911     },
31912     
31913     prepare : function(file)
31914     {   
31915         if(this.loadMask){
31916             this.maskEl.mask(this.loadingText);
31917         }
31918         
31919         this.file = false;
31920         this.exif = {};
31921         
31922         if(typeof(file) === 'string'){
31923             this.loadCanvas(file);
31924             return;
31925         }
31926         
31927         if(!file || !this.urlAPI){
31928             return;
31929         }
31930         
31931         this.file = file;
31932         this.cropType = file.type;
31933         
31934         var _this = this;
31935         
31936         if(this.fireEvent('prepare', this, this.file) != false){
31937             
31938             var reader = new FileReader();
31939             
31940             reader.onload = function (e) {
31941                 if (e.target.error) {
31942                     Roo.log(e.target.error);
31943                     return;
31944                 }
31945                 
31946                 var buffer = e.target.result,
31947                     dataView = new DataView(buffer),
31948                     offset = 2,
31949                     maxOffset = dataView.byteLength - 4,
31950                     markerBytes,
31951                     markerLength;
31952                 
31953                 if (dataView.getUint16(0) === 0xffd8) {
31954                     while (offset < maxOffset) {
31955                         markerBytes = dataView.getUint16(offset);
31956                         
31957                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31958                             markerLength = dataView.getUint16(offset + 2) + 2;
31959                             if (offset + markerLength > dataView.byteLength) {
31960                                 Roo.log('Invalid meta data: Invalid segment size.');
31961                                 break;
31962                             }
31963                             
31964                             if(markerBytes == 0xffe1){
31965                                 _this.parseExifData(
31966                                     dataView,
31967                                     offset,
31968                                     markerLength
31969                                 );
31970                             }
31971                             
31972                             offset += markerLength;
31973                             
31974                             continue;
31975                         }
31976                         
31977                         break;
31978                     }
31979                     
31980                 }
31981                 
31982                 var url = _this.urlAPI.createObjectURL(_this.file);
31983                 
31984                 _this.loadCanvas(url);
31985                 
31986                 return;
31987             }
31988             
31989             reader.readAsArrayBuffer(this.file);
31990             
31991         }
31992         
31993     },
31994     
31995     parseExifData : function(dataView, offset, length)
31996     {
31997         var tiffOffset = offset + 10,
31998             littleEndian,
31999             dirOffset;
32000     
32001         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32002             // No Exif data, might be XMP data instead
32003             return;
32004         }
32005         
32006         // Check for the ASCII code for "Exif" (0x45786966):
32007         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32008             // No Exif data, might be XMP data instead
32009             return;
32010         }
32011         if (tiffOffset + 8 > dataView.byteLength) {
32012             Roo.log('Invalid Exif data: Invalid segment size.');
32013             return;
32014         }
32015         // Check for the two null bytes:
32016         if (dataView.getUint16(offset + 8) !== 0x0000) {
32017             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32018             return;
32019         }
32020         // Check the byte alignment:
32021         switch (dataView.getUint16(tiffOffset)) {
32022         case 0x4949:
32023             littleEndian = true;
32024             break;
32025         case 0x4D4D:
32026             littleEndian = false;
32027             break;
32028         default:
32029             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32030             return;
32031         }
32032         // Check for the TIFF tag marker (0x002A):
32033         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32034             Roo.log('Invalid Exif data: Missing TIFF marker.');
32035             return;
32036         }
32037         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32038         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32039         
32040         this.parseExifTags(
32041             dataView,
32042             tiffOffset,
32043             tiffOffset + dirOffset,
32044             littleEndian
32045         );
32046     },
32047     
32048     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32049     {
32050         var tagsNumber,
32051             dirEndOffset,
32052             i;
32053         if (dirOffset + 6 > dataView.byteLength) {
32054             Roo.log('Invalid Exif data: Invalid directory offset.');
32055             return;
32056         }
32057         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32058         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32059         if (dirEndOffset + 4 > dataView.byteLength) {
32060             Roo.log('Invalid Exif data: Invalid directory size.');
32061             return;
32062         }
32063         for (i = 0; i < tagsNumber; i += 1) {
32064             this.parseExifTag(
32065                 dataView,
32066                 tiffOffset,
32067                 dirOffset + 2 + 12 * i, // tag offset
32068                 littleEndian
32069             );
32070         }
32071         // Return the offset to the next directory:
32072         return dataView.getUint32(dirEndOffset, littleEndian);
32073     },
32074     
32075     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32076     {
32077         var tag = dataView.getUint16(offset, littleEndian);
32078         
32079         this.exif[tag] = this.getExifValue(
32080             dataView,
32081             tiffOffset,
32082             offset,
32083             dataView.getUint16(offset + 2, littleEndian), // tag type
32084             dataView.getUint32(offset + 4, littleEndian), // tag length
32085             littleEndian
32086         );
32087     },
32088     
32089     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32090     {
32091         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32092             tagSize,
32093             dataOffset,
32094             values,
32095             i,
32096             str,
32097             c;
32098     
32099         if (!tagType) {
32100             Roo.log('Invalid Exif data: Invalid tag type.');
32101             return;
32102         }
32103         
32104         tagSize = tagType.size * length;
32105         // Determine if the value is contained in the dataOffset bytes,
32106         // or if the value at the dataOffset is a pointer to the actual data:
32107         dataOffset = tagSize > 4 ?
32108                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32109         if (dataOffset + tagSize > dataView.byteLength) {
32110             Roo.log('Invalid Exif data: Invalid data offset.');
32111             return;
32112         }
32113         if (length === 1) {
32114             return tagType.getValue(dataView, dataOffset, littleEndian);
32115         }
32116         values = [];
32117         for (i = 0; i < length; i += 1) {
32118             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32119         }
32120         
32121         if (tagType.ascii) {
32122             str = '';
32123             // Concatenate the chars:
32124             for (i = 0; i < values.length; i += 1) {
32125                 c = values[i];
32126                 // Ignore the terminating NULL byte(s):
32127                 if (c === '\u0000') {
32128                     break;
32129                 }
32130                 str += c;
32131             }
32132             return str;
32133         }
32134         return values;
32135     }
32136     
32137 });
32138
32139 Roo.apply(Roo.bootstrap.UploadCropbox, {
32140     tags : {
32141         'Orientation': 0x0112
32142     },
32143     
32144     Orientation: {
32145             1: 0, //'top-left',
32146 //            2: 'top-right',
32147             3: 180, //'bottom-right',
32148 //            4: 'bottom-left',
32149 //            5: 'left-top',
32150             6: 90, //'right-top',
32151 //            7: 'right-bottom',
32152             8: 270 //'left-bottom'
32153     },
32154     
32155     exifTagTypes : {
32156         // byte, 8-bit unsigned int:
32157         1: {
32158             getValue: function (dataView, dataOffset) {
32159                 return dataView.getUint8(dataOffset);
32160             },
32161             size: 1
32162         },
32163         // ascii, 8-bit byte:
32164         2: {
32165             getValue: function (dataView, dataOffset) {
32166                 return String.fromCharCode(dataView.getUint8(dataOffset));
32167             },
32168             size: 1,
32169             ascii: true
32170         },
32171         // short, 16 bit int:
32172         3: {
32173             getValue: function (dataView, dataOffset, littleEndian) {
32174                 return dataView.getUint16(dataOffset, littleEndian);
32175             },
32176             size: 2
32177         },
32178         // long, 32 bit int:
32179         4: {
32180             getValue: function (dataView, dataOffset, littleEndian) {
32181                 return dataView.getUint32(dataOffset, littleEndian);
32182             },
32183             size: 4
32184         },
32185         // rational = two long values, first is numerator, second is denominator:
32186         5: {
32187             getValue: function (dataView, dataOffset, littleEndian) {
32188                 return dataView.getUint32(dataOffset, littleEndian) /
32189                     dataView.getUint32(dataOffset + 4, littleEndian);
32190             },
32191             size: 8
32192         },
32193         // slong, 32 bit signed int:
32194         9: {
32195             getValue: function (dataView, dataOffset, littleEndian) {
32196                 return dataView.getInt32(dataOffset, littleEndian);
32197             },
32198             size: 4
32199         },
32200         // srational, two slongs, first is numerator, second is denominator:
32201         10: {
32202             getValue: function (dataView, dataOffset, littleEndian) {
32203                 return dataView.getInt32(dataOffset, littleEndian) /
32204                     dataView.getInt32(dataOffset + 4, littleEndian);
32205             },
32206             size: 8
32207         }
32208     },
32209     
32210     footer : {
32211         STANDARD : [
32212             {
32213                 tag : 'div',
32214                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32215                 action : 'rotate-left',
32216                 cn : [
32217                     {
32218                         tag : 'button',
32219                         cls : 'btn btn-default',
32220                         html : '<i class="fa fa-undo"></i>'
32221                     }
32222                 ]
32223             },
32224             {
32225                 tag : 'div',
32226                 cls : 'btn-group roo-upload-cropbox-picture',
32227                 action : 'picture',
32228                 cn : [
32229                     {
32230                         tag : 'button',
32231                         cls : 'btn btn-default',
32232                         html : '<i class="fa fa-picture-o"></i>'
32233                     }
32234                 ]
32235             },
32236             {
32237                 tag : 'div',
32238                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32239                 action : 'rotate-right',
32240                 cn : [
32241                     {
32242                         tag : 'button',
32243                         cls : 'btn btn-default',
32244                         html : '<i class="fa fa-repeat"></i>'
32245                     }
32246                 ]
32247             }
32248         ],
32249         DOCUMENT : [
32250             {
32251                 tag : 'div',
32252                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32253                 action : 'rotate-left',
32254                 cn : [
32255                     {
32256                         tag : 'button',
32257                         cls : 'btn btn-default',
32258                         html : '<i class="fa fa-undo"></i>'
32259                     }
32260                 ]
32261             },
32262             {
32263                 tag : 'div',
32264                 cls : 'btn-group roo-upload-cropbox-download',
32265                 action : 'download',
32266                 cn : [
32267                     {
32268                         tag : 'button',
32269                         cls : 'btn btn-default',
32270                         html : '<i class="fa fa-download"></i>'
32271                     }
32272                 ]
32273             },
32274             {
32275                 tag : 'div',
32276                 cls : 'btn-group roo-upload-cropbox-crop',
32277                 action : 'crop',
32278                 cn : [
32279                     {
32280                         tag : 'button',
32281                         cls : 'btn btn-default',
32282                         html : '<i class="fa fa-crop"></i>'
32283                     }
32284                 ]
32285             },
32286             {
32287                 tag : 'div',
32288                 cls : 'btn-group roo-upload-cropbox-trash',
32289                 action : 'trash',
32290                 cn : [
32291                     {
32292                         tag : 'button',
32293                         cls : 'btn btn-default',
32294                         html : '<i class="fa fa-trash"></i>'
32295                     }
32296                 ]
32297             },
32298             {
32299                 tag : 'div',
32300                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32301                 action : 'rotate-right',
32302                 cn : [
32303                     {
32304                         tag : 'button',
32305                         cls : 'btn btn-default',
32306                         html : '<i class="fa fa-repeat"></i>'
32307                     }
32308                 ]
32309             }
32310         ],
32311         ROTATOR : [
32312             {
32313                 tag : 'div',
32314                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32315                 action : 'rotate-left',
32316                 cn : [
32317                     {
32318                         tag : 'button',
32319                         cls : 'btn btn-default',
32320                         html : '<i class="fa fa-undo"></i>'
32321                     }
32322                 ]
32323             },
32324             {
32325                 tag : 'div',
32326                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32327                 action : 'rotate-right',
32328                 cn : [
32329                     {
32330                         tag : 'button',
32331                         cls : 'btn btn-default',
32332                         html : '<i class="fa fa-repeat"></i>'
32333                     }
32334                 ]
32335             }
32336         ]
32337     }
32338 });
32339
32340 /*
32341 * Licence: LGPL
32342 */
32343
32344 /**
32345  * @class Roo.bootstrap.DocumentManager
32346  * @extends Roo.bootstrap.Component
32347  * Bootstrap DocumentManager class
32348  * @cfg {String} paramName default 'imageUpload'
32349  * @cfg {String} toolTipName default 'filename'
32350  * @cfg {String} method default POST
32351  * @cfg {String} url action url
32352  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32353  * @cfg {Boolean} multiple multiple upload default true
32354  * @cfg {Number} thumbSize default 300
32355  * @cfg {String} fieldLabel
32356  * @cfg {Number} labelWidth default 4
32357  * @cfg {String} labelAlign (left|top) default left
32358  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32359 * @cfg {Number} labellg set the width of label (1-12)
32360  * @cfg {Number} labelmd set the width of label (1-12)
32361  * @cfg {Number} labelsm set the width of label (1-12)
32362  * @cfg {Number} labelxs set the width of label (1-12)
32363  * 
32364  * @constructor
32365  * Create a new DocumentManager
32366  * @param {Object} config The config object
32367  */
32368
32369 Roo.bootstrap.DocumentManager = function(config){
32370     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32371     
32372     this.files = [];
32373     this.delegates = [];
32374     
32375     this.addEvents({
32376         /**
32377          * @event initial
32378          * Fire when initial the DocumentManager
32379          * @param {Roo.bootstrap.DocumentManager} this
32380          */
32381         "initial" : true,
32382         /**
32383          * @event inspect
32384          * inspect selected file
32385          * @param {Roo.bootstrap.DocumentManager} this
32386          * @param {File} file
32387          */
32388         "inspect" : true,
32389         /**
32390          * @event exception
32391          * Fire when xhr load exception
32392          * @param {Roo.bootstrap.DocumentManager} this
32393          * @param {XMLHttpRequest} xhr
32394          */
32395         "exception" : true,
32396         /**
32397          * @event afterupload
32398          * Fire when xhr load exception
32399          * @param {Roo.bootstrap.DocumentManager} this
32400          * @param {XMLHttpRequest} xhr
32401          */
32402         "afterupload" : true,
32403         /**
32404          * @event prepare
32405          * prepare the form data
32406          * @param {Roo.bootstrap.DocumentManager} this
32407          * @param {Object} formData
32408          */
32409         "prepare" : true,
32410         /**
32411          * @event remove
32412          * Fire when remove the file
32413          * @param {Roo.bootstrap.DocumentManager} this
32414          * @param {Object} file
32415          */
32416         "remove" : true,
32417         /**
32418          * @event refresh
32419          * Fire after refresh the file
32420          * @param {Roo.bootstrap.DocumentManager} this
32421          */
32422         "refresh" : true,
32423         /**
32424          * @event click
32425          * Fire after click the image
32426          * @param {Roo.bootstrap.DocumentManager} this
32427          * @param {Object} file
32428          */
32429         "click" : true,
32430         /**
32431          * @event edit
32432          * Fire when upload a image and editable set to true
32433          * @param {Roo.bootstrap.DocumentManager} this
32434          * @param {Object} file
32435          */
32436         "edit" : true,
32437         /**
32438          * @event beforeselectfile
32439          * Fire before select file
32440          * @param {Roo.bootstrap.DocumentManager} this
32441          */
32442         "beforeselectfile" : true,
32443         /**
32444          * @event process
32445          * Fire before process file
32446          * @param {Roo.bootstrap.DocumentManager} this
32447          * @param {Object} file
32448          */
32449         "process" : true,
32450         /**
32451          * @event previewrendered
32452          * Fire when preview rendered
32453          * @param {Roo.bootstrap.DocumentManager} this
32454          * @param {Object} file
32455          */
32456         "previewrendered" : true,
32457         /**
32458          */
32459         "previewResize" : true
32460         
32461     });
32462 };
32463
32464 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32465     
32466     boxes : 0,
32467     inputName : '',
32468     thumbSize : 300,
32469     multiple : true,
32470     files : false,
32471     method : 'POST',
32472     url : '',
32473     paramName : 'imageUpload',
32474     toolTipName : 'filename',
32475     fieldLabel : '',
32476     labelWidth : 4,
32477     labelAlign : 'left',
32478     editable : true,
32479     delegates : false,
32480     xhr : false, 
32481     
32482     labellg : 0,
32483     labelmd : 0,
32484     labelsm : 0,
32485     labelxs : 0,
32486     
32487     getAutoCreate : function()
32488     {   
32489         var managerWidget = {
32490             tag : 'div',
32491             cls : 'roo-document-manager',
32492             cn : [
32493                 {
32494                     tag : 'input',
32495                     cls : 'roo-document-manager-selector',
32496                     type : 'file'
32497                 },
32498                 {
32499                     tag : 'div',
32500                     cls : 'roo-document-manager-uploader',
32501                     cn : [
32502                         {
32503                             tag : 'div',
32504                             cls : 'roo-document-manager-upload-btn',
32505                             html : '<i class="fa fa-plus"></i>'
32506                         }
32507                     ]
32508                     
32509                 }
32510             ]
32511         };
32512         
32513         var content = [
32514             {
32515                 tag : 'div',
32516                 cls : 'column col-md-12',
32517                 cn : managerWidget
32518             }
32519         ];
32520         
32521         if(this.fieldLabel.length){
32522             
32523             content = [
32524                 {
32525                     tag : 'div',
32526                     cls : 'column col-md-12',
32527                     html : this.fieldLabel
32528                 },
32529                 {
32530                     tag : 'div',
32531                     cls : 'column col-md-12',
32532                     cn : managerWidget
32533                 }
32534             ];
32535
32536             if(this.labelAlign == 'left'){
32537                 content = [
32538                     {
32539                         tag : 'div',
32540                         cls : 'column',
32541                         html : this.fieldLabel
32542                     },
32543                     {
32544                         tag : 'div',
32545                         cls : 'column',
32546                         cn : managerWidget
32547                     }
32548                 ];
32549                 
32550                 if(this.labelWidth > 12){
32551                     content[0].style = "width: " + this.labelWidth + 'px';
32552                 }
32553
32554                 if(this.labelWidth < 13 && this.labelmd == 0){
32555                     this.labelmd = this.labelWidth;
32556                 }
32557
32558                 if(this.labellg > 0){
32559                     content[0].cls += ' col-lg-' + this.labellg;
32560                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32561                 }
32562
32563                 if(this.labelmd > 0){
32564                     content[0].cls += ' col-md-' + this.labelmd;
32565                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32566                 }
32567
32568                 if(this.labelsm > 0){
32569                     content[0].cls += ' col-sm-' + this.labelsm;
32570                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32571                 }
32572
32573                 if(this.labelxs > 0){
32574                     content[0].cls += ' col-xs-' + this.labelxs;
32575                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32576                 }
32577                 
32578             }
32579         }
32580         
32581         var cfg = {
32582             tag : 'div',
32583             cls : 'row clearfix',
32584             cn : content
32585         };
32586         
32587         return cfg;
32588         
32589     },
32590     
32591     initEvents : function()
32592     {
32593         this.managerEl = this.el.select('.roo-document-manager', true).first();
32594         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32595         
32596         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32597         this.selectorEl.hide();
32598         
32599         if(this.multiple){
32600             this.selectorEl.attr('multiple', 'multiple');
32601         }
32602         
32603         this.selectorEl.on('change', this.onFileSelected, this);
32604         
32605         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32606         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32607         
32608         this.uploader.on('click', this.onUploaderClick, this);
32609         
32610         this.renderProgressDialog();
32611         
32612         var _this = this;
32613         
32614         window.addEventListener("resize", function() { _this.refresh(); } );
32615         
32616         this.fireEvent('initial', this);
32617     },
32618     
32619     renderProgressDialog : function()
32620     {
32621         var _this = this;
32622         
32623         this.progressDialog = new Roo.bootstrap.Modal({
32624             cls : 'roo-document-manager-progress-dialog',
32625             allow_close : false,
32626             animate : false,
32627             title : '',
32628             buttons : [
32629                 {
32630                     name  :'cancel',
32631                     weight : 'danger',
32632                     html : 'Cancel'
32633                 }
32634             ], 
32635             listeners : { 
32636                 btnclick : function() {
32637                     _this.uploadCancel();
32638                     this.hide();
32639                 }
32640             }
32641         });
32642          
32643         this.progressDialog.render(Roo.get(document.body));
32644          
32645         this.progress = new Roo.bootstrap.Progress({
32646             cls : 'roo-document-manager-progress',
32647             active : true,
32648             striped : true
32649         });
32650         
32651         this.progress.render(this.progressDialog.getChildContainer());
32652         
32653         this.progressBar = new Roo.bootstrap.ProgressBar({
32654             cls : 'roo-document-manager-progress-bar',
32655             aria_valuenow : 0,
32656             aria_valuemin : 0,
32657             aria_valuemax : 12,
32658             panel : 'success'
32659         });
32660         
32661         this.progressBar.render(this.progress.getChildContainer());
32662     },
32663     
32664     onUploaderClick : function(e)
32665     {
32666         e.preventDefault();
32667      
32668         if(this.fireEvent('beforeselectfile', this) != false){
32669             this.selectorEl.dom.click();
32670         }
32671         
32672     },
32673     
32674     onFileSelected : function(e)
32675     {
32676         e.preventDefault();
32677         
32678         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32679             return;
32680         }
32681         
32682         Roo.each(this.selectorEl.dom.files, function(file){
32683             if(this.fireEvent('inspect', this, file) != false){
32684                 this.files.push(file);
32685             }
32686         }, this);
32687         
32688         this.queue();
32689         
32690     },
32691     
32692     queue : function()
32693     {
32694         this.selectorEl.dom.value = '';
32695         
32696         if(!this.files || !this.files.length){
32697             return;
32698         }
32699         
32700         if(this.boxes > 0 && this.files.length > this.boxes){
32701             this.files = this.files.slice(0, this.boxes);
32702         }
32703         
32704         this.uploader.show();
32705         
32706         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32707             this.uploader.hide();
32708         }
32709         
32710         var _this = this;
32711         
32712         var files = [];
32713         
32714         var docs = [];
32715         
32716         Roo.each(this.files, function(file){
32717             
32718             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32719                 var f = this.renderPreview(file);
32720                 files.push(f);
32721                 return;
32722             }
32723             
32724             if(file.type.indexOf('image') != -1){
32725                 this.delegates.push(
32726                     (function(){
32727                         _this.process(file);
32728                     }).createDelegate(this)
32729                 );
32730         
32731                 return;
32732             }
32733             
32734             docs.push(
32735                 (function(){
32736                     _this.process(file);
32737                 }).createDelegate(this)
32738             );
32739             
32740         }, this);
32741         
32742         this.files = files;
32743         
32744         this.delegates = this.delegates.concat(docs);
32745         
32746         if(!this.delegates.length){
32747             this.refresh();
32748             return;
32749         }
32750         
32751         this.progressBar.aria_valuemax = this.delegates.length;
32752         
32753         this.arrange();
32754         
32755         return;
32756     },
32757     
32758     arrange : function()
32759     {
32760         if(!this.delegates.length){
32761             this.progressDialog.hide();
32762             this.refresh();
32763             return;
32764         }
32765         
32766         var delegate = this.delegates.shift();
32767         
32768         this.progressDialog.show();
32769         
32770         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32771         
32772         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32773         
32774         delegate();
32775     },
32776     
32777     refresh : function()
32778     {
32779         this.uploader.show();
32780         
32781         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32782             this.uploader.hide();
32783         }
32784         
32785         Roo.isTouch ? this.closable(false) : this.closable(true);
32786         
32787         this.fireEvent('refresh', this);
32788     },
32789     
32790     onRemove : function(e, el, o)
32791     {
32792         e.preventDefault();
32793         
32794         this.fireEvent('remove', this, o);
32795         
32796     },
32797     
32798     remove : function(o)
32799     {
32800         var files = [];
32801         
32802         Roo.each(this.files, function(file){
32803             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32804                 files.push(file);
32805                 return;
32806             }
32807
32808             o.target.remove();
32809
32810         }, this);
32811         
32812         this.files = files;
32813         
32814         this.refresh();
32815     },
32816     
32817     clear : function()
32818     {
32819         Roo.each(this.files, function(file){
32820             if(!file.target){
32821                 return;
32822             }
32823             
32824             file.target.remove();
32825
32826         }, this);
32827         
32828         this.files = [];
32829         
32830         this.refresh();
32831     },
32832     
32833     onClick : function(e, el, o)
32834     {
32835         e.preventDefault();
32836         
32837         this.fireEvent('click', this, o);
32838         
32839     },
32840     
32841     closable : function(closable)
32842     {
32843         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32844             
32845             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32846             
32847             if(closable){
32848                 el.show();
32849                 return;
32850             }
32851             
32852             el.hide();
32853             
32854         }, this);
32855     },
32856     
32857     xhrOnLoad : function(xhr)
32858     {
32859         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32860             el.remove();
32861         }, this);
32862         
32863         if (xhr.readyState !== 4) {
32864             this.arrange();
32865             this.fireEvent('exception', this, xhr);
32866             return;
32867         }
32868
32869         var response = Roo.decode(xhr.responseText);
32870         
32871         if(!response.success){
32872             this.arrange();
32873             this.fireEvent('exception', this, xhr);
32874             return;
32875         }
32876         
32877         var file = this.renderPreview(response.data);
32878         
32879         this.files.push(file);
32880         
32881         this.arrange();
32882         
32883         this.fireEvent('afterupload', this, xhr);
32884         
32885     },
32886     
32887     xhrOnError : function(xhr)
32888     {
32889         Roo.log('xhr on error');
32890         
32891         var response = Roo.decode(xhr.responseText);
32892           
32893         Roo.log(response);
32894         
32895         this.arrange();
32896     },
32897     
32898     process : function(file)
32899     {
32900         if(this.fireEvent('process', this, file) !== false){
32901             if(this.editable && file.type.indexOf('image') != -1){
32902                 this.fireEvent('edit', this, file);
32903                 return;
32904             }
32905
32906             this.uploadStart(file, false);
32907
32908             return;
32909         }
32910         
32911     },
32912     
32913     uploadStart : function(file, crop)
32914     {
32915         this.xhr = new XMLHttpRequest();
32916         
32917         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32918             this.arrange();
32919             return;
32920         }
32921         
32922         file.xhr = this.xhr;
32923             
32924         this.managerEl.createChild({
32925             tag : 'div',
32926             cls : 'roo-document-manager-loading',
32927             cn : [
32928                 {
32929                     tag : 'div',
32930                     tooltip : file.name,
32931                     cls : 'roo-document-manager-thumb',
32932                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32933                 }
32934             ]
32935
32936         });
32937
32938         this.xhr.open(this.method, this.url, true);
32939         
32940         var headers = {
32941             "Accept": "application/json",
32942             "Cache-Control": "no-cache",
32943             "X-Requested-With": "XMLHttpRequest"
32944         };
32945         
32946         for (var headerName in headers) {
32947             var headerValue = headers[headerName];
32948             if (headerValue) {
32949                 this.xhr.setRequestHeader(headerName, headerValue);
32950             }
32951         }
32952         
32953         var _this = this;
32954         
32955         this.xhr.onload = function()
32956         {
32957             _this.xhrOnLoad(_this.xhr);
32958         }
32959         
32960         this.xhr.onerror = function()
32961         {
32962             _this.xhrOnError(_this.xhr);
32963         }
32964         
32965         var formData = new FormData();
32966
32967         formData.append('returnHTML', 'NO');
32968         
32969         if(crop){
32970             formData.append('crop', crop);
32971         }
32972         
32973         formData.append(this.paramName, file, file.name);
32974         
32975         var options = {
32976             file : file, 
32977             manually : false
32978         };
32979         
32980         if(this.fireEvent('prepare', this, formData, options) != false){
32981             
32982             if(options.manually){
32983                 return;
32984             }
32985             
32986             this.xhr.send(formData);
32987             return;
32988         };
32989         
32990         this.uploadCancel();
32991     },
32992     
32993     uploadCancel : function()
32994     {
32995         if (this.xhr) {
32996             this.xhr.abort();
32997         }
32998         
32999         this.delegates = [];
33000         
33001         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33002             el.remove();
33003         }, this);
33004         
33005         this.arrange();
33006     },
33007     
33008     renderPreview : function(file)
33009     {
33010         if(typeof(file.target) != 'undefined' && file.target){
33011             return file;
33012         }
33013         
33014         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33015         
33016         var previewEl = this.managerEl.createChild({
33017             tag : 'div',
33018             cls : 'roo-document-manager-preview',
33019             cn : [
33020                 {
33021                     tag : 'div',
33022                     tooltip : file[this.toolTipName],
33023                     cls : 'roo-document-manager-thumb',
33024                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33025                 },
33026                 {
33027                     tag : 'button',
33028                     cls : 'close',
33029                     html : '<i class="fa fa-times-circle"></i>'
33030                 }
33031             ]
33032         });
33033
33034         var close = previewEl.select('button.close', true).first();
33035
33036         close.on('click', this.onRemove, this, file);
33037
33038         file.target = previewEl;
33039
33040         var image = previewEl.select('img', true).first();
33041         
33042         var _this = this;
33043         
33044         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33045         
33046         image.on('click', this.onClick, this, file);
33047         
33048         this.fireEvent('previewrendered', this, file);
33049         
33050         return file;
33051         
33052     },
33053     
33054     onPreviewLoad : function(file, image)
33055     {
33056         if(typeof(file.target) == 'undefined' || !file.target){
33057             return;
33058         }
33059         
33060         var width = image.dom.naturalWidth || image.dom.width;
33061         var height = image.dom.naturalHeight || image.dom.height;
33062         
33063         if(!this.previewResize) {
33064             return;
33065         }
33066         
33067         if(width > height){
33068             file.target.addClass('wide');
33069             return;
33070         }
33071         
33072         file.target.addClass('tall');
33073         return;
33074         
33075     },
33076     
33077     uploadFromSource : function(file, crop)
33078     {
33079         this.xhr = new XMLHttpRequest();
33080         
33081         this.managerEl.createChild({
33082             tag : 'div',
33083             cls : 'roo-document-manager-loading',
33084             cn : [
33085                 {
33086                     tag : 'div',
33087                     tooltip : file.name,
33088                     cls : 'roo-document-manager-thumb',
33089                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33090                 }
33091             ]
33092
33093         });
33094
33095         this.xhr.open(this.method, this.url, true);
33096         
33097         var headers = {
33098             "Accept": "application/json",
33099             "Cache-Control": "no-cache",
33100             "X-Requested-With": "XMLHttpRequest"
33101         };
33102         
33103         for (var headerName in headers) {
33104             var headerValue = headers[headerName];
33105             if (headerValue) {
33106                 this.xhr.setRequestHeader(headerName, headerValue);
33107             }
33108         }
33109         
33110         var _this = this;
33111         
33112         this.xhr.onload = function()
33113         {
33114             _this.xhrOnLoad(_this.xhr);
33115         }
33116         
33117         this.xhr.onerror = function()
33118         {
33119             _this.xhrOnError(_this.xhr);
33120         }
33121         
33122         var formData = new FormData();
33123
33124         formData.append('returnHTML', 'NO');
33125         
33126         formData.append('crop', crop);
33127         
33128         if(typeof(file.filename) != 'undefined'){
33129             formData.append('filename', file.filename);
33130         }
33131         
33132         if(typeof(file.mimetype) != 'undefined'){
33133             formData.append('mimetype', file.mimetype);
33134         }
33135         
33136         Roo.log(formData);
33137         
33138         if(this.fireEvent('prepare', this, formData) != false){
33139             this.xhr.send(formData);
33140         };
33141     }
33142 });
33143
33144 /*
33145 * Licence: LGPL
33146 */
33147
33148 /**
33149  * @class Roo.bootstrap.DocumentViewer
33150  * @extends Roo.bootstrap.Component
33151  * Bootstrap DocumentViewer class
33152  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33153  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33154  * 
33155  * @constructor
33156  * Create a new DocumentViewer
33157  * @param {Object} config The config object
33158  */
33159
33160 Roo.bootstrap.DocumentViewer = function(config){
33161     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33162     
33163     this.addEvents({
33164         /**
33165          * @event initial
33166          * Fire after initEvent
33167          * @param {Roo.bootstrap.DocumentViewer} this
33168          */
33169         "initial" : true,
33170         /**
33171          * @event click
33172          * Fire after click
33173          * @param {Roo.bootstrap.DocumentViewer} this
33174          */
33175         "click" : true,
33176         /**
33177          * @event download
33178          * Fire after download button
33179          * @param {Roo.bootstrap.DocumentViewer} this
33180          */
33181         "download" : true,
33182         /**
33183          * @event trash
33184          * Fire after trash button
33185          * @param {Roo.bootstrap.DocumentViewer} this
33186          */
33187         "trash" : true
33188         
33189     });
33190 };
33191
33192 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33193     
33194     showDownload : true,
33195     
33196     showTrash : true,
33197     
33198     getAutoCreate : function()
33199     {
33200         var cfg = {
33201             tag : 'div',
33202             cls : 'roo-document-viewer',
33203             cn : [
33204                 {
33205                     tag : 'div',
33206                     cls : 'roo-document-viewer-body',
33207                     cn : [
33208                         {
33209                             tag : 'div',
33210                             cls : 'roo-document-viewer-thumb',
33211                             cn : [
33212                                 {
33213                                     tag : 'img',
33214                                     cls : 'roo-document-viewer-image'
33215                                 }
33216                             ]
33217                         }
33218                     ]
33219                 },
33220                 {
33221                     tag : 'div',
33222                     cls : 'roo-document-viewer-footer',
33223                     cn : {
33224                         tag : 'div',
33225                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33226                         cn : [
33227                             {
33228                                 tag : 'div',
33229                                 cls : 'btn-group roo-document-viewer-download',
33230                                 cn : [
33231                                     {
33232                                         tag : 'button',
33233                                         cls : 'btn btn-default',
33234                                         html : '<i class="fa fa-download"></i>'
33235                                     }
33236                                 ]
33237                             },
33238                             {
33239                                 tag : 'div',
33240                                 cls : 'btn-group roo-document-viewer-trash',
33241                                 cn : [
33242                                     {
33243                                         tag : 'button',
33244                                         cls : 'btn btn-default',
33245                                         html : '<i class="fa fa-trash"></i>'
33246                                     }
33247                                 ]
33248                             }
33249                         ]
33250                     }
33251                 }
33252             ]
33253         };
33254         
33255         return cfg;
33256     },
33257     
33258     initEvents : function()
33259     {
33260         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33261         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33262         
33263         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33264         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33265         
33266         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33267         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33268         
33269         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33270         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33271         
33272         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33273         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33274         
33275         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33276         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33277         
33278         this.bodyEl.on('click', this.onClick, this);
33279         this.downloadBtn.on('click', this.onDownload, this);
33280         this.trashBtn.on('click', this.onTrash, this);
33281         
33282         this.downloadBtn.hide();
33283         this.trashBtn.hide();
33284         
33285         if(this.showDownload){
33286             this.downloadBtn.show();
33287         }
33288         
33289         if(this.showTrash){
33290             this.trashBtn.show();
33291         }
33292         
33293         if(!this.showDownload && !this.showTrash) {
33294             this.footerEl.hide();
33295         }
33296         
33297     },
33298     
33299     initial : function()
33300     {
33301         this.fireEvent('initial', this);
33302         
33303     },
33304     
33305     onClick : function(e)
33306     {
33307         e.preventDefault();
33308         
33309         this.fireEvent('click', this);
33310     },
33311     
33312     onDownload : function(e)
33313     {
33314         e.preventDefault();
33315         
33316         this.fireEvent('download', this);
33317     },
33318     
33319     onTrash : function(e)
33320     {
33321         e.preventDefault();
33322         
33323         this.fireEvent('trash', this);
33324     }
33325     
33326 });
33327 /*
33328  * - LGPL
33329  *
33330  * nav progress bar
33331  * 
33332  */
33333
33334 /**
33335  * @class Roo.bootstrap.NavProgressBar
33336  * @extends Roo.bootstrap.Component
33337  * Bootstrap NavProgressBar class
33338  * 
33339  * @constructor
33340  * Create a new nav progress bar
33341  * @param {Object} config The config object
33342  */
33343
33344 Roo.bootstrap.NavProgressBar = function(config){
33345     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33346
33347     this.bullets = this.bullets || [];
33348    
33349 //    Roo.bootstrap.NavProgressBar.register(this);
33350      this.addEvents({
33351         /**
33352              * @event changed
33353              * Fires when the active item changes
33354              * @param {Roo.bootstrap.NavProgressBar} this
33355              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33356              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33357          */
33358         'changed': true
33359      });
33360     
33361 };
33362
33363 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33364     
33365     bullets : [],
33366     barItems : [],
33367     
33368     getAutoCreate : function()
33369     {
33370         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33371         
33372         cfg = {
33373             tag : 'div',
33374             cls : 'roo-navigation-bar-group',
33375             cn : [
33376                 {
33377                     tag : 'div',
33378                     cls : 'roo-navigation-top-bar'
33379                 },
33380                 {
33381                     tag : 'div',
33382                     cls : 'roo-navigation-bullets-bar',
33383                     cn : [
33384                         {
33385                             tag : 'ul',
33386                             cls : 'roo-navigation-bar'
33387                         }
33388                     ]
33389                 },
33390                 
33391                 {
33392                     tag : 'div',
33393                     cls : 'roo-navigation-bottom-bar'
33394                 }
33395             ]
33396             
33397         };
33398         
33399         return cfg;
33400         
33401     },
33402     
33403     initEvents: function() 
33404     {
33405         
33406     },
33407     
33408     onRender : function(ct, position) 
33409     {
33410         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33411         
33412         if(this.bullets.length){
33413             Roo.each(this.bullets, function(b){
33414                this.addItem(b);
33415             }, this);
33416         }
33417         
33418         this.format();
33419         
33420     },
33421     
33422     addItem : function(cfg)
33423     {
33424         var item = new Roo.bootstrap.NavProgressItem(cfg);
33425         
33426         item.parentId = this.id;
33427         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33428         
33429         if(cfg.html){
33430             var top = new Roo.bootstrap.Element({
33431                 tag : 'div',
33432                 cls : 'roo-navigation-bar-text'
33433             });
33434             
33435             var bottom = new Roo.bootstrap.Element({
33436                 tag : 'div',
33437                 cls : 'roo-navigation-bar-text'
33438             });
33439             
33440             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33441             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33442             
33443             var topText = new Roo.bootstrap.Element({
33444                 tag : 'span',
33445                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33446             });
33447             
33448             var bottomText = new Roo.bootstrap.Element({
33449                 tag : 'span',
33450                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33451             });
33452             
33453             topText.onRender(top.el, null);
33454             bottomText.onRender(bottom.el, null);
33455             
33456             item.topEl = top;
33457             item.bottomEl = bottom;
33458         }
33459         
33460         this.barItems.push(item);
33461         
33462         return item;
33463     },
33464     
33465     getActive : function()
33466     {
33467         var active = false;
33468         
33469         Roo.each(this.barItems, function(v){
33470             
33471             if (!v.isActive()) {
33472                 return;
33473             }
33474             
33475             active = v;
33476             return false;
33477             
33478         });
33479         
33480         return active;
33481     },
33482     
33483     setActiveItem : function(item)
33484     {
33485         var prev = false;
33486         
33487         Roo.each(this.barItems, function(v){
33488             if (v.rid == item.rid) {
33489                 return ;
33490             }
33491             
33492             if (v.isActive()) {
33493                 v.setActive(false);
33494                 prev = v;
33495             }
33496         });
33497
33498         item.setActive(true);
33499         
33500         this.fireEvent('changed', this, item, prev);
33501     },
33502     
33503     getBarItem: function(rid)
33504     {
33505         var ret = false;
33506         
33507         Roo.each(this.barItems, function(e) {
33508             if (e.rid != rid) {
33509                 return;
33510             }
33511             
33512             ret =  e;
33513             return false;
33514         });
33515         
33516         return ret;
33517     },
33518     
33519     indexOfItem : function(item)
33520     {
33521         var index = false;
33522         
33523         Roo.each(this.barItems, function(v, i){
33524             
33525             if (v.rid != item.rid) {
33526                 return;
33527             }
33528             
33529             index = i;
33530             return false
33531         });
33532         
33533         return index;
33534     },
33535     
33536     setActiveNext : function()
33537     {
33538         var i = this.indexOfItem(this.getActive());
33539         
33540         if (i > this.barItems.length) {
33541             return;
33542         }
33543         
33544         this.setActiveItem(this.barItems[i+1]);
33545     },
33546     
33547     setActivePrev : function()
33548     {
33549         var i = this.indexOfItem(this.getActive());
33550         
33551         if (i  < 1) {
33552             return;
33553         }
33554         
33555         this.setActiveItem(this.barItems[i-1]);
33556     },
33557     
33558     format : function()
33559     {
33560         if(!this.barItems.length){
33561             return;
33562         }
33563      
33564         var width = 100 / this.barItems.length;
33565         
33566         Roo.each(this.barItems, function(i){
33567             i.el.setStyle('width', width + '%');
33568             i.topEl.el.setStyle('width', width + '%');
33569             i.bottomEl.el.setStyle('width', width + '%');
33570         }, this);
33571         
33572     }
33573     
33574 });
33575 /*
33576  * - LGPL
33577  *
33578  * Nav Progress Item
33579  * 
33580  */
33581
33582 /**
33583  * @class Roo.bootstrap.NavProgressItem
33584  * @extends Roo.bootstrap.Component
33585  * Bootstrap NavProgressItem class
33586  * @cfg {String} rid the reference id
33587  * @cfg {Boolean} active (true|false) Is item active default false
33588  * @cfg {Boolean} disabled (true|false) Is item active default false
33589  * @cfg {String} html
33590  * @cfg {String} position (top|bottom) text position default bottom
33591  * @cfg {String} icon show icon instead of number
33592  * 
33593  * @constructor
33594  * Create a new NavProgressItem
33595  * @param {Object} config The config object
33596  */
33597 Roo.bootstrap.NavProgressItem = function(config){
33598     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33599     this.addEvents({
33600         // raw events
33601         /**
33602          * @event click
33603          * The raw click event for the entire grid.
33604          * @param {Roo.bootstrap.NavProgressItem} this
33605          * @param {Roo.EventObject} e
33606          */
33607         "click" : true
33608     });
33609    
33610 };
33611
33612 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33613     
33614     rid : '',
33615     active : false,
33616     disabled : false,
33617     html : '',
33618     position : 'bottom',
33619     icon : false,
33620     
33621     getAutoCreate : function()
33622     {
33623         var iconCls = 'roo-navigation-bar-item-icon';
33624         
33625         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33626         
33627         var cfg = {
33628             tag: 'li',
33629             cls: 'roo-navigation-bar-item',
33630             cn : [
33631                 {
33632                     tag : 'i',
33633                     cls : iconCls
33634                 }
33635             ]
33636         };
33637         
33638         if(this.active){
33639             cfg.cls += ' active';
33640         }
33641         if(this.disabled){
33642             cfg.cls += ' disabled';
33643         }
33644         
33645         return cfg;
33646     },
33647     
33648     disable : function()
33649     {
33650         this.setDisabled(true);
33651     },
33652     
33653     enable : function()
33654     {
33655         this.setDisabled(false);
33656     },
33657     
33658     initEvents: function() 
33659     {
33660         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33661         
33662         this.iconEl.on('click', this.onClick, this);
33663     },
33664     
33665     onClick : function(e)
33666     {
33667         e.preventDefault();
33668         
33669         if(this.disabled){
33670             return;
33671         }
33672         
33673         if(this.fireEvent('click', this, e) === false){
33674             return;
33675         };
33676         
33677         this.parent().setActiveItem(this);
33678     },
33679     
33680     isActive: function () 
33681     {
33682         return this.active;
33683     },
33684     
33685     setActive : function(state)
33686     {
33687         if(this.active == state){
33688             return;
33689         }
33690         
33691         this.active = state;
33692         
33693         if (state) {
33694             this.el.addClass('active');
33695             return;
33696         }
33697         
33698         this.el.removeClass('active');
33699         
33700         return;
33701     },
33702     
33703     setDisabled : function(state)
33704     {
33705         if(this.disabled == state){
33706             return;
33707         }
33708         
33709         this.disabled = state;
33710         
33711         if (state) {
33712             this.el.addClass('disabled');
33713             return;
33714         }
33715         
33716         this.el.removeClass('disabled');
33717     },
33718     
33719     tooltipEl : function()
33720     {
33721         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33722     }
33723 });
33724  
33725
33726  /*
33727  * - LGPL
33728  *
33729  * FieldLabel
33730  * 
33731  */
33732
33733 /**
33734  * @class Roo.bootstrap.FieldLabel
33735  * @extends Roo.bootstrap.Component
33736  * Bootstrap FieldLabel class
33737  * @cfg {String} html contents of the element
33738  * @cfg {String} tag tag of the element default label
33739  * @cfg {String} cls class of the element
33740  * @cfg {String} target label target 
33741  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33742  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33743  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33744  * @cfg {String} iconTooltip default "This field is required"
33745  * @cfg {String} indicatorpos (left|right) default left
33746  * 
33747  * @constructor
33748  * Create a new FieldLabel
33749  * @param {Object} config The config object
33750  */
33751
33752 Roo.bootstrap.FieldLabel = function(config){
33753     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33754     
33755     this.addEvents({
33756             /**
33757              * @event invalid
33758              * Fires after the field has been marked as invalid.
33759              * @param {Roo.form.FieldLabel} this
33760              * @param {String} msg The validation message
33761              */
33762             invalid : true,
33763             /**
33764              * @event valid
33765              * Fires after the field has been validated with no errors.
33766              * @param {Roo.form.FieldLabel} this
33767              */
33768             valid : true
33769         });
33770 };
33771
33772 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33773     
33774     tag: 'label',
33775     cls: '',
33776     html: '',
33777     target: '',
33778     allowBlank : true,
33779     invalidClass : 'has-warning',
33780     validClass : 'has-success',
33781     iconTooltip : 'This field is required',
33782     indicatorpos : 'left',
33783     
33784     getAutoCreate : function(){
33785         
33786         var cls = "";
33787         if (!this.allowBlank) {
33788             cls  = "visible";
33789         }
33790         
33791         var cfg = {
33792             tag : this.tag,
33793             cls : 'roo-bootstrap-field-label ' + this.cls,
33794             for : this.target,
33795             cn : [
33796                 {
33797                     tag : 'i',
33798                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33799                     tooltip : this.iconTooltip
33800                 },
33801                 {
33802                     tag : 'span',
33803                     html : this.html
33804                 }
33805             ] 
33806         };
33807         
33808         if(this.indicatorpos == 'right'){
33809             var cfg = {
33810                 tag : this.tag,
33811                 cls : 'roo-bootstrap-field-label ' + this.cls,
33812                 for : this.target,
33813                 cn : [
33814                     {
33815                         tag : 'span',
33816                         html : this.html
33817                     },
33818                     {
33819                         tag : 'i',
33820                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33821                         tooltip : this.iconTooltip
33822                     }
33823                 ] 
33824             };
33825         }
33826         
33827         return cfg;
33828     },
33829     
33830     initEvents: function() 
33831     {
33832         Roo.bootstrap.Element.superclass.initEvents.call(this);
33833         
33834         this.indicator = this.indicatorEl();
33835         
33836         if(this.indicator){
33837             this.indicator.removeClass('visible');
33838             this.indicator.addClass('invisible');
33839         }
33840         
33841         Roo.bootstrap.FieldLabel.register(this);
33842     },
33843     
33844     indicatorEl : function()
33845     {
33846         var indicator = this.el.select('i.roo-required-indicator',true).first();
33847         
33848         if(!indicator){
33849             return false;
33850         }
33851         
33852         return indicator;
33853         
33854     },
33855     
33856     /**
33857      * Mark this field as valid
33858      */
33859     markValid : function()
33860     {
33861         if(this.indicator){
33862             this.indicator.removeClass('visible');
33863             this.indicator.addClass('invisible');
33864         }
33865         if (Roo.bootstrap.version == 3) {
33866             this.el.removeClass(this.invalidClass);
33867             this.el.addClass(this.validClass);
33868         } else {
33869             this.el.removeClass('is-invalid');
33870             this.el.addClass('is-valid');
33871         }
33872         
33873         
33874         this.fireEvent('valid', this);
33875     },
33876     
33877     /**
33878      * Mark this field as invalid
33879      * @param {String} msg The validation message
33880      */
33881     markInvalid : function(msg)
33882     {
33883         if(this.indicator){
33884             this.indicator.removeClass('invisible');
33885             this.indicator.addClass('visible');
33886         }
33887           if (Roo.bootstrap.version == 3) {
33888             this.el.removeClass(this.validClass);
33889             this.el.addClass(this.invalidClass);
33890         } else {
33891             this.el.removeClass('is-valid');
33892             this.el.addClass('is-invalid');
33893         }
33894         
33895         
33896         this.fireEvent('invalid', this, msg);
33897     }
33898     
33899    
33900 });
33901
33902 Roo.apply(Roo.bootstrap.FieldLabel, {
33903     
33904     groups: {},
33905     
33906      /**
33907     * register a FieldLabel Group
33908     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33909     */
33910     register : function(label)
33911     {
33912         if(this.groups.hasOwnProperty(label.target)){
33913             return;
33914         }
33915      
33916         this.groups[label.target] = label;
33917         
33918     },
33919     /**
33920     * fetch a FieldLabel Group based on the target
33921     * @param {string} target
33922     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33923     */
33924     get: function(target) {
33925         if (typeof(this.groups[target]) == 'undefined') {
33926             return false;
33927         }
33928         
33929         return this.groups[target] ;
33930     }
33931 });
33932
33933  
33934
33935  /*
33936  * - LGPL
33937  *
33938  * page DateSplitField.
33939  * 
33940  */
33941
33942
33943 /**
33944  * @class Roo.bootstrap.DateSplitField
33945  * @extends Roo.bootstrap.Component
33946  * Bootstrap DateSplitField class
33947  * @cfg {string} fieldLabel - the label associated
33948  * @cfg {Number} labelWidth set the width of label (0-12)
33949  * @cfg {String} labelAlign (top|left)
33950  * @cfg {Boolean} dayAllowBlank (true|false) default false
33951  * @cfg {Boolean} monthAllowBlank (true|false) default false
33952  * @cfg {Boolean} yearAllowBlank (true|false) default false
33953  * @cfg {string} dayPlaceholder 
33954  * @cfg {string} monthPlaceholder
33955  * @cfg {string} yearPlaceholder
33956  * @cfg {string} dayFormat default 'd'
33957  * @cfg {string} monthFormat default 'm'
33958  * @cfg {string} yearFormat default 'Y'
33959  * @cfg {Number} labellg set the width of label (1-12)
33960  * @cfg {Number} labelmd set the width of label (1-12)
33961  * @cfg {Number} labelsm set the width of label (1-12)
33962  * @cfg {Number} labelxs set the width of label (1-12)
33963
33964  *     
33965  * @constructor
33966  * Create a new DateSplitField
33967  * @param {Object} config The config object
33968  */
33969
33970 Roo.bootstrap.DateSplitField = function(config){
33971     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33972     
33973     this.addEvents({
33974         // raw events
33975          /**
33976          * @event years
33977          * getting the data of years
33978          * @param {Roo.bootstrap.DateSplitField} this
33979          * @param {Object} years
33980          */
33981         "years" : true,
33982         /**
33983          * @event days
33984          * getting the data of days
33985          * @param {Roo.bootstrap.DateSplitField} this
33986          * @param {Object} days
33987          */
33988         "days" : true,
33989         /**
33990          * @event invalid
33991          * Fires after the field has been marked as invalid.
33992          * @param {Roo.form.Field} this
33993          * @param {String} msg The validation message
33994          */
33995         invalid : true,
33996        /**
33997          * @event valid
33998          * Fires after the field has been validated with no errors.
33999          * @param {Roo.form.Field} this
34000          */
34001         valid : true
34002     });
34003 };
34004
34005 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34006     
34007     fieldLabel : '',
34008     labelAlign : 'top',
34009     labelWidth : 3,
34010     dayAllowBlank : false,
34011     monthAllowBlank : false,
34012     yearAllowBlank : false,
34013     dayPlaceholder : '',
34014     monthPlaceholder : '',
34015     yearPlaceholder : '',
34016     dayFormat : 'd',
34017     monthFormat : 'm',
34018     yearFormat : 'Y',
34019     isFormField : true,
34020     labellg : 0,
34021     labelmd : 0,
34022     labelsm : 0,
34023     labelxs : 0,
34024     
34025     getAutoCreate : function()
34026     {
34027         var cfg = {
34028             tag : 'div',
34029             cls : 'row roo-date-split-field-group',
34030             cn : [
34031                 {
34032                     tag : 'input',
34033                     type : 'hidden',
34034                     cls : 'form-hidden-field roo-date-split-field-group-value',
34035                     name : this.name
34036                 }
34037             ]
34038         };
34039         
34040         var labelCls = 'col-md-12';
34041         var contentCls = 'col-md-4';
34042         
34043         if(this.fieldLabel){
34044             
34045             var label = {
34046                 tag : 'div',
34047                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34048                 cn : [
34049                     {
34050                         tag : 'label',
34051                         html : this.fieldLabel
34052                     }
34053                 ]
34054             };
34055             
34056             if(this.labelAlign == 'left'){
34057             
34058                 if(this.labelWidth > 12){
34059                     label.style = "width: " + this.labelWidth + 'px';
34060                 }
34061
34062                 if(this.labelWidth < 13 && this.labelmd == 0){
34063                     this.labelmd = this.labelWidth;
34064                 }
34065
34066                 if(this.labellg > 0){
34067                     labelCls = ' col-lg-' + this.labellg;
34068                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34069                 }
34070
34071                 if(this.labelmd > 0){
34072                     labelCls = ' col-md-' + this.labelmd;
34073                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34074                 }
34075
34076                 if(this.labelsm > 0){
34077                     labelCls = ' col-sm-' + this.labelsm;
34078                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34079                 }
34080
34081                 if(this.labelxs > 0){
34082                     labelCls = ' col-xs-' + this.labelxs;
34083                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34084                 }
34085             }
34086             
34087             label.cls += ' ' + labelCls;
34088             
34089             cfg.cn.push(label);
34090         }
34091         
34092         Roo.each(['day', 'month', 'year'], function(t){
34093             cfg.cn.push({
34094                 tag : 'div',
34095                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34096             });
34097         }, this);
34098         
34099         return cfg;
34100     },
34101     
34102     inputEl: function ()
34103     {
34104         return this.el.select('.roo-date-split-field-group-value', true).first();
34105     },
34106     
34107     onRender : function(ct, position) 
34108     {
34109         var _this = this;
34110         
34111         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34112         
34113         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34114         
34115         this.dayField = new Roo.bootstrap.ComboBox({
34116             allowBlank : this.dayAllowBlank,
34117             alwaysQuery : true,
34118             displayField : 'value',
34119             editable : false,
34120             fieldLabel : '',
34121             forceSelection : true,
34122             mode : 'local',
34123             placeholder : this.dayPlaceholder,
34124             selectOnFocus : true,
34125             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34126             triggerAction : 'all',
34127             typeAhead : true,
34128             valueField : 'value',
34129             store : new Roo.data.SimpleStore({
34130                 data : (function() {    
34131                     var days = [];
34132                     _this.fireEvent('days', _this, days);
34133                     return days;
34134                 })(),
34135                 fields : [ 'value' ]
34136             }),
34137             listeners : {
34138                 select : function (_self, record, index)
34139                 {
34140                     _this.setValue(_this.getValue());
34141                 }
34142             }
34143         });
34144
34145         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34146         
34147         this.monthField = new Roo.bootstrap.MonthField({
34148             after : '<i class=\"fa fa-calendar\"></i>',
34149             allowBlank : this.monthAllowBlank,
34150             placeholder : this.monthPlaceholder,
34151             readOnly : true,
34152             listeners : {
34153                 render : function (_self)
34154                 {
34155                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34156                         e.preventDefault();
34157                         _self.focus();
34158                     });
34159                 },
34160                 select : function (_self, oldvalue, newvalue)
34161                 {
34162                     _this.setValue(_this.getValue());
34163                 }
34164             }
34165         });
34166         
34167         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34168         
34169         this.yearField = new Roo.bootstrap.ComboBox({
34170             allowBlank : this.yearAllowBlank,
34171             alwaysQuery : true,
34172             displayField : 'value',
34173             editable : false,
34174             fieldLabel : '',
34175             forceSelection : true,
34176             mode : 'local',
34177             placeholder : this.yearPlaceholder,
34178             selectOnFocus : true,
34179             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34180             triggerAction : 'all',
34181             typeAhead : true,
34182             valueField : 'value',
34183             store : new Roo.data.SimpleStore({
34184                 data : (function() {
34185                     var years = [];
34186                     _this.fireEvent('years', _this, years);
34187                     return years;
34188                 })(),
34189                 fields : [ 'value' ]
34190             }),
34191             listeners : {
34192                 select : function (_self, record, index)
34193                 {
34194                     _this.setValue(_this.getValue());
34195                 }
34196             }
34197         });
34198
34199         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34200     },
34201     
34202     setValue : function(v, format)
34203     {
34204         this.inputEl.dom.value = v;
34205         
34206         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34207         
34208         var d = Date.parseDate(v, f);
34209         
34210         if(!d){
34211             this.validate();
34212             return;
34213         }
34214         
34215         this.setDay(d.format(this.dayFormat));
34216         this.setMonth(d.format(this.monthFormat));
34217         this.setYear(d.format(this.yearFormat));
34218         
34219         this.validate();
34220         
34221         return;
34222     },
34223     
34224     setDay : function(v)
34225     {
34226         this.dayField.setValue(v);
34227         this.inputEl.dom.value = this.getValue();
34228         this.validate();
34229         return;
34230     },
34231     
34232     setMonth : function(v)
34233     {
34234         this.monthField.setValue(v, true);
34235         this.inputEl.dom.value = this.getValue();
34236         this.validate();
34237         return;
34238     },
34239     
34240     setYear : function(v)
34241     {
34242         this.yearField.setValue(v);
34243         this.inputEl.dom.value = this.getValue();
34244         this.validate();
34245         return;
34246     },
34247     
34248     getDay : function()
34249     {
34250         return this.dayField.getValue();
34251     },
34252     
34253     getMonth : function()
34254     {
34255         return this.monthField.getValue();
34256     },
34257     
34258     getYear : function()
34259     {
34260         return this.yearField.getValue();
34261     },
34262     
34263     getValue : function()
34264     {
34265         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34266         
34267         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34268         
34269         return date;
34270     },
34271     
34272     reset : function()
34273     {
34274         this.setDay('');
34275         this.setMonth('');
34276         this.setYear('');
34277         this.inputEl.dom.value = '';
34278         this.validate();
34279         return;
34280     },
34281     
34282     validate : function()
34283     {
34284         var d = this.dayField.validate();
34285         var m = this.monthField.validate();
34286         var y = this.yearField.validate();
34287         
34288         var valid = true;
34289         
34290         if(
34291                 (!this.dayAllowBlank && !d) ||
34292                 (!this.monthAllowBlank && !m) ||
34293                 (!this.yearAllowBlank && !y)
34294         ){
34295             valid = false;
34296         }
34297         
34298         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34299             return valid;
34300         }
34301         
34302         if(valid){
34303             this.markValid();
34304             return valid;
34305         }
34306         
34307         this.markInvalid();
34308         
34309         return valid;
34310     },
34311     
34312     markValid : function()
34313     {
34314         
34315         var label = this.el.select('label', true).first();
34316         var icon = this.el.select('i.fa-star', true).first();
34317
34318         if(label && icon){
34319             icon.remove();
34320         }
34321         
34322         this.fireEvent('valid', this);
34323     },
34324     
34325      /**
34326      * Mark this field as invalid
34327      * @param {String} msg The validation message
34328      */
34329     markInvalid : function(msg)
34330     {
34331         
34332         var label = this.el.select('label', true).first();
34333         var icon = this.el.select('i.fa-star', true).first();
34334
34335         if(label && !icon){
34336             this.el.select('.roo-date-split-field-label', true).createChild({
34337                 tag : 'i',
34338                 cls : 'text-danger fa fa-lg fa-star',
34339                 tooltip : 'This field is required',
34340                 style : 'margin-right:5px;'
34341             }, label, true);
34342         }
34343         
34344         this.fireEvent('invalid', this, msg);
34345     },
34346     
34347     clearInvalid : function()
34348     {
34349         var label = this.el.select('label', true).first();
34350         var icon = this.el.select('i.fa-star', true).first();
34351
34352         if(label && icon){
34353             icon.remove();
34354         }
34355         
34356         this.fireEvent('valid', this);
34357     },
34358     
34359     getName: function()
34360     {
34361         return this.name;
34362     }
34363     
34364 });
34365
34366  /**
34367  *
34368  * This is based on 
34369  * http://masonry.desandro.com
34370  *
34371  * The idea is to render all the bricks based on vertical width...
34372  *
34373  * The original code extends 'outlayer' - we might need to use that....
34374  * 
34375  */
34376
34377
34378 /**
34379  * @class Roo.bootstrap.LayoutMasonry
34380  * @extends Roo.bootstrap.Component
34381  * Bootstrap Layout Masonry class
34382  * 
34383  * @constructor
34384  * Create a new Element
34385  * @param {Object} config The config object
34386  */
34387
34388 Roo.bootstrap.LayoutMasonry = function(config){
34389     
34390     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34391     
34392     this.bricks = [];
34393     
34394     Roo.bootstrap.LayoutMasonry.register(this);
34395     
34396     this.addEvents({
34397         // raw events
34398         /**
34399          * @event layout
34400          * Fire after layout the items
34401          * @param {Roo.bootstrap.LayoutMasonry} this
34402          * @param {Roo.EventObject} e
34403          */
34404         "layout" : true
34405     });
34406     
34407 };
34408
34409 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34410     
34411     /**
34412      * @cfg {Boolean} isLayoutInstant = no animation?
34413      */   
34414     isLayoutInstant : false, // needed?
34415    
34416     /**
34417      * @cfg {Number} boxWidth  width of the columns
34418      */   
34419     boxWidth : 450,
34420     
34421       /**
34422      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34423      */   
34424     boxHeight : 0,
34425     
34426     /**
34427      * @cfg {Number} padWidth padding below box..
34428      */   
34429     padWidth : 10, 
34430     
34431     /**
34432      * @cfg {Number} gutter gutter width..
34433      */   
34434     gutter : 10,
34435     
34436      /**
34437      * @cfg {Number} maxCols maximum number of columns
34438      */   
34439     
34440     maxCols: 0,
34441     
34442     /**
34443      * @cfg {Boolean} isAutoInitial defalut true
34444      */   
34445     isAutoInitial : true, 
34446     
34447     containerWidth: 0,
34448     
34449     /**
34450      * @cfg {Boolean} isHorizontal defalut false
34451      */   
34452     isHorizontal : false, 
34453
34454     currentSize : null,
34455     
34456     tag: 'div',
34457     
34458     cls: '',
34459     
34460     bricks: null, //CompositeElement
34461     
34462     cols : 1,
34463     
34464     _isLayoutInited : false,
34465     
34466 //    isAlternative : false, // only use for vertical layout...
34467     
34468     /**
34469      * @cfg {Number} alternativePadWidth padding below box..
34470      */   
34471     alternativePadWidth : 50,
34472     
34473     selectedBrick : [],
34474     
34475     getAutoCreate : function(){
34476         
34477         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34478         
34479         var cfg = {
34480             tag: this.tag,
34481             cls: 'blog-masonary-wrapper ' + this.cls,
34482             cn : {
34483                 cls : 'mas-boxes masonary'
34484             }
34485         };
34486         
34487         return cfg;
34488     },
34489     
34490     getChildContainer: function( )
34491     {
34492         if (this.boxesEl) {
34493             return this.boxesEl;
34494         }
34495         
34496         this.boxesEl = this.el.select('.mas-boxes').first();
34497         
34498         return this.boxesEl;
34499     },
34500     
34501     
34502     initEvents : function()
34503     {
34504         var _this = this;
34505         
34506         if(this.isAutoInitial){
34507             Roo.log('hook children rendered');
34508             this.on('childrenrendered', function() {
34509                 Roo.log('children rendered');
34510                 _this.initial();
34511             } ,this);
34512         }
34513     },
34514     
34515     initial : function()
34516     {
34517         this.selectedBrick = [];
34518         
34519         this.currentSize = this.el.getBox(true);
34520         
34521         Roo.EventManager.onWindowResize(this.resize, this); 
34522
34523         if(!this.isAutoInitial){
34524             this.layout();
34525             return;
34526         }
34527         
34528         this.layout();
34529         
34530         return;
34531         //this.layout.defer(500,this);
34532         
34533     },
34534     
34535     resize : function()
34536     {
34537         var cs = this.el.getBox(true);
34538         
34539         if (
34540                 this.currentSize.width == cs.width && 
34541                 this.currentSize.x == cs.x && 
34542                 this.currentSize.height == cs.height && 
34543                 this.currentSize.y == cs.y 
34544         ) {
34545             Roo.log("no change in with or X or Y");
34546             return;
34547         }
34548         
34549         this.currentSize = cs;
34550         
34551         this.layout();
34552         
34553     },
34554     
34555     layout : function()
34556     {   
34557         this._resetLayout();
34558         
34559         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34560         
34561         this.layoutItems( isInstant );
34562       
34563         this._isLayoutInited = true;
34564         
34565         this.fireEvent('layout', this);
34566         
34567     },
34568     
34569     _resetLayout : function()
34570     {
34571         if(this.isHorizontal){
34572             this.horizontalMeasureColumns();
34573             return;
34574         }
34575         
34576         this.verticalMeasureColumns();
34577         
34578     },
34579     
34580     verticalMeasureColumns : function()
34581     {
34582         this.getContainerWidth();
34583         
34584 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34585 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34586 //            return;
34587 //        }
34588         
34589         var boxWidth = this.boxWidth + this.padWidth;
34590         
34591         if(this.containerWidth < this.boxWidth){
34592             boxWidth = this.containerWidth
34593         }
34594         
34595         var containerWidth = this.containerWidth;
34596         
34597         var cols = Math.floor(containerWidth / boxWidth);
34598         
34599         this.cols = Math.max( cols, 1 );
34600         
34601         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34602         
34603         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34604         
34605         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34606         
34607         this.colWidth = boxWidth + avail - this.padWidth;
34608         
34609         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34610         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34611     },
34612     
34613     horizontalMeasureColumns : function()
34614     {
34615         this.getContainerWidth();
34616         
34617         var boxWidth = this.boxWidth;
34618         
34619         if(this.containerWidth < boxWidth){
34620             boxWidth = this.containerWidth;
34621         }
34622         
34623         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34624         
34625         this.el.setHeight(boxWidth);
34626         
34627     },
34628     
34629     getContainerWidth : function()
34630     {
34631         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34632     },
34633     
34634     layoutItems : function( isInstant )
34635     {
34636         Roo.log(this.bricks);
34637         
34638         var items = Roo.apply([], this.bricks);
34639         
34640         if(this.isHorizontal){
34641             this._horizontalLayoutItems( items , isInstant );
34642             return;
34643         }
34644         
34645 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34646 //            this._verticalAlternativeLayoutItems( items , isInstant );
34647 //            return;
34648 //        }
34649         
34650         this._verticalLayoutItems( items , isInstant );
34651         
34652     },
34653     
34654     _verticalLayoutItems : function ( items , isInstant)
34655     {
34656         if ( !items || !items.length ) {
34657             return;
34658         }
34659         
34660         var standard = [
34661             ['xs', 'xs', 'xs', 'tall'],
34662             ['xs', 'xs', 'tall'],
34663             ['xs', 'xs', 'sm'],
34664             ['xs', 'xs', 'xs'],
34665             ['xs', 'tall'],
34666             ['xs', 'sm'],
34667             ['xs', 'xs'],
34668             ['xs'],
34669             
34670             ['sm', 'xs', 'xs'],
34671             ['sm', 'xs'],
34672             ['sm'],
34673             
34674             ['tall', 'xs', 'xs', 'xs'],
34675             ['tall', 'xs', 'xs'],
34676             ['tall', 'xs'],
34677             ['tall']
34678             
34679         ];
34680         
34681         var queue = [];
34682         
34683         var boxes = [];
34684         
34685         var box = [];
34686         
34687         Roo.each(items, function(item, k){
34688             
34689             switch (item.size) {
34690                 // these layouts take up a full box,
34691                 case 'md' :
34692                 case 'md-left' :
34693                 case 'md-right' :
34694                 case 'wide' :
34695                     
34696                     if(box.length){
34697                         boxes.push(box);
34698                         box = [];
34699                     }
34700                     
34701                     boxes.push([item]);
34702                     
34703                     break;
34704                     
34705                 case 'xs' :
34706                 case 'sm' :
34707                 case 'tall' :
34708                     
34709                     box.push(item);
34710                     
34711                     break;
34712                 default :
34713                     break;
34714                     
34715             }
34716             
34717         }, this);
34718         
34719         if(box.length){
34720             boxes.push(box);
34721             box = [];
34722         }
34723         
34724         var filterPattern = function(box, length)
34725         {
34726             if(!box.length){
34727                 return;
34728             }
34729             
34730             var match = false;
34731             
34732             var pattern = box.slice(0, length);
34733             
34734             var format = [];
34735             
34736             Roo.each(pattern, function(i){
34737                 format.push(i.size);
34738             }, this);
34739             
34740             Roo.each(standard, function(s){
34741                 
34742                 if(String(s) != String(format)){
34743                     return;
34744                 }
34745                 
34746                 match = true;
34747                 return false;
34748                 
34749             }, this);
34750             
34751             if(!match && length == 1){
34752                 return;
34753             }
34754             
34755             if(!match){
34756                 filterPattern(box, length - 1);
34757                 return;
34758             }
34759                 
34760             queue.push(pattern);
34761
34762             box = box.slice(length, box.length);
34763
34764             filterPattern(box, 4);
34765
34766             return;
34767             
34768         }
34769         
34770         Roo.each(boxes, function(box, k){
34771             
34772             if(!box.length){
34773                 return;
34774             }
34775             
34776             if(box.length == 1){
34777                 queue.push(box);
34778                 return;
34779             }
34780             
34781             filterPattern(box, 4);
34782             
34783         }, this);
34784         
34785         this._processVerticalLayoutQueue( queue, isInstant );
34786         
34787     },
34788     
34789 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34790 //    {
34791 //        if ( !items || !items.length ) {
34792 //            return;
34793 //        }
34794 //
34795 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34796 //        
34797 //    },
34798     
34799     _horizontalLayoutItems : function ( items , isInstant)
34800     {
34801         if ( !items || !items.length || items.length < 3) {
34802             return;
34803         }
34804         
34805         items.reverse();
34806         
34807         var eItems = items.slice(0, 3);
34808         
34809         items = items.slice(3, items.length);
34810         
34811         var standard = [
34812             ['xs', 'xs', 'xs', 'wide'],
34813             ['xs', 'xs', 'wide'],
34814             ['xs', 'xs', 'sm'],
34815             ['xs', 'xs', 'xs'],
34816             ['xs', 'wide'],
34817             ['xs', 'sm'],
34818             ['xs', 'xs'],
34819             ['xs'],
34820             
34821             ['sm', 'xs', 'xs'],
34822             ['sm', 'xs'],
34823             ['sm'],
34824             
34825             ['wide', 'xs', 'xs', 'xs'],
34826             ['wide', 'xs', 'xs'],
34827             ['wide', 'xs'],
34828             ['wide'],
34829             
34830             ['wide-thin']
34831         ];
34832         
34833         var queue = [];
34834         
34835         var boxes = [];
34836         
34837         var box = [];
34838         
34839         Roo.each(items, function(item, k){
34840             
34841             switch (item.size) {
34842                 case 'md' :
34843                 case 'md-left' :
34844                 case 'md-right' :
34845                 case 'tall' :
34846                     
34847                     if(box.length){
34848                         boxes.push(box);
34849                         box = [];
34850                     }
34851                     
34852                     boxes.push([item]);
34853                     
34854                     break;
34855                     
34856                 case 'xs' :
34857                 case 'sm' :
34858                 case 'wide' :
34859                 case 'wide-thin' :
34860                     
34861                     box.push(item);
34862                     
34863                     break;
34864                 default :
34865                     break;
34866                     
34867             }
34868             
34869         }, this);
34870         
34871         if(box.length){
34872             boxes.push(box);
34873             box = [];
34874         }
34875         
34876         var filterPattern = function(box, length)
34877         {
34878             if(!box.length){
34879                 return;
34880             }
34881             
34882             var match = false;
34883             
34884             var pattern = box.slice(0, length);
34885             
34886             var format = [];
34887             
34888             Roo.each(pattern, function(i){
34889                 format.push(i.size);
34890             }, this);
34891             
34892             Roo.each(standard, function(s){
34893                 
34894                 if(String(s) != String(format)){
34895                     return;
34896                 }
34897                 
34898                 match = true;
34899                 return false;
34900                 
34901             }, this);
34902             
34903             if(!match && length == 1){
34904                 return;
34905             }
34906             
34907             if(!match){
34908                 filterPattern(box, length - 1);
34909                 return;
34910             }
34911                 
34912             queue.push(pattern);
34913
34914             box = box.slice(length, box.length);
34915
34916             filterPattern(box, 4);
34917
34918             return;
34919             
34920         }
34921         
34922         Roo.each(boxes, function(box, k){
34923             
34924             if(!box.length){
34925                 return;
34926             }
34927             
34928             if(box.length == 1){
34929                 queue.push(box);
34930                 return;
34931             }
34932             
34933             filterPattern(box, 4);
34934             
34935         }, this);
34936         
34937         
34938         var prune = [];
34939         
34940         var pos = this.el.getBox(true);
34941         
34942         var minX = pos.x;
34943         
34944         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34945         
34946         var hit_end = false;
34947         
34948         Roo.each(queue, function(box){
34949             
34950             if(hit_end){
34951                 
34952                 Roo.each(box, function(b){
34953                 
34954                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34955                     b.el.hide();
34956
34957                 }, this);
34958
34959                 return;
34960             }
34961             
34962             var mx = 0;
34963             
34964             Roo.each(box, function(b){
34965                 
34966                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34967                 b.el.show();
34968
34969                 mx = Math.max(mx, b.x);
34970                 
34971             }, this);
34972             
34973             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34974             
34975             if(maxX < minX){
34976                 
34977                 Roo.each(box, function(b){
34978                 
34979                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34980                     b.el.hide();
34981                     
34982                 }, this);
34983                 
34984                 hit_end = true;
34985                 
34986                 return;
34987             }
34988             
34989             prune.push(box);
34990             
34991         }, this);
34992         
34993         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34994     },
34995     
34996     /** Sets position of item in DOM
34997     * @param {Element} item
34998     * @param {Number} x - horizontal position
34999     * @param {Number} y - vertical position
35000     * @param {Boolean} isInstant - disables transitions
35001     */
35002     _processVerticalLayoutQueue : function( queue, isInstant )
35003     {
35004         var pos = this.el.getBox(true);
35005         var x = pos.x;
35006         var y = pos.y;
35007         var maxY = [];
35008         
35009         for (var i = 0; i < this.cols; i++){
35010             maxY[i] = pos.y;
35011         }
35012         
35013         Roo.each(queue, function(box, k){
35014             
35015             var col = k % this.cols;
35016             
35017             Roo.each(box, function(b,kk){
35018                 
35019                 b.el.position('absolute');
35020                 
35021                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35022                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35023                 
35024                 if(b.size == 'md-left' || b.size == 'md-right'){
35025                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35026                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35027                 }
35028                 
35029                 b.el.setWidth(width);
35030                 b.el.setHeight(height);
35031                 // iframe?
35032                 b.el.select('iframe',true).setSize(width,height);
35033                 
35034             }, this);
35035             
35036             for (var i = 0; i < this.cols; i++){
35037                 
35038                 if(maxY[i] < maxY[col]){
35039                     col = i;
35040                     continue;
35041                 }
35042                 
35043                 col = Math.min(col, i);
35044                 
35045             }
35046             
35047             x = pos.x + col * (this.colWidth + this.padWidth);
35048             
35049             y = maxY[col];
35050             
35051             var positions = [];
35052             
35053             switch (box.length){
35054                 case 1 :
35055                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35056                     break;
35057                 case 2 :
35058                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35059                     break;
35060                 case 3 :
35061                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35062                     break;
35063                 case 4 :
35064                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35065                     break;
35066                 default :
35067                     break;
35068             }
35069             
35070             Roo.each(box, function(b,kk){
35071                 
35072                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35073                 
35074                 var sz = b.el.getSize();
35075                 
35076                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35077                 
35078             }, this);
35079             
35080         }, this);
35081         
35082         var mY = 0;
35083         
35084         for (var i = 0; i < this.cols; i++){
35085             mY = Math.max(mY, maxY[i]);
35086         }
35087         
35088         this.el.setHeight(mY - pos.y);
35089         
35090     },
35091     
35092 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35093 //    {
35094 //        var pos = this.el.getBox(true);
35095 //        var x = pos.x;
35096 //        var y = pos.y;
35097 //        var maxX = pos.right;
35098 //        
35099 //        var maxHeight = 0;
35100 //        
35101 //        Roo.each(items, function(item, k){
35102 //            
35103 //            var c = k % 2;
35104 //            
35105 //            item.el.position('absolute');
35106 //                
35107 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35108 //
35109 //            item.el.setWidth(width);
35110 //
35111 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35112 //
35113 //            item.el.setHeight(height);
35114 //            
35115 //            if(c == 0){
35116 //                item.el.setXY([x, y], isInstant ? false : true);
35117 //            } else {
35118 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35119 //            }
35120 //            
35121 //            y = y + height + this.alternativePadWidth;
35122 //            
35123 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35124 //            
35125 //        }, this);
35126 //        
35127 //        this.el.setHeight(maxHeight);
35128 //        
35129 //    },
35130     
35131     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35132     {
35133         var pos = this.el.getBox(true);
35134         
35135         var minX = pos.x;
35136         var minY = pos.y;
35137         
35138         var maxX = pos.right;
35139         
35140         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35141         
35142         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35143         
35144         Roo.each(queue, function(box, k){
35145             
35146             Roo.each(box, function(b, kk){
35147                 
35148                 b.el.position('absolute');
35149                 
35150                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35151                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35152                 
35153                 if(b.size == 'md-left' || b.size == 'md-right'){
35154                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35155                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35156                 }
35157                 
35158                 b.el.setWidth(width);
35159                 b.el.setHeight(height);
35160                 
35161             }, this);
35162             
35163             if(!box.length){
35164                 return;
35165             }
35166             
35167             var positions = [];
35168             
35169             switch (box.length){
35170                 case 1 :
35171                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35172                     break;
35173                 case 2 :
35174                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35175                     break;
35176                 case 3 :
35177                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35178                     break;
35179                 case 4 :
35180                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35181                     break;
35182                 default :
35183                     break;
35184             }
35185             
35186             Roo.each(box, function(b,kk){
35187                 
35188                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35189                 
35190                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35191                 
35192             }, this);
35193             
35194         }, this);
35195         
35196     },
35197     
35198     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35199     {
35200         Roo.each(eItems, function(b,k){
35201             
35202             b.size = (k == 0) ? 'sm' : 'xs';
35203             b.x = (k == 0) ? 2 : 1;
35204             b.y = (k == 0) ? 2 : 1;
35205             
35206             b.el.position('absolute');
35207             
35208             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35209                 
35210             b.el.setWidth(width);
35211             
35212             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35213             
35214             b.el.setHeight(height);
35215             
35216         }, this);
35217
35218         var positions = [];
35219         
35220         positions.push({
35221             x : maxX - this.unitWidth * 2 - this.gutter,
35222             y : minY
35223         });
35224         
35225         positions.push({
35226             x : maxX - this.unitWidth,
35227             y : minY + (this.unitWidth + this.gutter) * 2
35228         });
35229         
35230         positions.push({
35231             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35232             y : minY
35233         });
35234         
35235         Roo.each(eItems, function(b,k){
35236             
35237             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35238
35239         }, this);
35240         
35241     },
35242     
35243     getVerticalOneBoxColPositions : function(x, y, box)
35244     {
35245         var pos = [];
35246         
35247         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35248         
35249         if(box[0].size == 'md-left'){
35250             rand = 0;
35251         }
35252         
35253         if(box[0].size == 'md-right'){
35254             rand = 1;
35255         }
35256         
35257         pos.push({
35258             x : x + (this.unitWidth + this.gutter) * rand,
35259             y : y
35260         });
35261         
35262         return pos;
35263     },
35264     
35265     getVerticalTwoBoxColPositions : function(x, y, box)
35266     {
35267         var pos = [];
35268         
35269         if(box[0].size == 'xs'){
35270             
35271             pos.push({
35272                 x : x,
35273                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35274             });
35275
35276             pos.push({
35277                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35278                 y : y
35279             });
35280             
35281             return pos;
35282             
35283         }
35284         
35285         pos.push({
35286             x : x,
35287             y : y
35288         });
35289
35290         pos.push({
35291             x : x + (this.unitWidth + this.gutter) * 2,
35292             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35293         });
35294         
35295         return pos;
35296         
35297     },
35298     
35299     getVerticalThreeBoxColPositions : function(x, y, box)
35300     {
35301         var pos = [];
35302         
35303         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35304             
35305             pos.push({
35306                 x : x,
35307                 y : y
35308             });
35309
35310             pos.push({
35311                 x : x + (this.unitWidth + this.gutter) * 1,
35312                 y : y
35313             });
35314             
35315             pos.push({
35316                 x : x + (this.unitWidth + this.gutter) * 2,
35317                 y : y
35318             });
35319             
35320             return pos;
35321             
35322         }
35323         
35324         if(box[0].size == 'xs' && box[1].size == 'xs'){
35325             
35326             pos.push({
35327                 x : x,
35328                 y : y
35329             });
35330
35331             pos.push({
35332                 x : x,
35333                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35334             });
35335             
35336             pos.push({
35337                 x : x + (this.unitWidth + this.gutter) * 1,
35338                 y : y
35339             });
35340             
35341             return pos;
35342             
35343         }
35344         
35345         pos.push({
35346             x : x,
35347             y : y
35348         });
35349
35350         pos.push({
35351             x : x + (this.unitWidth + this.gutter) * 2,
35352             y : y
35353         });
35354
35355         pos.push({
35356             x : x + (this.unitWidth + this.gutter) * 2,
35357             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35358         });
35359             
35360         return pos;
35361         
35362     },
35363     
35364     getVerticalFourBoxColPositions : function(x, y, box)
35365     {
35366         var pos = [];
35367         
35368         if(box[0].size == 'xs'){
35369             
35370             pos.push({
35371                 x : x,
35372                 y : y
35373             });
35374
35375             pos.push({
35376                 x : x,
35377                 y : y + (this.unitHeight + this.gutter) * 1
35378             });
35379             
35380             pos.push({
35381                 x : x,
35382                 y : y + (this.unitHeight + this.gutter) * 2
35383             });
35384             
35385             pos.push({
35386                 x : x + (this.unitWidth + this.gutter) * 1,
35387                 y : y
35388             });
35389             
35390             return pos;
35391             
35392         }
35393         
35394         pos.push({
35395             x : x,
35396             y : y
35397         });
35398
35399         pos.push({
35400             x : x + (this.unitWidth + this.gutter) * 2,
35401             y : y
35402         });
35403
35404         pos.push({
35405             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35406             y : y + (this.unitHeight + this.gutter) * 1
35407         });
35408
35409         pos.push({
35410             x : x + (this.unitWidth + this.gutter) * 2,
35411             y : y + (this.unitWidth + this.gutter) * 2
35412         });
35413
35414         return pos;
35415         
35416     },
35417     
35418     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35419     {
35420         var pos = [];
35421         
35422         if(box[0].size == 'md-left'){
35423             pos.push({
35424                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35425                 y : minY
35426             });
35427             
35428             return pos;
35429         }
35430         
35431         if(box[0].size == 'md-right'){
35432             pos.push({
35433                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35434                 y : minY + (this.unitWidth + this.gutter) * 1
35435             });
35436             
35437             return pos;
35438         }
35439         
35440         var rand = Math.floor(Math.random() * (4 - box[0].y));
35441         
35442         pos.push({
35443             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35444             y : minY + (this.unitWidth + this.gutter) * rand
35445         });
35446         
35447         return pos;
35448         
35449     },
35450     
35451     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35452     {
35453         var pos = [];
35454         
35455         if(box[0].size == 'xs'){
35456             
35457             pos.push({
35458                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35459                 y : minY
35460             });
35461
35462             pos.push({
35463                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35464                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35465             });
35466             
35467             return pos;
35468             
35469         }
35470         
35471         pos.push({
35472             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35473             y : minY
35474         });
35475
35476         pos.push({
35477             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35478             y : minY + (this.unitWidth + this.gutter) * 2
35479         });
35480         
35481         return pos;
35482         
35483     },
35484     
35485     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35486     {
35487         var pos = [];
35488         
35489         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35490             
35491             pos.push({
35492                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35493                 y : minY
35494             });
35495
35496             pos.push({
35497                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35498                 y : minY + (this.unitWidth + this.gutter) * 1
35499             });
35500             
35501             pos.push({
35502                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35503                 y : minY + (this.unitWidth + this.gutter) * 2
35504             });
35505             
35506             return pos;
35507             
35508         }
35509         
35510         if(box[0].size == 'xs' && box[1].size == 'xs'){
35511             
35512             pos.push({
35513                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35514                 y : minY
35515             });
35516
35517             pos.push({
35518                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35519                 y : minY
35520             });
35521             
35522             pos.push({
35523                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35524                 y : minY + (this.unitWidth + this.gutter) * 1
35525             });
35526             
35527             return pos;
35528             
35529         }
35530         
35531         pos.push({
35532             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35533             y : minY
35534         });
35535
35536         pos.push({
35537             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35538             y : minY + (this.unitWidth + this.gutter) * 2
35539         });
35540
35541         pos.push({
35542             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35543             y : minY + (this.unitWidth + this.gutter) * 2
35544         });
35545             
35546         return pos;
35547         
35548     },
35549     
35550     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35551     {
35552         var pos = [];
35553         
35554         if(box[0].size == 'xs'){
35555             
35556             pos.push({
35557                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35558                 y : minY
35559             });
35560
35561             pos.push({
35562                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35563                 y : minY
35564             });
35565             
35566             pos.push({
35567                 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),
35568                 y : minY
35569             });
35570             
35571             pos.push({
35572                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35573                 y : minY + (this.unitWidth + this.gutter) * 1
35574             });
35575             
35576             return pos;
35577             
35578         }
35579         
35580         pos.push({
35581             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35582             y : minY
35583         });
35584         
35585         pos.push({
35586             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35587             y : minY + (this.unitWidth + this.gutter) * 2
35588         });
35589         
35590         pos.push({
35591             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35592             y : minY + (this.unitWidth + this.gutter) * 2
35593         });
35594         
35595         pos.push({
35596             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),
35597             y : minY + (this.unitWidth + this.gutter) * 2
35598         });
35599
35600         return pos;
35601         
35602     },
35603     
35604     /**
35605     * remove a Masonry Brick
35606     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35607     */
35608     removeBrick : function(brick_id)
35609     {
35610         if (!brick_id) {
35611             return;
35612         }
35613         
35614         for (var i = 0; i<this.bricks.length; i++) {
35615             if (this.bricks[i].id == brick_id) {
35616                 this.bricks.splice(i,1);
35617                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35618                 this.initial();
35619             }
35620         }
35621     },
35622     
35623     /**
35624     * adds a Masonry Brick
35625     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35626     */
35627     addBrick : function(cfg)
35628     {
35629         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35630         //this.register(cn);
35631         cn.parentId = this.id;
35632         cn.render(this.el);
35633         return cn;
35634     },
35635     
35636     /**
35637     * register a Masonry Brick
35638     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35639     */
35640     
35641     register : function(brick)
35642     {
35643         this.bricks.push(brick);
35644         brick.masonryId = this.id;
35645     },
35646     
35647     /**
35648     * clear all the Masonry Brick
35649     */
35650     clearAll : function()
35651     {
35652         this.bricks = [];
35653         //this.getChildContainer().dom.innerHTML = "";
35654         this.el.dom.innerHTML = '';
35655     },
35656     
35657     getSelected : function()
35658     {
35659         if (!this.selectedBrick) {
35660             return false;
35661         }
35662         
35663         return this.selectedBrick;
35664     }
35665 });
35666
35667 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35668     
35669     groups: {},
35670      /**
35671     * register a Masonry Layout
35672     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35673     */
35674     
35675     register : function(layout)
35676     {
35677         this.groups[layout.id] = layout;
35678     },
35679     /**
35680     * fetch a  Masonry Layout based on the masonry layout ID
35681     * @param {string} the masonry layout to add
35682     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35683     */
35684     
35685     get: function(layout_id) {
35686         if (typeof(this.groups[layout_id]) == 'undefined') {
35687             return false;
35688         }
35689         return this.groups[layout_id] ;
35690     }
35691     
35692     
35693     
35694 });
35695
35696  
35697
35698  /**
35699  *
35700  * This is based on 
35701  * http://masonry.desandro.com
35702  *
35703  * The idea is to render all the bricks based on vertical width...
35704  *
35705  * The original code extends 'outlayer' - we might need to use that....
35706  * 
35707  */
35708
35709
35710 /**
35711  * @class Roo.bootstrap.LayoutMasonryAuto
35712  * @extends Roo.bootstrap.Component
35713  * Bootstrap Layout Masonry class
35714  * 
35715  * @constructor
35716  * Create a new Element
35717  * @param {Object} config The config object
35718  */
35719
35720 Roo.bootstrap.LayoutMasonryAuto = function(config){
35721     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35722 };
35723
35724 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35725     
35726       /**
35727      * @cfg {Boolean} isFitWidth  - resize the width..
35728      */   
35729     isFitWidth : false,  // options..
35730     /**
35731      * @cfg {Boolean} isOriginLeft = left align?
35732      */   
35733     isOriginLeft : true,
35734     /**
35735      * @cfg {Boolean} isOriginTop = top align?
35736      */   
35737     isOriginTop : false,
35738     /**
35739      * @cfg {Boolean} isLayoutInstant = no animation?
35740      */   
35741     isLayoutInstant : false, // needed?
35742     /**
35743      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35744      */   
35745     isResizingContainer : true,
35746     /**
35747      * @cfg {Number} columnWidth  width of the columns 
35748      */   
35749     
35750     columnWidth : 0,
35751     
35752     /**
35753      * @cfg {Number} maxCols maximum number of columns
35754      */   
35755     
35756     maxCols: 0,
35757     /**
35758      * @cfg {Number} padHeight padding below box..
35759      */   
35760     
35761     padHeight : 10, 
35762     
35763     /**
35764      * @cfg {Boolean} isAutoInitial defalut true
35765      */   
35766     
35767     isAutoInitial : true, 
35768     
35769     // private?
35770     gutter : 0,
35771     
35772     containerWidth: 0,
35773     initialColumnWidth : 0,
35774     currentSize : null,
35775     
35776     colYs : null, // array.
35777     maxY : 0,
35778     padWidth: 10,
35779     
35780     
35781     tag: 'div',
35782     cls: '',
35783     bricks: null, //CompositeElement
35784     cols : 0, // array?
35785     // element : null, // wrapped now this.el
35786     _isLayoutInited : null, 
35787     
35788     
35789     getAutoCreate : function(){
35790         
35791         var cfg = {
35792             tag: this.tag,
35793             cls: 'blog-masonary-wrapper ' + this.cls,
35794             cn : {
35795                 cls : 'mas-boxes masonary'
35796             }
35797         };
35798         
35799         return cfg;
35800     },
35801     
35802     getChildContainer: function( )
35803     {
35804         if (this.boxesEl) {
35805             return this.boxesEl;
35806         }
35807         
35808         this.boxesEl = this.el.select('.mas-boxes').first();
35809         
35810         return this.boxesEl;
35811     },
35812     
35813     
35814     initEvents : function()
35815     {
35816         var _this = this;
35817         
35818         if(this.isAutoInitial){
35819             Roo.log('hook children rendered');
35820             this.on('childrenrendered', function() {
35821                 Roo.log('children rendered');
35822                 _this.initial();
35823             } ,this);
35824         }
35825         
35826     },
35827     
35828     initial : function()
35829     {
35830         this.reloadItems();
35831
35832         this.currentSize = this.el.getBox(true);
35833
35834         /// was window resize... - let's see if this works..
35835         Roo.EventManager.onWindowResize(this.resize, this); 
35836
35837         if(!this.isAutoInitial){
35838             this.layout();
35839             return;
35840         }
35841         
35842         this.layout.defer(500,this);
35843     },
35844     
35845     reloadItems: function()
35846     {
35847         this.bricks = this.el.select('.masonry-brick', true);
35848         
35849         this.bricks.each(function(b) {
35850             //Roo.log(b.getSize());
35851             if (!b.attr('originalwidth')) {
35852                 b.attr('originalwidth',  b.getSize().width);
35853             }
35854             
35855         });
35856         
35857         Roo.log(this.bricks.elements.length);
35858     },
35859     
35860     resize : function()
35861     {
35862         Roo.log('resize');
35863         var cs = this.el.getBox(true);
35864         
35865         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35866             Roo.log("no change in with or X");
35867             return;
35868         }
35869         this.currentSize = cs;
35870         this.layout();
35871     },
35872     
35873     layout : function()
35874     {
35875          Roo.log('layout');
35876         this._resetLayout();
35877         //this._manageStamps();
35878       
35879         // don't animate first layout
35880         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35881         this.layoutItems( isInstant );
35882       
35883         // flag for initalized
35884         this._isLayoutInited = true;
35885     },
35886     
35887     layoutItems : function( isInstant )
35888     {
35889         //var items = this._getItemsForLayout( this.items );
35890         // original code supports filtering layout items.. we just ignore it..
35891         
35892         this._layoutItems( this.bricks , isInstant );
35893       
35894         this._postLayout();
35895     },
35896     _layoutItems : function ( items , isInstant)
35897     {
35898        //this.fireEvent( 'layout', this, items );
35899     
35900
35901         if ( !items || !items.elements.length ) {
35902           // no items, emit event with empty array
35903             return;
35904         }
35905
35906         var queue = [];
35907         items.each(function(item) {
35908             Roo.log("layout item");
35909             Roo.log(item);
35910             // get x/y object from method
35911             var position = this._getItemLayoutPosition( item );
35912             // enqueue
35913             position.item = item;
35914             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35915             queue.push( position );
35916         }, this);
35917       
35918         this._processLayoutQueue( queue );
35919     },
35920     /** Sets position of item in DOM
35921     * @param {Element} item
35922     * @param {Number} x - horizontal position
35923     * @param {Number} y - vertical position
35924     * @param {Boolean} isInstant - disables transitions
35925     */
35926     _processLayoutQueue : function( queue )
35927     {
35928         for ( var i=0, len = queue.length; i < len; i++ ) {
35929             var obj = queue[i];
35930             obj.item.position('absolute');
35931             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35932         }
35933     },
35934       
35935     
35936     /**
35937     * Any logic you want to do after each layout,
35938     * i.e. size the container
35939     */
35940     _postLayout : function()
35941     {
35942         this.resizeContainer();
35943     },
35944     
35945     resizeContainer : function()
35946     {
35947         if ( !this.isResizingContainer ) {
35948             return;
35949         }
35950         var size = this._getContainerSize();
35951         if ( size ) {
35952             this.el.setSize(size.width,size.height);
35953             this.boxesEl.setSize(size.width,size.height);
35954         }
35955     },
35956     
35957     
35958     
35959     _resetLayout : function()
35960     {
35961         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35962         this.colWidth = this.el.getWidth();
35963         //this.gutter = this.el.getWidth(); 
35964         
35965         this.measureColumns();
35966
35967         // reset column Y
35968         var i = this.cols;
35969         this.colYs = [];
35970         while (i--) {
35971             this.colYs.push( 0 );
35972         }
35973     
35974         this.maxY = 0;
35975     },
35976
35977     measureColumns : function()
35978     {
35979         this.getContainerWidth();
35980       // if columnWidth is 0, default to outerWidth of first item
35981         if ( !this.columnWidth ) {
35982             var firstItem = this.bricks.first();
35983             Roo.log(firstItem);
35984             this.columnWidth  = this.containerWidth;
35985             if (firstItem && firstItem.attr('originalwidth') ) {
35986                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35987             }
35988             // columnWidth fall back to item of first element
35989             Roo.log("set column width?");
35990                         this.initialColumnWidth = this.columnWidth  ;
35991
35992             // if first elem has no width, default to size of container
35993             
35994         }
35995         
35996         
35997         if (this.initialColumnWidth) {
35998             this.columnWidth = this.initialColumnWidth;
35999         }
36000         
36001         
36002             
36003         // column width is fixed at the top - however if container width get's smaller we should
36004         // reduce it...
36005         
36006         // this bit calcs how man columns..
36007             
36008         var columnWidth = this.columnWidth += this.gutter;
36009       
36010         // calculate columns
36011         var containerWidth = this.containerWidth + this.gutter;
36012         
36013         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36014         // fix rounding errors, typically with gutters
36015         var excess = columnWidth - containerWidth % columnWidth;
36016         
36017         
36018         // if overshoot is less than a pixel, round up, otherwise floor it
36019         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36020         cols = Math[ mathMethod ]( cols );
36021         this.cols = Math.max( cols, 1 );
36022         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36023         
36024          // padding positioning..
36025         var totalColWidth = this.cols * this.columnWidth;
36026         var padavail = this.containerWidth - totalColWidth;
36027         // so for 2 columns - we need 3 'pads'
36028         
36029         var padNeeded = (1+this.cols) * this.padWidth;
36030         
36031         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36032         
36033         this.columnWidth += padExtra
36034         //this.padWidth = Math.floor(padavail /  ( this.cols));
36035         
36036         // adjust colum width so that padding is fixed??
36037         
36038         // we have 3 columns ... total = width * 3
36039         // we have X left over... that should be used by 
36040         
36041         //if (this.expandC) {
36042             
36043         //}
36044         
36045         
36046         
36047     },
36048     
36049     getContainerWidth : function()
36050     {
36051        /* // container is parent if fit width
36052         var container = this.isFitWidth ? this.element.parentNode : this.element;
36053         // check that this.size and size are there
36054         // IE8 triggers resize on body size change, so they might not be
36055         
36056         var size = getSize( container );  //FIXME
36057         this.containerWidth = size && size.innerWidth; //FIXME
36058         */
36059          
36060         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36061         
36062     },
36063     
36064     _getItemLayoutPosition : function( item )  // what is item?
36065     {
36066         // we resize the item to our columnWidth..
36067       
36068         item.setWidth(this.columnWidth);
36069         item.autoBoxAdjust  = false;
36070         
36071         var sz = item.getSize();
36072  
36073         // how many columns does this brick span
36074         var remainder = this.containerWidth % this.columnWidth;
36075         
36076         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36077         // round if off by 1 pixel, otherwise use ceil
36078         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36079         colSpan = Math.min( colSpan, this.cols );
36080         
36081         // normally this should be '1' as we dont' currently allow multi width columns..
36082         
36083         var colGroup = this._getColGroup( colSpan );
36084         // get the minimum Y value from the columns
36085         var minimumY = Math.min.apply( Math, colGroup );
36086         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36087         
36088         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36089          
36090         // position the brick
36091         var position = {
36092             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36093             y: this.currentSize.y + minimumY + this.padHeight
36094         };
36095         
36096         Roo.log(position);
36097         // apply setHeight to necessary columns
36098         var setHeight = minimumY + sz.height + this.padHeight;
36099         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36100         
36101         var setSpan = this.cols + 1 - colGroup.length;
36102         for ( var i = 0; i < setSpan; i++ ) {
36103           this.colYs[ shortColIndex + i ] = setHeight ;
36104         }
36105       
36106         return position;
36107     },
36108     
36109     /**
36110      * @param {Number} colSpan - number of columns the element spans
36111      * @returns {Array} colGroup
36112      */
36113     _getColGroup : function( colSpan )
36114     {
36115         if ( colSpan < 2 ) {
36116           // if brick spans only one column, use all the column Ys
36117           return this.colYs;
36118         }
36119       
36120         var colGroup = [];
36121         // how many different places could this brick fit horizontally
36122         var groupCount = this.cols + 1 - colSpan;
36123         // for each group potential horizontal position
36124         for ( var i = 0; i < groupCount; i++ ) {
36125           // make an array of colY values for that one group
36126           var groupColYs = this.colYs.slice( i, i + colSpan );
36127           // and get the max value of the array
36128           colGroup[i] = Math.max.apply( Math, groupColYs );
36129         }
36130         return colGroup;
36131     },
36132     /*
36133     _manageStamp : function( stamp )
36134     {
36135         var stampSize =  stamp.getSize();
36136         var offset = stamp.getBox();
36137         // get the columns that this stamp affects
36138         var firstX = this.isOriginLeft ? offset.x : offset.right;
36139         var lastX = firstX + stampSize.width;
36140         var firstCol = Math.floor( firstX / this.columnWidth );
36141         firstCol = Math.max( 0, firstCol );
36142         
36143         var lastCol = Math.floor( lastX / this.columnWidth );
36144         // lastCol should not go over if multiple of columnWidth #425
36145         lastCol -= lastX % this.columnWidth ? 0 : 1;
36146         lastCol = Math.min( this.cols - 1, lastCol );
36147         
36148         // set colYs to bottom of the stamp
36149         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36150             stampSize.height;
36151             
36152         for ( var i = firstCol; i <= lastCol; i++ ) {
36153           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36154         }
36155     },
36156     */
36157     
36158     _getContainerSize : function()
36159     {
36160         this.maxY = Math.max.apply( Math, this.colYs );
36161         var size = {
36162             height: this.maxY
36163         };
36164       
36165         if ( this.isFitWidth ) {
36166             size.width = this._getContainerFitWidth();
36167         }
36168       
36169         return size;
36170     },
36171     
36172     _getContainerFitWidth : function()
36173     {
36174         var unusedCols = 0;
36175         // count unused columns
36176         var i = this.cols;
36177         while ( --i ) {
36178           if ( this.colYs[i] !== 0 ) {
36179             break;
36180           }
36181           unusedCols++;
36182         }
36183         // fit container to columns that have been used
36184         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36185     },
36186     
36187     needsResizeLayout : function()
36188     {
36189         var previousWidth = this.containerWidth;
36190         this.getContainerWidth();
36191         return previousWidth !== this.containerWidth;
36192     }
36193  
36194 });
36195
36196  
36197
36198  /*
36199  * - LGPL
36200  *
36201  * element
36202  * 
36203  */
36204
36205 /**
36206  * @class Roo.bootstrap.MasonryBrick
36207  * @extends Roo.bootstrap.Component
36208  * Bootstrap MasonryBrick class
36209  * 
36210  * @constructor
36211  * Create a new MasonryBrick
36212  * @param {Object} config The config object
36213  */
36214
36215 Roo.bootstrap.MasonryBrick = function(config){
36216     
36217     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36218     
36219     Roo.bootstrap.MasonryBrick.register(this);
36220     
36221     this.addEvents({
36222         // raw events
36223         /**
36224          * @event click
36225          * When a MasonryBrick is clcik
36226          * @param {Roo.bootstrap.MasonryBrick} this
36227          * @param {Roo.EventObject} e
36228          */
36229         "click" : true
36230     });
36231 };
36232
36233 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36234     
36235     /**
36236      * @cfg {String} title
36237      */   
36238     title : '',
36239     /**
36240      * @cfg {String} html
36241      */   
36242     html : '',
36243     /**
36244      * @cfg {String} bgimage
36245      */   
36246     bgimage : '',
36247     /**
36248      * @cfg {String} videourl
36249      */   
36250     videourl : '',
36251     /**
36252      * @cfg {String} cls
36253      */   
36254     cls : '',
36255     /**
36256      * @cfg {String} href
36257      */   
36258     href : '',
36259     /**
36260      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36261      */   
36262     size : 'xs',
36263     
36264     /**
36265      * @cfg {String} placetitle (center|bottom)
36266      */   
36267     placetitle : '',
36268     
36269     /**
36270      * @cfg {Boolean} isFitContainer defalut true
36271      */   
36272     isFitContainer : true, 
36273     
36274     /**
36275      * @cfg {Boolean} preventDefault defalut false
36276      */   
36277     preventDefault : false, 
36278     
36279     /**
36280      * @cfg {Boolean} inverse defalut false
36281      */   
36282     maskInverse : false, 
36283     
36284     getAutoCreate : function()
36285     {
36286         if(!this.isFitContainer){
36287             return this.getSplitAutoCreate();
36288         }
36289         
36290         var cls = 'masonry-brick masonry-brick-full';
36291         
36292         if(this.href.length){
36293             cls += ' masonry-brick-link';
36294         }
36295         
36296         if(this.bgimage.length){
36297             cls += ' masonry-brick-image';
36298         }
36299         
36300         if(this.maskInverse){
36301             cls += ' mask-inverse';
36302         }
36303         
36304         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36305             cls += ' enable-mask';
36306         }
36307         
36308         if(this.size){
36309             cls += ' masonry-' + this.size + '-brick';
36310         }
36311         
36312         if(this.placetitle.length){
36313             
36314             switch (this.placetitle) {
36315                 case 'center' :
36316                     cls += ' masonry-center-title';
36317                     break;
36318                 case 'bottom' :
36319                     cls += ' masonry-bottom-title';
36320                     break;
36321                 default:
36322                     break;
36323             }
36324             
36325         } else {
36326             if(!this.html.length && !this.bgimage.length){
36327                 cls += ' masonry-center-title';
36328             }
36329
36330             if(!this.html.length && this.bgimage.length){
36331                 cls += ' masonry-bottom-title';
36332             }
36333         }
36334         
36335         if(this.cls){
36336             cls += ' ' + this.cls;
36337         }
36338         
36339         var cfg = {
36340             tag: (this.href.length) ? 'a' : 'div',
36341             cls: cls,
36342             cn: [
36343                 {
36344                     tag: 'div',
36345                     cls: 'masonry-brick-mask'
36346                 },
36347                 {
36348                     tag: 'div',
36349                     cls: 'masonry-brick-paragraph',
36350                     cn: []
36351                 }
36352             ]
36353         };
36354         
36355         if(this.href.length){
36356             cfg.href = this.href;
36357         }
36358         
36359         var cn = cfg.cn[1].cn;
36360         
36361         if(this.title.length){
36362             cn.push({
36363                 tag: 'h4',
36364                 cls: 'masonry-brick-title',
36365                 html: this.title
36366             });
36367         }
36368         
36369         if(this.html.length){
36370             cn.push({
36371                 tag: 'p',
36372                 cls: 'masonry-brick-text',
36373                 html: this.html
36374             });
36375         }
36376         
36377         if (!this.title.length && !this.html.length) {
36378             cfg.cn[1].cls += ' hide';
36379         }
36380         
36381         if(this.bgimage.length){
36382             cfg.cn.push({
36383                 tag: 'img',
36384                 cls: 'masonry-brick-image-view',
36385                 src: this.bgimage
36386             });
36387         }
36388         
36389         if(this.videourl.length){
36390             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36391             // youtube support only?
36392             cfg.cn.push({
36393                 tag: 'iframe',
36394                 cls: 'masonry-brick-image-view',
36395                 src: vurl,
36396                 frameborder : 0,
36397                 allowfullscreen : true
36398             });
36399         }
36400         
36401         return cfg;
36402         
36403     },
36404     
36405     getSplitAutoCreate : function()
36406     {
36407         var cls = 'masonry-brick masonry-brick-split';
36408         
36409         if(this.href.length){
36410             cls += ' masonry-brick-link';
36411         }
36412         
36413         if(this.bgimage.length){
36414             cls += ' masonry-brick-image';
36415         }
36416         
36417         if(this.size){
36418             cls += ' masonry-' + this.size + '-brick';
36419         }
36420         
36421         switch (this.placetitle) {
36422             case 'center' :
36423                 cls += ' masonry-center-title';
36424                 break;
36425             case 'bottom' :
36426                 cls += ' masonry-bottom-title';
36427                 break;
36428             default:
36429                 if(!this.bgimage.length){
36430                     cls += ' masonry-center-title';
36431                 }
36432
36433                 if(this.bgimage.length){
36434                     cls += ' masonry-bottom-title';
36435                 }
36436                 break;
36437         }
36438         
36439         if(this.cls){
36440             cls += ' ' + this.cls;
36441         }
36442         
36443         var cfg = {
36444             tag: (this.href.length) ? 'a' : 'div',
36445             cls: cls,
36446             cn: [
36447                 {
36448                     tag: 'div',
36449                     cls: 'masonry-brick-split-head',
36450                     cn: [
36451                         {
36452                             tag: 'div',
36453                             cls: 'masonry-brick-paragraph',
36454                             cn: []
36455                         }
36456                     ]
36457                 },
36458                 {
36459                     tag: 'div',
36460                     cls: 'masonry-brick-split-body',
36461                     cn: []
36462                 }
36463             ]
36464         };
36465         
36466         if(this.href.length){
36467             cfg.href = this.href;
36468         }
36469         
36470         if(this.title.length){
36471             cfg.cn[0].cn[0].cn.push({
36472                 tag: 'h4',
36473                 cls: 'masonry-brick-title',
36474                 html: this.title
36475             });
36476         }
36477         
36478         if(this.html.length){
36479             cfg.cn[1].cn.push({
36480                 tag: 'p',
36481                 cls: 'masonry-brick-text',
36482                 html: this.html
36483             });
36484         }
36485
36486         if(this.bgimage.length){
36487             cfg.cn[0].cn.push({
36488                 tag: 'img',
36489                 cls: 'masonry-brick-image-view',
36490                 src: this.bgimage
36491             });
36492         }
36493         
36494         if(this.videourl.length){
36495             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36496             // youtube support only?
36497             cfg.cn[0].cn.cn.push({
36498                 tag: 'iframe',
36499                 cls: 'masonry-brick-image-view',
36500                 src: vurl,
36501                 frameborder : 0,
36502                 allowfullscreen : true
36503             });
36504         }
36505         
36506         return cfg;
36507     },
36508     
36509     initEvents: function() 
36510     {
36511         switch (this.size) {
36512             case 'xs' :
36513                 this.x = 1;
36514                 this.y = 1;
36515                 break;
36516             case 'sm' :
36517                 this.x = 2;
36518                 this.y = 2;
36519                 break;
36520             case 'md' :
36521             case 'md-left' :
36522             case 'md-right' :
36523                 this.x = 3;
36524                 this.y = 3;
36525                 break;
36526             case 'tall' :
36527                 this.x = 2;
36528                 this.y = 3;
36529                 break;
36530             case 'wide' :
36531                 this.x = 3;
36532                 this.y = 2;
36533                 break;
36534             case 'wide-thin' :
36535                 this.x = 3;
36536                 this.y = 1;
36537                 break;
36538                         
36539             default :
36540                 break;
36541         }
36542         
36543         if(Roo.isTouch){
36544             this.el.on('touchstart', this.onTouchStart, this);
36545             this.el.on('touchmove', this.onTouchMove, this);
36546             this.el.on('touchend', this.onTouchEnd, this);
36547             this.el.on('contextmenu', this.onContextMenu, this);
36548         } else {
36549             this.el.on('mouseenter'  ,this.enter, this);
36550             this.el.on('mouseleave', this.leave, this);
36551             this.el.on('click', this.onClick, this);
36552         }
36553         
36554         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36555             this.parent().bricks.push(this);   
36556         }
36557         
36558     },
36559     
36560     onClick: function(e, el)
36561     {
36562         var time = this.endTimer - this.startTimer;
36563         // Roo.log(e.preventDefault());
36564         if(Roo.isTouch){
36565             if(time > 1000){
36566                 e.preventDefault();
36567                 return;
36568             }
36569         }
36570         
36571         if(!this.preventDefault){
36572             return;
36573         }
36574         
36575         e.preventDefault();
36576         
36577         if (this.activeClass != '') {
36578             this.selectBrick();
36579         }
36580         
36581         this.fireEvent('click', this, e);
36582     },
36583     
36584     enter: function(e, el)
36585     {
36586         e.preventDefault();
36587         
36588         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36589             return;
36590         }
36591         
36592         if(this.bgimage.length && this.html.length){
36593             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36594         }
36595     },
36596     
36597     leave: function(e, el)
36598     {
36599         e.preventDefault();
36600         
36601         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36602             return;
36603         }
36604         
36605         if(this.bgimage.length && this.html.length){
36606             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36607         }
36608     },
36609     
36610     onTouchStart: function(e, el)
36611     {
36612 //        e.preventDefault();
36613         
36614         this.touchmoved = false;
36615         
36616         if(!this.isFitContainer){
36617             return;
36618         }
36619         
36620         if(!this.bgimage.length || !this.html.length){
36621             return;
36622         }
36623         
36624         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36625         
36626         this.timer = new Date().getTime();
36627         
36628     },
36629     
36630     onTouchMove: function(e, el)
36631     {
36632         this.touchmoved = true;
36633     },
36634     
36635     onContextMenu : function(e,el)
36636     {
36637         e.preventDefault();
36638         e.stopPropagation();
36639         return false;
36640     },
36641     
36642     onTouchEnd: function(e, el)
36643     {
36644 //        e.preventDefault();
36645         
36646         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36647         
36648             this.leave(e,el);
36649             
36650             return;
36651         }
36652         
36653         if(!this.bgimage.length || !this.html.length){
36654             
36655             if(this.href.length){
36656                 window.location.href = this.href;
36657             }
36658             
36659             return;
36660         }
36661         
36662         if(!this.isFitContainer){
36663             return;
36664         }
36665         
36666         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36667         
36668         window.location.href = this.href;
36669     },
36670     
36671     //selection on single brick only
36672     selectBrick : function() {
36673         
36674         if (!this.parentId) {
36675             return;
36676         }
36677         
36678         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36679         var index = m.selectedBrick.indexOf(this.id);
36680         
36681         if ( index > -1) {
36682             m.selectedBrick.splice(index,1);
36683             this.el.removeClass(this.activeClass);
36684             return;
36685         }
36686         
36687         for(var i = 0; i < m.selectedBrick.length; i++) {
36688             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36689             b.el.removeClass(b.activeClass);
36690         }
36691         
36692         m.selectedBrick = [];
36693         
36694         m.selectedBrick.push(this.id);
36695         this.el.addClass(this.activeClass);
36696         return;
36697     },
36698     
36699     isSelected : function(){
36700         return this.el.hasClass(this.activeClass);
36701         
36702     }
36703 });
36704
36705 Roo.apply(Roo.bootstrap.MasonryBrick, {
36706     
36707     //groups: {},
36708     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36709      /**
36710     * register a Masonry Brick
36711     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36712     */
36713     
36714     register : function(brick)
36715     {
36716         //this.groups[brick.id] = brick;
36717         this.groups.add(brick.id, brick);
36718     },
36719     /**
36720     * fetch a  masonry brick based on the masonry brick ID
36721     * @param {string} the masonry brick to add
36722     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36723     */
36724     
36725     get: function(brick_id) 
36726     {
36727         // if (typeof(this.groups[brick_id]) == 'undefined') {
36728         //     return false;
36729         // }
36730         // return this.groups[brick_id] ;
36731         
36732         if(this.groups.key(brick_id)) {
36733             return this.groups.key(brick_id);
36734         }
36735         
36736         return false;
36737     }
36738     
36739     
36740     
36741 });
36742
36743  /*
36744  * - LGPL
36745  *
36746  * element
36747  * 
36748  */
36749
36750 /**
36751  * @class Roo.bootstrap.Brick
36752  * @extends Roo.bootstrap.Component
36753  * Bootstrap Brick class
36754  * 
36755  * @constructor
36756  * Create a new Brick
36757  * @param {Object} config The config object
36758  */
36759
36760 Roo.bootstrap.Brick = function(config){
36761     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36762     
36763     this.addEvents({
36764         // raw events
36765         /**
36766          * @event click
36767          * When a Brick is click
36768          * @param {Roo.bootstrap.Brick} this
36769          * @param {Roo.EventObject} e
36770          */
36771         "click" : true
36772     });
36773 };
36774
36775 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36776     
36777     /**
36778      * @cfg {String} title
36779      */   
36780     title : '',
36781     /**
36782      * @cfg {String} html
36783      */   
36784     html : '',
36785     /**
36786      * @cfg {String} bgimage
36787      */   
36788     bgimage : '',
36789     /**
36790      * @cfg {String} cls
36791      */   
36792     cls : '',
36793     /**
36794      * @cfg {String} href
36795      */   
36796     href : '',
36797     /**
36798      * @cfg {String} video
36799      */   
36800     video : '',
36801     /**
36802      * @cfg {Boolean} square
36803      */   
36804     square : true,
36805     
36806     getAutoCreate : function()
36807     {
36808         var cls = 'roo-brick';
36809         
36810         if(this.href.length){
36811             cls += ' roo-brick-link';
36812         }
36813         
36814         if(this.bgimage.length){
36815             cls += ' roo-brick-image';
36816         }
36817         
36818         if(!this.html.length && !this.bgimage.length){
36819             cls += ' roo-brick-center-title';
36820         }
36821         
36822         if(!this.html.length && this.bgimage.length){
36823             cls += ' roo-brick-bottom-title';
36824         }
36825         
36826         if(this.cls){
36827             cls += ' ' + this.cls;
36828         }
36829         
36830         var cfg = {
36831             tag: (this.href.length) ? 'a' : 'div',
36832             cls: cls,
36833             cn: [
36834                 {
36835                     tag: 'div',
36836                     cls: 'roo-brick-paragraph',
36837                     cn: []
36838                 }
36839             ]
36840         };
36841         
36842         if(this.href.length){
36843             cfg.href = this.href;
36844         }
36845         
36846         var cn = cfg.cn[0].cn;
36847         
36848         if(this.title.length){
36849             cn.push({
36850                 tag: 'h4',
36851                 cls: 'roo-brick-title',
36852                 html: this.title
36853             });
36854         }
36855         
36856         if(this.html.length){
36857             cn.push({
36858                 tag: 'p',
36859                 cls: 'roo-brick-text',
36860                 html: this.html
36861             });
36862         } else {
36863             cn.cls += ' hide';
36864         }
36865         
36866         if(this.bgimage.length){
36867             cfg.cn.push({
36868                 tag: 'img',
36869                 cls: 'roo-brick-image-view',
36870                 src: this.bgimage
36871             });
36872         }
36873         
36874         return cfg;
36875     },
36876     
36877     initEvents: function() 
36878     {
36879         if(this.title.length || this.html.length){
36880             this.el.on('mouseenter'  ,this.enter, this);
36881             this.el.on('mouseleave', this.leave, this);
36882         }
36883         
36884         Roo.EventManager.onWindowResize(this.resize, this); 
36885         
36886         if(this.bgimage.length){
36887             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36888             this.imageEl.on('load', this.onImageLoad, this);
36889             return;
36890         }
36891         
36892         this.resize();
36893     },
36894     
36895     onImageLoad : function()
36896     {
36897         this.resize();
36898     },
36899     
36900     resize : function()
36901     {
36902         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36903         
36904         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36905         
36906         if(this.bgimage.length){
36907             var image = this.el.select('.roo-brick-image-view', true).first();
36908             
36909             image.setWidth(paragraph.getWidth());
36910             
36911             if(this.square){
36912                 image.setHeight(paragraph.getWidth());
36913             }
36914             
36915             this.el.setHeight(image.getHeight());
36916             paragraph.setHeight(image.getHeight());
36917             
36918         }
36919         
36920     },
36921     
36922     enter: function(e, el)
36923     {
36924         e.preventDefault();
36925         
36926         if(this.bgimage.length){
36927             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36928             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36929         }
36930     },
36931     
36932     leave: function(e, el)
36933     {
36934         e.preventDefault();
36935         
36936         if(this.bgimage.length){
36937             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36938             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36939         }
36940     }
36941     
36942 });
36943
36944  
36945
36946  /*
36947  * - LGPL
36948  *
36949  * Number field 
36950  */
36951
36952 /**
36953  * @class Roo.bootstrap.NumberField
36954  * @extends Roo.bootstrap.Input
36955  * Bootstrap NumberField class
36956  * 
36957  * 
36958  * 
36959  * 
36960  * @constructor
36961  * Create a new NumberField
36962  * @param {Object} config The config object
36963  */
36964
36965 Roo.bootstrap.NumberField = function(config){
36966     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36967 };
36968
36969 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36970     
36971     /**
36972      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36973      */
36974     allowDecimals : true,
36975     /**
36976      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36977      */
36978     decimalSeparator : ".",
36979     /**
36980      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36981      */
36982     decimalPrecision : 2,
36983     /**
36984      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36985      */
36986     allowNegative : true,
36987     
36988     /**
36989      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36990      */
36991     allowZero: true,
36992     /**
36993      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36994      */
36995     minValue : Number.NEGATIVE_INFINITY,
36996     /**
36997      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36998      */
36999     maxValue : Number.MAX_VALUE,
37000     /**
37001      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37002      */
37003     minText : "The minimum value for this field is {0}",
37004     /**
37005      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37006      */
37007     maxText : "The maximum value for this field is {0}",
37008     /**
37009      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37010      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37011      */
37012     nanText : "{0} is not a valid number",
37013     /**
37014      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37015      */
37016     thousandsDelimiter : false,
37017     /**
37018      * @cfg {String} valueAlign alignment of value
37019      */
37020     valueAlign : "left",
37021
37022     getAutoCreate : function()
37023     {
37024         var hiddenInput = {
37025             tag: 'input',
37026             type: 'hidden',
37027             id: Roo.id(),
37028             cls: 'hidden-number-input'
37029         };
37030         
37031         if (this.name) {
37032             hiddenInput.name = this.name;
37033         }
37034         
37035         this.name = '';
37036         
37037         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37038         
37039         this.name = hiddenInput.name;
37040         
37041         if(cfg.cn.length > 0) {
37042             cfg.cn.push(hiddenInput);
37043         }
37044         
37045         return cfg;
37046     },
37047
37048     // private
37049     initEvents : function()
37050     {   
37051         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37052         
37053         var allowed = "0123456789";
37054         
37055         if(this.allowDecimals){
37056             allowed += this.decimalSeparator;
37057         }
37058         
37059         if(this.allowNegative){
37060             allowed += "-";
37061         }
37062         
37063         if(this.thousandsDelimiter) {
37064             allowed += ",";
37065         }
37066         
37067         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37068         
37069         var keyPress = function(e){
37070             
37071             var k = e.getKey();
37072             
37073             var c = e.getCharCode();
37074             
37075             if(
37076                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37077                     allowed.indexOf(String.fromCharCode(c)) === -1
37078             ){
37079                 e.stopEvent();
37080                 return;
37081             }
37082             
37083             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37084                 return;
37085             }
37086             
37087             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37088                 e.stopEvent();
37089             }
37090         };
37091         
37092         this.el.on("keypress", keyPress, this);
37093     },
37094     
37095     validateValue : function(value)
37096     {
37097         
37098         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37099             return false;
37100         }
37101         
37102         var num = this.parseValue(value);
37103         
37104         if(isNaN(num)){
37105             this.markInvalid(String.format(this.nanText, value));
37106             return false;
37107         }
37108         
37109         if(num < this.minValue){
37110             this.markInvalid(String.format(this.minText, this.minValue));
37111             return false;
37112         }
37113         
37114         if(num > this.maxValue){
37115             this.markInvalid(String.format(this.maxText, this.maxValue));
37116             return false;
37117         }
37118         
37119         return true;
37120     },
37121
37122     getValue : function()
37123     {
37124         var v = this.hiddenEl().getValue();
37125         
37126         return this.fixPrecision(this.parseValue(v));
37127     },
37128
37129     parseValue : function(value)
37130     {
37131         if(this.thousandsDelimiter) {
37132             value += "";
37133             r = new RegExp(",", "g");
37134             value = value.replace(r, "");
37135         }
37136         
37137         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37138         return isNaN(value) ? '' : value;
37139     },
37140
37141     fixPrecision : function(value)
37142     {
37143         if(this.thousandsDelimiter) {
37144             value += "";
37145             r = new RegExp(",", "g");
37146             value = value.replace(r, "");
37147         }
37148         
37149         var nan = isNaN(value);
37150         
37151         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37152             return nan ? '' : value;
37153         }
37154         return parseFloat(value).toFixed(this.decimalPrecision);
37155     },
37156
37157     setValue : function(v)
37158     {
37159         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37160         
37161         this.value = v;
37162         
37163         if(this.rendered){
37164             
37165             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37166             
37167             this.inputEl().dom.value = (v == '') ? '' :
37168                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37169             
37170             if(!this.allowZero && v === '0') {
37171                 this.hiddenEl().dom.value = '';
37172                 this.inputEl().dom.value = '';
37173             }
37174             
37175             this.validate();
37176         }
37177     },
37178
37179     decimalPrecisionFcn : function(v)
37180     {
37181         return Math.floor(v);
37182     },
37183
37184     beforeBlur : function()
37185     {
37186         var v = this.parseValue(this.getRawValue());
37187         
37188         if(v || v === 0 || v === ''){
37189             this.setValue(v);
37190         }
37191     },
37192     
37193     hiddenEl : function()
37194     {
37195         return this.el.select('input.hidden-number-input',true).first();
37196     }
37197     
37198 });
37199
37200  
37201
37202 /*
37203 * Licence: LGPL
37204 */
37205
37206 /**
37207  * @class Roo.bootstrap.DocumentSlider
37208  * @extends Roo.bootstrap.Component
37209  * Bootstrap DocumentSlider class
37210  * 
37211  * @constructor
37212  * Create a new DocumentViewer
37213  * @param {Object} config The config object
37214  */
37215
37216 Roo.bootstrap.DocumentSlider = function(config){
37217     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37218     
37219     this.files = [];
37220     
37221     this.addEvents({
37222         /**
37223          * @event initial
37224          * Fire after initEvent
37225          * @param {Roo.bootstrap.DocumentSlider} this
37226          */
37227         "initial" : true,
37228         /**
37229          * @event update
37230          * Fire after update
37231          * @param {Roo.bootstrap.DocumentSlider} this
37232          */
37233         "update" : true,
37234         /**
37235          * @event click
37236          * Fire after click
37237          * @param {Roo.bootstrap.DocumentSlider} this
37238          */
37239         "click" : true
37240     });
37241 };
37242
37243 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37244     
37245     files : false,
37246     
37247     indicator : 0,
37248     
37249     getAutoCreate : function()
37250     {
37251         var cfg = {
37252             tag : 'div',
37253             cls : 'roo-document-slider',
37254             cn : [
37255                 {
37256                     tag : 'div',
37257                     cls : 'roo-document-slider-header',
37258                     cn : [
37259                         {
37260                             tag : 'div',
37261                             cls : 'roo-document-slider-header-title'
37262                         }
37263                     ]
37264                 },
37265                 {
37266                     tag : 'div',
37267                     cls : 'roo-document-slider-body',
37268                     cn : [
37269                         {
37270                             tag : 'div',
37271                             cls : 'roo-document-slider-prev',
37272                             cn : [
37273                                 {
37274                                     tag : 'i',
37275                                     cls : 'fa fa-chevron-left'
37276                                 }
37277                             ]
37278                         },
37279                         {
37280                             tag : 'div',
37281                             cls : 'roo-document-slider-thumb',
37282                             cn : [
37283                                 {
37284                                     tag : 'img',
37285                                     cls : 'roo-document-slider-image'
37286                                 }
37287                             ]
37288                         },
37289                         {
37290                             tag : 'div',
37291                             cls : 'roo-document-slider-next',
37292                             cn : [
37293                                 {
37294                                     tag : 'i',
37295                                     cls : 'fa fa-chevron-right'
37296                                 }
37297                             ]
37298                         }
37299                     ]
37300                 }
37301             ]
37302         };
37303         
37304         return cfg;
37305     },
37306     
37307     initEvents : function()
37308     {
37309         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37310         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37311         
37312         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37313         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37314         
37315         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37316         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37317         
37318         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37319         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37320         
37321         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37322         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37323         
37324         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37325         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37326         
37327         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37328         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37329         
37330         this.thumbEl.on('click', this.onClick, this);
37331         
37332         this.prevIndicator.on('click', this.prev, this);
37333         
37334         this.nextIndicator.on('click', this.next, this);
37335         
37336     },
37337     
37338     initial : function()
37339     {
37340         if(this.files.length){
37341             this.indicator = 1;
37342             this.update()
37343         }
37344         
37345         this.fireEvent('initial', this);
37346     },
37347     
37348     update : function()
37349     {
37350         this.imageEl.attr('src', this.files[this.indicator - 1]);
37351         
37352         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37353         
37354         this.prevIndicator.show();
37355         
37356         if(this.indicator == 1){
37357             this.prevIndicator.hide();
37358         }
37359         
37360         this.nextIndicator.show();
37361         
37362         if(this.indicator == this.files.length){
37363             this.nextIndicator.hide();
37364         }
37365         
37366         this.thumbEl.scrollTo('top');
37367         
37368         this.fireEvent('update', this);
37369     },
37370     
37371     onClick : function(e)
37372     {
37373         e.preventDefault();
37374         
37375         this.fireEvent('click', this);
37376     },
37377     
37378     prev : function(e)
37379     {
37380         e.preventDefault();
37381         
37382         this.indicator = Math.max(1, this.indicator - 1);
37383         
37384         this.update();
37385     },
37386     
37387     next : function(e)
37388     {
37389         e.preventDefault();
37390         
37391         this.indicator = Math.min(this.files.length, this.indicator + 1);
37392         
37393         this.update();
37394     }
37395 });
37396 /*
37397  * - LGPL
37398  *
37399  * RadioSet
37400  *
37401  *
37402  */
37403
37404 /**
37405  * @class Roo.bootstrap.RadioSet
37406  * @extends Roo.bootstrap.Input
37407  * Bootstrap RadioSet class
37408  * @cfg {String} indicatorpos (left|right) default left
37409  * @cfg {Boolean} inline (true|false) inline the element (default true)
37410  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37411  * @constructor
37412  * Create a new RadioSet
37413  * @param {Object} config The config object
37414  */
37415
37416 Roo.bootstrap.RadioSet = function(config){
37417     
37418     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37419     
37420     this.radioes = [];
37421     
37422     Roo.bootstrap.RadioSet.register(this);
37423     
37424     this.addEvents({
37425         /**
37426         * @event check
37427         * Fires when the element is checked or unchecked.
37428         * @param {Roo.bootstrap.RadioSet} this This radio
37429         * @param {Roo.bootstrap.Radio} item The checked item
37430         */
37431        check : true,
37432        /**
37433         * @event click
37434         * Fires when the element is click.
37435         * @param {Roo.bootstrap.RadioSet} this This radio set
37436         * @param {Roo.bootstrap.Radio} item The checked item
37437         * @param {Roo.EventObject} e The event object
37438         */
37439        click : true
37440     });
37441     
37442 };
37443
37444 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37445
37446     radioes : false,
37447     
37448     inline : true,
37449     
37450     weight : '',
37451     
37452     indicatorpos : 'left',
37453     
37454     getAutoCreate : function()
37455     {
37456         var label = {
37457             tag : 'label',
37458             cls : 'roo-radio-set-label',
37459             cn : [
37460                 {
37461                     tag : 'span',
37462                     html : this.fieldLabel
37463                 }
37464             ]
37465         };
37466         if (Roo.bootstrap.version == 3) {
37467             
37468             
37469             if(this.indicatorpos == 'left'){
37470                 label.cn.unshift({
37471                     tag : 'i',
37472                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37473                     tooltip : 'This field is required'
37474                 });
37475             } else {
37476                 label.cn.push({
37477                     tag : 'i',
37478                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37479                     tooltip : 'This field is required'
37480                 });
37481             }
37482         }
37483         var items = {
37484             tag : 'div',
37485             cls : 'roo-radio-set-items'
37486         };
37487         
37488         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37489         
37490         if (align === 'left' && this.fieldLabel.length) {
37491             
37492             items = {
37493                 cls : "roo-radio-set-right", 
37494                 cn: [
37495                     items
37496                 ]
37497             };
37498             
37499             if(this.labelWidth > 12){
37500                 label.style = "width: " + this.labelWidth + 'px';
37501             }
37502             
37503             if(this.labelWidth < 13 && this.labelmd == 0){
37504                 this.labelmd = this.labelWidth;
37505             }
37506             
37507             if(this.labellg > 0){
37508                 label.cls += ' col-lg-' + this.labellg;
37509                 items.cls += ' col-lg-' + (12 - this.labellg);
37510             }
37511             
37512             if(this.labelmd > 0){
37513                 label.cls += ' col-md-' + this.labelmd;
37514                 items.cls += ' col-md-' + (12 - this.labelmd);
37515             }
37516             
37517             if(this.labelsm > 0){
37518                 label.cls += ' col-sm-' + this.labelsm;
37519                 items.cls += ' col-sm-' + (12 - this.labelsm);
37520             }
37521             
37522             if(this.labelxs > 0){
37523                 label.cls += ' col-xs-' + this.labelxs;
37524                 items.cls += ' col-xs-' + (12 - this.labelxs);
37525             }
37526         }
37527         
37528         var cfg = {
37529             tag : 'div',
37530             cls : 'roo-radio-set',
37531             cn : [
37532                 {
37533                     tag : 'input',
37534                     cls : 'roo-radio-set-input',
37535                     type : 'hidden',
37536                     name : this.name,
37537                     value : this.value ? this.value :  ''
37538                 },
37539                 label,
37540                 items
37541             ]
37542         };
37543         
37544         if(this.weight.length){
37545             cfg.cls += ' roo-radio-' + this.weight;
37546         }
37547         
37548         if(this.inline) {
37549             cfg.cls += ' roo-radio-set-inline';
37550         }
37551         
37552         var settings=this;
37553         ['xs','sm','md','lg'].map(function(size){
37554             if (settings[size]) {
37555                 cfg.cls += ' col-' + size + '-' + settings[size];
37556             }
37557         });
37558         
37559         return cfg;
37560         
37561     },
37562
37563     initEvents : function()
37564     {
37565         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37566         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37567         
37568         if(!this.fieldLabel.length){
37569             this.labelEl.hide();
37570         }
37571         
37572         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37573         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37574         
37575         this.indicator = this.indicatorEl();
37576         
37577         if(this.indicator){
37578             this.indicator.addClass('invisible');
37579         }
37580         
37581         this.originalValue = this.getValue();
37582         
37583     },
37584     
37585     inputEl: function ()
37586     {
37587         return this.el.select('.roo-radio-set-input', true).first();
37588     },
37589     
37590     getChildContainer : function()
37591     {
37592         return this.itemsEl;
37593     },
37594     
37595     register : function(item)
37596     {
37597         this.radioes.push(item);
37598         
37599     },
37600     
37601     validate : function()
37602     {   
37603         if(this.getVisibilityEl().hasClass('hidden')){
37604             return true;
37605         }
37606         
37607         var valid = false;
37608         
37609         Roo.each(this.radioes, function(i){
37610             if(!i.checked){
37611                 return;
37612             }
37613             
37614             valid = true;
37615             return false;
37616         });
37617         
37618         if(this.allowBlank) {
37619             return true;
37620         }
37621         
37622         if(this.disabled || valid){
37623             this.markValid();
37624             return true;
37625         }
37626         
37627         this.markInvalid();
37628         return false;
37629         
37630     },
37631     
37632     markValid : function()
37633     {
37634         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37635             this.indicatorEl().removeClass('visible');
37636             this.indicatorEl().addClass('invisible');
37637         }
37638         
37639         
37640         if (Roo.bootstrap.version == 3) {
37641             this.el.removeClass([this.invalidClass, this.validClass]);
37642             this.el.addClass(this.validClass);
37643         } else {
37644             this.el.removeClass(['is-invalid','is-valid']);
37645             this.el.addClass(['is-valid']);
37646         }
37647         this.fireEvent('valid', this);
37648     },
37649     
37650     markInvalid : function(msg)
37651     {
37652         if(this.allowBlank || this.disabled){
37653             return;
37654         }
37655         
37656         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37657             this.indicatorEl().removeClass('invisible');
37658             this.indicatorEl().addClass('visible');
37659         }
37660         if (Roo.bootstrap.version == 3) {
37661             this.el.removeClass([this.invalidClass, this.validClass]);
37662             this.el.addClass(this.invalidClass);
37663         } else {
37664             this.el.removeClass(['is-invalid','is-valid']);
37665             this.el.addClass(['is-invalid']);
37666         }
37667         
37668         this.fireEvent('invalid', this, msg);
37669         
37670     },
37671     
37672     setValue : function(v, suppressEvent)
37673     {   
37674         if(this.value === v){
37675             return;
37676         }
37677         
37678         this.value = v;
37679         
37680         if(this.rendered){
37681             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37682         }
37683         
37684         Roo.each(this.radioes, function(i){
37685             i.checked = false;
37686             i.el.removeClass('checked');
37687         });
37688         
37689         Roo.each(this.radioes, function(i){
37690             
37691             if(i.value === v || i.value.toString() === v.toString()){
37692                 i.checked = true;
37693                 i.el.addClass('checked');
37694                 
37695                 if(suppressEvent !== true){
37696                     this.fireEvent('check', this, i);
37697                 }
37698                 
37699                 return false;
37700             }
37701             
37702         }, this);
37703         
37704         this.validate();
37705     },
37706     
37707     clearInvalid : function(){
37708         
37709         if(!this.el || this.preventMark){
37710             return;
37711         }
37712         
37713         this.el.removeClass([this.invalidClass]);
37714         
37715         this.fireEvent('valid', this);
37716     }
37717     
37718 });
37719
37720 Roo.apply(Roo.bootstrap.RadioSet, {
37721     
37722     groups: {},
37723     
37724     register : function(set)
37725     {
37726         this.groups[set.name] = set;
37727     },
37728     
37729     get: function(name) 
37730     {
37731         if (typeof(this.groups[name]) == 'undefined') {
37732             return false;
37733         }
37734         
37735         return this.groups[name] ;
37736     }
37737     
37738 });
37739 /*
37740  * Based on:
37741  * Ext JS Library 1.1.1
37742  * Copyright(c) 2006-2007, Ext JS, LLC.
37743  *
37744  * Originally Released Under LGPL - original licence link has changed is not relivant.
37745  *
37746  * Fork - LGPL
37747  * <script type="text/javascript">
37748  */
37749
37750
37751 /**
37752  * @class Roo.bootstrap.SplitBar
37753  * @extends Roo.util.Observable
37754  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37755  * <br><br>
37756  * Usage:
37757  * <pre><code>
37758 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37759                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37760 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37761 split.minSize = 100;
37762 split.maxSize = 600;
37763 split.animate = true;
37764 split.on('moved', splitterMoved);
37765 </code></pre>
37766  * @constructor
37767  * Create a new SplitBar
37768  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37769  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37770  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37771  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37772                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37773                         position of the SplitBar).
37774  */
37775 Roo.bootstrap.SplitBar = function(cfg){
37776     
37777     /** @private */
37778     
37779     //{
37780     //  dragElement : elm
37781     //  resizingElement: el,
37782         // optional..
37783     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37784     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37785         // existingProxy ???
37786     //}
37787     
37788     this.el = Roo.get(cfg.dragElement, true);
37789     this.el.dom.unselectable = "on";
37790     /** @private */
37791     this.resizingEl = Roo.get(cfg.resizingElement, true);
37792
37793     /**
37794      * @private
37795      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37796      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37797      * @type Number
37798      */
37799     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37800     
37801     /**
37802      * The minimum size of the resizing element. (Defaults to 0)
37803      * @type Number
37804      */
37805     this.minSize = 0;
37806     
37807     /**
37808      * The maximum size of the resizing element. (Defaults to 2000)
37809      * @type Number
37810      */
37811     this.maxSize = 2000;
37812     
37813     /**
37814      * Whether to animate the transition to the new size
37815      * @type Boolean
37816      */
37817     this.animate = false;
37818     
37819     /**
37820      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37821      * @type Boolean
37822      */
37823     this.useShim = false;
37824     
37825     /** @private */
37826     this.shim = null;
37827     
37828     if(!cfg.existingProxy){
37829         /** @private */
37830         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37831     }else{
37832         this.proxy = Roo.get(cfg.existingProxy).dom;
37833     }
37834     /** @private */
37835     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37836     
37837     /** @private */
37838     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37839     
37840     /** @private */
37841     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37842     
37843     /** @private */
37844     this.dragSpecs = {};
37845     
37846     /**
37847      * @private The adapter to use to positon and resize elements
37848      */
37849     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37850     this.adapter.init(this);
37851     
37852     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37853         /** @private */
37854         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37855         this.el.addClass("roo-splitbar-h");
37856     }else{
37857         /** @private */
37858         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37859         this.el.addClass("roo-splitbar-v");
37860     }
37861     
37862     this.addEvents({
37863         /**
37864          * @event resize
37865          * Fires when the splitter is moved (alias for {@link #event-moved})
37866          * @param {Roo.bootstrap.SplitBar} this
37867          * @param {Number} newSize the new width or height
37868          */
37869         "resize" : true,
37870         /**
37871          * @event moved
37872          * Fires when the splitter is moved
37873          * @param {Roo.bootstrap.SplitBar} this
37874          * @param {Number} newSize the new width or height
37875          */
37876         "moved" : true,
37877         /**
37878          * @event beforeresize
37879          * Fires before the splitter is dragged
37880          * @param {Roo.bootstrap.SplitBar} this
37881          */
37882         "beforeresize" : true,
37883
37884         "beforeapply" : true
37885     });
37886
37887     Roo.util.Observable.call(this);
37888 };
37889
37890 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37891     onStartProxyDrag : function(x, y){
37892         this.fireEvent("beforeresize", this);
37893         if(!this.overlay){
37894             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37895             o.unselectable();
37896             o.enableDisplayMode("block");
37897             // all splitbars share the same overlay
37898             Roo.bootstrap.SplitBar.prototype.overlay = o;
37899         }
37900         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37901         this.overlay.show();
37902         Roo.get(this.proxy).setDisplayed("block");
37903         var size = this.adapter.getElementSize(this);
37904         this.activeMinSize = this.getMinimumSize();;
37905         this.activeMaxSize = this.getMaximumSize();;
37906         var c1 = size - this.activeMinSize;
37907         var c2 = Math.max(this.activeMaxSize - size, 0);
37908         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37909             this.dd.resetConstraints();
37910             this.dd.setXConstraint(
37911                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37912                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37913             );
37914             this.dd.setYConstraint(0, 0);
37915         }else{
37916             this.dd.resetConstraints();
37917             this.dd.setXConstraint(0, 0);
37918             this.dd.setYConstraint(
37919                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37920                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37921             );
37922          }
37923         this.dragSpecs.startSize = size;
37924         this.dragSpecs.startPoint = [x, y];
37925         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37926     },
37927     
37928     /** 
37929      * @private Called after the drag operation by the DDProxy
37930      */
37931     onEndProxyDrag : function(e){
37932         Roo.get(this.proxy).setDisplayed(false);
37933         var endPoint = Roo.lib.Event.getXY(e);
37934         if(this.overlay){
37935             this.overlay.hide();
37936         }
37937         var newSize;
37938         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37939             newSize = this.dragSpecs.startSize + 
37940                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37941                     endPoint[0] - this.dragSpecs.startPoint[0] :
37942                     this.dragSpecs.startPoint[0] - endPoint[0]
37943                 );
37944         }else{
37945             newSize = this.dragSpecs.startSize + 
37946                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37947                     endPoint[1] - this.dragSpecs.startPoint[1] :
37948                     this.dragSpecs.startPoint[1] - endPoint[1]
37949                 );
37950         }
37951         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37952         if(newSize != this.dragSpecs.startSize){
37953             if(this.fireEvent('beforeapply', this, newSize) !== false){
37954                 this.adapter.setElementSize(this, newSize);
37955                 this.fireEvent("moved", this, newSize);
37956                 this.fireEvent("resize", this, newSize);
37957             }
37958         }
37959     },
37960     
37961     /**
37962      * Get the adapter this SplitBar uses
37963      * @return The adapter object
37964      */
37965     getAdapter : function(){
37966         return this.adapter;
37967     },
37968     
37969     /**
37970      * Set the adapter this SplitBar uses
37971      * @param {Object} adapter A SplitBar adapter object
37972      */
37973     setAdapter : function(adapter){
37974         this.adapter = adapter;
37975         this.adapter.init(this);
37976     },
37977     
37978     /**
37979      * Gets the minimum size for the resizing element
37980      * @return {Number} The minimum size
37981      */
37982     getMinimumSize : function(){
37983         return this.minSize;
37984     },
37985     
37986     /**
37987      * Sets the minimum size for the resizing element
37988      * @param {Number} minSize The minimum size
37989      */
37990     setMinimumSize : function(minSize){
37991         this.minSize = minSize;
37992     },
37993     
37994     /**
37995      * Gets the maximum size for the resizing element
37996      * @return {Number} The maximum size
37997      */
37998     getMaximumSize : function(){
37999         return this.maxSize;
38000     },
38001     
38002     /**
38003      * Sets the maximum size for the resizing element
38004      * @param {Number} maxSize The maximum size
38005      */
38006     setMaximumSize : function(maxSize){
38007         this.maxSize = maxSize;
38008     },
38009     
38010     /**
38011      * Sets the initialize size for the resizing element
38012      * @param {Number} size The initial size
38013      */
38014     setCurrentSize : function(size){
38015         var oldAnimate = this.animate;
38016         this.animate = false;
38017         this.adapter.setElementSize(this, size);
38018         this.animate = oldAnimate;
38019     },
38020     
38021     /**
38022      * Destroy this splitbar. 
38023      * @param {Boolean} removeEl True to remove the element
38024      */
38025     destroy : function(removeEl){
38026         if(this.shim){
38027             this.shim.remove();
38028         }
38029         this.dd.unreg();
38030         this.proxy.parentNode.removeChild(this.proxy);
38031         if(removeEl){
38032             this.el.remove();
38033         }
38034     }
38035 });
38036
38037 /**
38038  * @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.
38039  */
38040 Roo.bootstrap.SplitBar.createProxy = function(dir){
38041     var proxy = new Roo.Element(document.createElement("div"));
38042     proxy.unselectable();
38043     var cls = 'roo-splitbar-proxy';
38044     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38045     document.body.appendChild(proxy.dom);
38046     return proxy.dom;
38047 };
38048
38049 /** 
38050  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38051  * Default Adapter. It assumes the splitter and resizing element are not positioned
38052  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38053  */
38054 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38055 };
38056
38057 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38058     // do nothing for now
38059     init : function(s){
38060     
38061     },
38062     /**
38063      * Called before drag operations to get the current size of the resizing element. 
38064      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38065      */
38066      getElementSize : function(s){
38067         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38068             return s.resizingEl.getWidth();
38069         }else{
38070             return s.resizingEl.getHeight();
38071         }
38072     },
38073     
38074     /**
38075      * Called after drag operations to set the size of the resizing element.
38076      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38077      * @param {Number} newSize The new size to set
38078      * @param {Function} onComplete A function to be invoked when resizing is complete
38079      */
38080     setElementSize : function(s, newSize, onComplete){
38081         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38082             if(!s.animate){
38083                 s.resizingEl.setWidth(newSize);
38084                 if(onComplete){
38085                     onComplete(s, newSize);
38086                 }
38087             }else{
38088                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38089             }
38090         }else{
38091             
38092             if(!s.animate){
38093                 s.resizingEl.setHeight(newSize);
38094                 if(onComplete){
38095                     onComplete(s, newSize);
38096                 }
38097             }else{
38098                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38099             }
38100         }
38101     }
38102 };
38103
38104 /** 
38105  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38106  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38107  * Adapter that  moves the splitter element to align with the resized sizing element. 
38108  * Used with an absolute positioned SplitBar.
38109  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38110  * document.body, make sure you assign an id to the body element.
38111  */
38112 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38113     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38114     this.container = Roo.get(container);
38115 };
38116
38117 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38118     init : function(s){
38119         this.basic.init(s);
38120     },
38121     
38122     getElementSize : function(s){
38123         return this.basic.getElementSize(s);
38124     },
38125     
38126     setElementSize : function(s, newSize, onComplete){
38127         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38128     },
38129     
38130     moveSplitter : function(s){
38131         var yes = Roo.bootstrap.SplitBar;
38132         switch(s.placement){
38133             case yes.LEFT:
38134                 s.el.setX(s.resizingEl.getRight());
38135                 break;
38136             case yes.RIGHT:
38137                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38138                 break;
38139             case yes.TOP:
38140                 s.el.setY(s.resizingEl.getBottom());
38141                 break;
38142             case yes.BOTTOM:
38143                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38144                 break;
38145         }
38146     }
38147 };
38148
38149 /**
38150  * Orientation constant - Create a vertical SplitBar
38151  * @static
38152  * @type Number
38153  */
38154 Roo.bootstrap.SplitBar.VERTICAL = 1;
38155
38156 /**
38157  * Orientation constant - Create a horizontal SplitBar
38158  * @static
38159  * @type Number
38160  */
38161 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38162
38163 /**
38164  * Placement constant - The resizing element is to the left of the splitter element
38165  * @static
38166  * @type Number
38167  */
38168 Roo.bootstrap.SplitBar.LEFT = 1;
38169
38170 /**
38171  * Placement constant - The resizing element is to the right of the splitter element
38172  * @static
38173  * @type Number
38174  */
38175 Roo.bootstrap.SplitBar.RIGHT = 2;
38176
38177 /**
38178  * Placement constant - The resizing element is positioned above the splitter element
38179  * @static
38180  * @type Number
38181  */
38182 Roo.bootstrap.SplitBar.TOP = 3;
38183
38184 /**
38185  * Placement constant - The resizing element is positioned under splitter element
38186  * @static
38187  * @type Number
38188  */
38189 Roo.bootstrap.SplitBar.BOTTOM = 4;
38190 Roo.namespace("Roo.bootstrap.layout");/*
38191  * Based on:
38192  * Ext JS Library 1.1.1
38193  * Copyright(c) 2006-2007, Ext JS, LLC.
38194  *
38195  * Originally Released Under LGPL - original licence link has changed is not relivant.
38196  *
38197  * Fork - LGPL
38198  * <script type="text/javascript">
38199  */
38200
38201 /**
38202  * @class Roo.bootstrap.layout.Manager
38203  * @extends Roo.bootstrap.Component
38204  * Base class for layout managers.
38205  */
38206 Roo.bootstrap.layout.Manager = function(config)
38207 {
38208     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38209
38210
38211
38212
38213
38214     /** false to disable window resize monitoring @type Boolean */
38215     this.monitorWindowResize = true;
38216     this.regions = {};
38217     this.addEvents({
38218         /**
38219          * @event layout
38220          * Fires when a layout is performed.
38221          * @param {Roo.LayoutManager} this
38222          */
38223         "layout" : true,
38224         /**
38225          * @event regionresized
38226          * Fires when the user resizes a region.
38227          * @param {Roo.LayoutRegion} region The resized region
38228          * @param {Number} newSize The new size (width for east/west, height for north/south)
38229          */
38230         "regionresized" : true,
38231         /**
38232          * @event regioncollapsed
38233          * Fires when a region is collapsed.
38234          * @param {Roo.LayoutRegion} region The collapsed region
38235          */
38236         "regioncollapsed" : true,
38237         /**
38238          * @event regionexpanded
38239          * Fires when a region is expanded.
38240          * @param {Roo.LayoutRegion} region The expanded region
38241          */
38242         "regionexpanded" : true
38243     });
38244     this.updating = false;
38245
38246     if (config.el) {
38247         this.el = Roo.get(config.el);
38248         this.initEvents();
38249     }
38250
38251 };
38252
38253 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38254
38255
38256     regions : null,
38257
38258     monitorWindowResize : true,
38259
38260
38261     updating : false,
38262
38263
38264     onRender : function(ct, position)
38265     {
38266         if(!this.el){
38267             this.el = Roo.get(ct);
38268             this.initEvents();
38269         }
38270         //this.fireEvent('render',this);
38271     },
38272
38273
38274     initEvents: function()
38275     {
38276
38277
38278         // ie scrollbar fix
38279         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38280             document.body.scroll = "no";
38281         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38282             this.el.position('relative');
38283         }
38284         this.id = this.el.id;
38285         this.el.addClass("roo-layout-container");
38286         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38287         if(this.el.dom != document.body ) {
38288             this.el.on('resize', this.layout,this);
38289             this.el.on('show', this.layout,this);
38290         }
38291
38292     },
38293
38294     /**
38295      * Returns true if this layout is currently being updated
38296      * @return {Boolean}
38297      */
38298     isUpdating : function(){
38299         return this.updating;
38300     },
38301
38302     /**
38303      * Suspend the LayoutManager from doing auto-layouts while
38304      * making multiple add or remove calls
38305      */
38306     beginUpdate : function(){
38307         this.updating = true;
38308     },
38309
38310     /**
38311      * Restore auto-layouts and optionally disable the manager from performing a layout
38312      * @param {Boolean} noLayout true to disable a layout update
38313      */
38314     endUpdate : function(noLayout){
38315         this.updating = false;
38316         if(!noLayout){
38317             this.layout();
38318         }
38319     },
38320
38321     layout: function(){
38322         // abstract...
38323     },
38324
38325     onRegionResized : function(region, newSize){
38326         this.fireEvent("regionresized", region, newSize);
38327         this.layout();
38328     },
38329
38330     onRegionCollapsed : function(region){
38331         this.fireEvent("regioncollapsed", region);
38332     },
38333
38334     onRegionExpanded : function(region){
38335         this.fireEvent("regionexpanded", region);
38336     },
38337
38338     /**
38339      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38340      * performs box-model adjustments.
38341      * @return {Object} The size as an object {width: (the width), height: (the height)}
38342      */
38343     getViewSize : function()
38344     {
38345         var size;
38346         if(this.el.dom != document.body){
38347             size = this.el.getSize();
38348         }else{
38349             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38350         }
38351         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38352         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38353         return size;
38354     },
38355
38356     /**
38357      * Returns the Element this layout is bound to.
38358      * @return {Roo.Element}
38359      */
38360     getEl : function(){
38361         return this.el;
38362     },
38363
38364     /**
38365      * Returns the specified region.
38366      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38367      * @return {Roo.LayoutRegion}
38368      */
38369     getRegion : function(target){
38370         return this.regions[target.toLowerCase()];
38371     },
38372
38373     onWindowResize : function(){
38374         if(this.monitorWindowResize){
38375             this.layout();
38376         }
38377     }
38378 });
38379 /*
38380  * Based on:
38381  * Ext JS Library 1.1.1
38382  * Copyright(c) 2006-2007, Ext JS, LLC.
38383  *
38384  * Originally Released Under LGPL - original licence link has changed is not relivant.
38385  *
38386  * Fork - LGPL
38387  * <script type="text/javascript">
38388  */
38389 /**
38390  * @class Roo.bootstrap.layout.Border
38391  * @extends Roo.bootstrap.layout.Manager
38392  * @builder-top
38393  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38394  * please see: examples/bootstrap/nested.html<br><br>
38395  
38396 <b>The container the layout is rendered into can be either the body element or any other element.
38397 If it is not the body element, the container needs to either be an absolute positioned element,
38398 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38399 the container size if it is not the body element.</b>
38400
38401 * @constructor
38402 * Create a new Border
38403 * @param {Object} config Configuration options
38404  */
38405 Roo.bootstrap.layout.Border = function(config){
38406     config = config || {};
38407     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38408     
38409     
38410     
38411     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38412         if(config[region]){
38413             config[region].region = region;
38414             this.addRegion(config[region]);
38415         }
38416     },this);
38417     
38418 };
38419
38420 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38421
38422 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38423     
38424     parent : false, // this might point to a 'nest' or a ???
38425     
38426     /**
38427      * Creates and adds a new region if it doesn't already exist.
38428      * @param {String} target The target region key (north, south, east, west or center).
38429      * @param {Object} config The regions config object
38430      * @return {BorderLayoutRegion} The new region
38431      */
38432     addRegion : function(config)
38433     {
38434         if(!this.regions[config.region]){
38435             var r = this.factory(config);
38436             this.bindRegion(r);
38437         }
38438         return this.regions[config.region];
38439     },
38440
38441     // private (kinda)
38442     bindRegion : function(r){
38443         this.regions[r.config.region] = r;
38444         
38445         r.on("visibilitychange",    this.layout, this);
38446         r.on("paneladded",          this.layout, this);
38447         r.on("panelremoved",        this.layout, this);
38448         r.on("invalidated",         this.layout, this);
38449         r.on("resized",             this.onRegionResized, this);
38450         r.on("collapsed",           this.onRegionCollapsed, this);
38451         r.on("expanded",            this.onRegionExpanded, this);
38452     },
38453
38454     /**
38455      * Performs a layout update.
38456      */
38457     layout : function()
38458     {
38459         if(this.updating) {
38460             return;
38461         }
38462         
38463         // render all the rebions if they have not been done alreayd?
38464         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38465             if(this.regions[region] && !this.regions[region].bodyEl){
38466                 this.regions[region].onRender(this.el)
38467             }
38468         },this);
38469         
38470         var size = this.getViewSize();
38471         var w = size.width;
38472         var h = size.height;
38473         var centerW = w;
38474         var centerH = h;
38475         var centerY = 0;
38476         var centerX = 0;
38477         //var x = 0, y = 0;
38478
38479         var rs = this.regions;
38480         var north = rs["north"];
38481         var south = rs["south"]; 
38482         var west = rs["west"];
38483         var east = rs["east"];
38484         var center = rs["center"];
38485         //if(this.hideOnLayout){ // not supported anymore
38486             //c.el.setStyle("display", "none");
38487         //}
38488         if(north && north.isVisible()){
38489             var b = north.getBox();
38490             var m = north.getMargins();
38491             b.width = w - (m.left+m.right);
38492             b.x = m.left;
38493             b.y = m.top;
38494             centerY = b.height + b.y + m.bottom;
38495             centerH -= centerY;
38496             north.updateBox(this.safeBox(b));
38497         }
38498         if(south && south.isVisible()){
38499             var b = south.getBox();
38500             var m = south.getMargins();
38501             b.width = w - (m.left+m.right);
38502             b.x = m.left;
38503             var totalHeight = (b.height + m.top + m.bottom);
38504             b.y = h - totalHeight + m.top;
38505             centerH -= totalHeight;
38506             south.updateBox(this.safeBox(b));
38507         }
38508         if(west && west.isVisible()){
38509             var b = west.getBox();
38510             var m = west.getMargins();
38511             b.height = centerH - (m.top+m.bottom);
38512             b.x = m.left;
38513             b.y = centerY + m.top;
38514             var totalWidth = (b.width + m.left + m.right);
38515             centerX += totalWidth;
38516             centerW -= totalWidth;
38517             west.updateBox(this.safeBox(b));
38518         }
38519         if(east && east.isVisible()){
38520             var b = east.getBox();
38521             var m = east.getMargins();
38522             b.height = centerH - (m.top+m.bottom);
38523             var totalWidth = (b.width + m.left + m.right);
38524             b.x = w - totalWidth + m.left;
38525             b.y = centerY + m.top;
38526             centerW -= totalWidth;
38527             east.updateBox(this.safeBox(b));
38528         }
38529         if(center){
38530             var m = center.getMargins();
38531             var centerBox = {
38532                 x: centerX + m.left,
38533                 y: centerY + m.top,
38534                 width: centerW - (m.left+m.right),
38535                 height: centerH - (m.top+m.bottom)
38536             };
38537             //if(this.hideOnLayout){
38538                 //center.el.setStyle("display", "block");
38539             //}
38540             center.updateBox(this.safeBox(centerBox));
38541         }
38542         this.el.repaint();
38543         this.fireEvent("layout", this);
38544     },
38545
38546     // private
38547     safeBox : function(box){
38548         box.width = Math.max(0, box.width);
38549         box.height = Math.max(0, box.height);
38550         return box;
38551     },
38552
38553     /**
38554      * Adds a ContentPanel (or subclass) to this layout.
38555      * @param {String} target The target region key (north, south, east, west or center).
38556      * @param {Roo.ContentPanel} panel The panel to add
38557      * @return {Roo.ContentPanel} The added panel
38558      */
38559     add : function(target, panel){
38560          
38561         target = target.toLowerCase();
38562         return this.regions[target].add(panel);
38563     },
38564
38565     /**
38566      * Remove a ContentPanel (or subclass) to this layout.
38567      * @param {String} target The target region key (north, south, east, west or center).
38568      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38569      * @return {Roo.ContentPanel} The removed panel
38570      */
38571     remove : function(target, panel){
38572         target = target.toLowerCase();
38573         return this.regions[target].remove(panel);
38574     },
38575
38576     /**
38577      * Searches all regions for a panel with the specified id
38578      * @param {String} panelId
38579      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38580      */
38581     findPanel : function(panelId){
38582         var rs = this.regions;
38583         for(var target in rs){
38584             if(typeof rs[target] != "function"){
38585                 var p = rs[target].getPanel(panelId);
38586                 if(p){
38587                     return p;
38588                 }
38589             }
38590         }
38591         return null;
38592     },
38593
38594     /**
38595      * Searches all regions for a panel with the specified id and activates (shows) it.
38596      * @param {String/ContentPanel} panelId The panels id or the panel itself
38597      * @return {Roo.ContentPanel} The shown panel or null
38598      */
38599     showPanel : function(panelId) {
38600       var rs = this.regions;
38601       for(var target in rs){
38602          var r = rs[target];
38603          if(typeof r != "function"){
38604             if(r.hasPanel(panelId)){
38605                return r.showPanel(panelId);
38606             }
38607          }
38608       }
38609       return null;
38610    },
38611
38612    /**
38613      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38614      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38615      */
38616    /*
38617     restoreState : function(provider){
38618         if(!provider){
38619             provider = Roo.state.Manager;
38620         }
38621         var sm = new Roo.LayoutStateManager();
38622         sm.init(this, provider);
38623     },
38624 */
38625  
38626  
38627     /**
38628      * Adds a xtype elements to the layout.
38629      * <pre><code>
38630
38631 layout.addxtype({
38632        xtype : 'ContentPanel',
38633        region: 'west',
38634        items: [ .... ]
38635    }
38636 );
38637
38638 layout.addxtype({
38639         xtype : 'NestedLayoutPanel',
38640         region: 'west',
38641         layout: {
38642            center: { },
38643            west: { }   
38644         },
38645         items : [ ... list of content panels or nested layout panels.. ]
38646    }
38647 );
38648 </code></pre>
38649      * @param {Object} cfg Xtype definition of item to add.
38650      */
38651     addxtype : function(cfg)
38652     {
38653         // basically accepts a pannel...
38654         // can accept a layout region..!?!?
38655         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38656         
38657         
38658         // theory?  children can only be panels??
38659         
38660         //if (!cfg.xtype.match(/Panel$/)) {
38661         //    return false;
38662         //}
38663         var ret = false;
38664         
38665         if (typeof(cfg.region) == 'undefined') {
38666             Roo.log("Failed to add Panel, region was not set");
38667             Roo.log(cfg);
38668             return false;
38669         }
38670         var region = cfg.region;
38671         delete cfg.region;
38672         
38673           
38674         var xitems = [];
38675         if (cfg.items) {
38676             xitems = cfg.items;
38677             delete cfg.items;
38678         }
38679         var nb = false;
38680         
38681         if ( region == 'center') {
38682             Roo.log("Center: " + cfg.title);
38683         }
38684         
38685         
38686         switch(cfg.xtype) 
38687         {
38688             case 'Content':  // ContentPanel (el, cfg)
38689             case 'Scroll':  // ContentPanel (el, cfg)
38690             case 'View': 
38691                 cfg.autoCreate = cfg.autoCreate || true;
38692                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38693                 //} else {
38694                 //    var el = this.el.createChild();
38695                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38696                 //}
38697                 
38698                 this.add(region, ret);
38699                 break;
38700             
38701             /*
38702             case 'TreePanel': // our new panel!
38703                 cfg.el = this.el.createChild();
38704                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38705                 this.add(region, ret);
38706                 break;
38707             */
38708             
38709             case 'Nest': 
38710                 // create a new Layout (which is  a Border Layout...
38711                 
38712                 var clayout = cfg.layout;
38713                 clayout.el  = this.el.createChild();
38714                 clayout.items   = clayout.items  || [];
38715                 
38716                 delete cfg.layout;
38717                 
38718                 // replace this exitems with the clayout ones..
38719                 xitems = clayout.items;
38720                  
38721                 // force background off if it's in center...
38722                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38723                     cfg.background = false;
38724                 }
38725                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38726                 
38727                 
38728                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38729                 //console.log('adding nested layout panel '  + cfg.toSource());
38730                 this.add(region, ret);
38731                 nb = {}; /// find first...
38732                 break;
38733             
38734             case 'Grid':
38735                 
38736                 // needs grid and region
38737                 
38738                 //var el = this.getRegion(region).el.createChild();
38739                 /*
38740                  *var el = this.el.createChild();
38741                 // create the grid first...
38742                 cfg.grid.container = el;
38743                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38744                 */
38745                 
38746                 if (region == 'center' && this.active ) {
38747                     cfg.background = false;
38748                 }
38749                 
38750                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38751                 
38752                 this.add(region, ret);
38753                 /*
38754                 if (cfg.background) {
38755                     // render grid on panel activation (if panel background)
38756                     ret.on('activate', function(gp) {
38757                         if (!gp.grid.rendered) {
38758                     //        gp.grid.render(el);
38759                         }
38760                     });
38761                 } else {
38762                   //  cfg.grid.render(el);
38763                 }
38764                 */
38765                 break;
38766            
38767            
38768             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38769                 // it was the old xcomponent building that caused this before.
38770                 // espeically if border is the top element in the tree.
38771                 ret = this;
38772                 break; 
38773                 
38774                     
38775                 
38776                 
38777                 
38778             default:
38779                 /*
38780                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38781                     
38782                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38783                     this.add(region, ret);
38784                 } else {
38785                 */
38786                     Roo.log(cfg);
38787                     throw "Can not add '" + cfg.xtype + "' to Border";
38788                     return null;
38789              
38790                                 
38791              
38792         }
38793         this.beginUpdate();
38794         // add children..
38795         var region = '';
38796         var abn = {};
38797         Roo.each(xitems, function(i)  {
38798             region = nb && i.region ? i.region : false;
38799             
38800             var add = ret.addxtype(i);
38801            
38802             if (region) {
38803                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38804                 if (!i.background) {
38805                     abn[region] = nb[region] ;
38806                 }
38807             }
38808             
38809         });
38810         this.endUpdate();
38811
38812         // make the last non-background panel active..
38813         //if (nb) { Roo.log(abn); }
38814         if (nb) {
38815             
38816             for(var r in abn) {
38817                 region = this.getRegion(r);
38818                 if (region) {
38819                     // tried using nb[r], but it does not work..
38820                      
38821                     region.showPanel(abn[r]);
38822                    
38823                 }
38824             }
38825         }
38826         return ret;
38827         
38828     },
38829     
38830     
38831 // private
38832     factory : function(cfg)
38833     {
38834         
38835         var validRegions = Roo.bootstrap.layout.Border.regions;
38836
38837         var target = cfg.region;
38838         cfg.mgr = this;
38839         
38840         var r = Roo.bootstrap.layout;
38841         Roo.log(target);
38842         switch(target){
38843             case "north":
38844                 return new r.North(cfg);
38845             case "south":
38846                 return new r.South(cfg);
38847             case "east":
38848                 return new r.East(cfg);
38849             case "west":
38850                 return new r.West(cfg);
38851             case "center":
38852                 return new r.Center(cfg);
38853         }
38854         throw 'Layout region "'+target+'" not supported.';
38855     }
38856     
38857     
38858 });
38859  /*
38860  * Based on:
38861  * Ext JS Library 1.1.1
38862  * Copyright(c) 2006-2007, Ext JS, LLC.
38863  *
38864  * Originally Released Under LGPL - original licence link has changed is not relivant.
38865  *
38866  * Fork - LGPL
38867  * <script type="text/javascript">
38868  */
38869  
38870 /**
38871  * @class Roo.bootstrap.layout.Basic
38872  * @extends Roo.util.Observable
38873  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38874  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38875  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38876  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38877  * @cfg {string}   region  the region that it inhabits..
38878  * @cfg {bool}   skipConfig skip config?
38879  * 
38880
38881  */
38882 Roo.bootstrap.layout.Basic = function(config){
38883     
38884     this.mgr = config.mgr;
38885     
38886     this.position = config.region;
38887     
38888     var skipConfig = config.skipConfig;
38889     
38890     this.events = {
38891         /**
38892          * @scope Roo.BasicLayoutRegion
38893          */
38894         
38895         /**
38896          * @event beforeremove
38897          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38898          * @param {Roo.LayoutRegion} this
38899          * @param {Roo.ContentPanel} panel The panel
38900          * @param {Object} e The cancel event object
38901          */
38902         "beforeremove" : true,
38903         /**
38904          * @event invalidated
38905          * Fires when the layout for this region is changed.
38906          * @param {Roo.LayoutRegion} this
38907          */
38908         "invalidated" : true,
38909         /**
38910          * @event visibilitychange
38911          * Fires when this region is shown or hidden 
38912          * @param {Roo.LayoutRegion} this
38913          * @param {Boolean} visibility true or false
38914          */
38915         "visibilitychange" : true,
38916         /**
38917          * @event paneladded
38918          * Fires when a panel is added. 
38919          * @param {Roo.LayoutRegion} this
38920          * @param {Roo.ContentPanel} panel The panel
38921          */
38922         "paneladded" : true,
38923         /**
38924          * @event panelremoved
38925          * Fires when a panel is removed. 
38926          * @param {Roo.LayoutRegion} this
38927          * @param {Roo.ContentPanel} panel The panel
38928          */
38929         "panelremoved" : true,
38930         /**
38931          * @event beforecollapse
38932          * Fires when this region before collapse.
38933          * @param {Roo.LayoutRegion} this
38934          */
38935         "beforecollapse" : true,
38936         /**
38937          * @event collapsed
38938          * Fires when this region is collapsed.
38939          * @param {Roo.LayoutRegion} this
38940          */
38941         "collapsed" : true,
38942         /**
38943          * @event expanded
38944          * Fires when this region is expanded.
38945          * @param {Roo.LayoutRegion} this
38946          */
38947         "expanded" : true,
38948         /**
38949          * @event slideshow
38950          * Fires when this region is slid into view.
38951          * @param {Roo.LayoutRegion} this
38952          */
38953         "slideshow" : true,
38954         /**
38955          * @event slidehide
38956          * Fires when this region slides out of view. 
38957          * @param {Roo.LayoutRegion} this
38958          */
38959         "slidehide" : true,
38960         /**
38961          * @event panelactivated
38962          * Fires when a panel is activated. 
38963          * @param {Roo.LayoutRegion} this
38964          * @param {Roo.ContentPanel} panel The activated panel
38965          */
38966         "panelactivated" : true,
38967         /**
38968          * @event resized
38969          * Fires when the user resizes this region. 
38970          * @param {Roo.LayoutRegion} this
38971          * @param {Number} newSize The new size (width for east/west, height for north/south)
38972          */
38973         "resized" : true
38974     };
38975     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38976     this.panels = new Roo.util.MixedCollection();
38977     this.panels.getKey = this.getPanelId.createDelegate(this);
38978     this.box = null;
38979     this.activePanel = null;
38980     // ensure listeners are added...
38981     
38982     if (config.listeners || config.events) {
38983         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38984             listeners : config.listeners || {},
38985             events : config.events || {}
38986         });
38987     }
38988     
38989     if(skipConfig !== true){
38990         this.applyConfig(config);
38991     }
38992 };
38993
38994 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38995 {
38996     getPanelId : function(p){
38997         return p.getId();
38998     },
38999     
39000     applyConfig : function(config){
39001         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39002         this.config = config;
39003         
39004     },
39005     
39006     /**
39007      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39008      * the width, for horizontal (north, south) the height.
39009      * @param {Number} newSize The new width or height
39010      */
39011     resizeTo : function(newSize){
39012         var el = this.el ? this.el :
39013                  (this.activePanel ? this.activePanel.getEl() : null);
39014         if(el){
39015             switch(this.position){
39016                 case "east":
39017                 case "west":
39018                     el.setWidth(newSize);
39019                     this.fireEvent("resized", this, newSize);
39020                 break;
39021                 case "north":
39022                 case "south":
39023                     el.setHeight(newSize);
39024                     this.fireEvent("resized", this, newSize);
39025                 break;                
39026             }
39027         }
39028     },
39029     
39030     getBox : function(){
39031         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39032     },
39033     
39034     getMargins : function(){
39035         return this.margins;
39036     },
39037     
39038     updateBox : function(box){
39039         this.box = box;
39040         var el = this.activePanel.getEl();
39041         el.dom.style.left = box.x + "px";
39042         el.dom.style.top = box.y + "px";
39043         this.activePanel.setSize(box.width, box.height);
39044     },
39045     
39046     /**
39047      * Returns the container element for this region.
39048      * @return {Roo.Element}
39049      */
39050     getEl : function(){
39051         return this.activePanel;
39052     },
39053     
39054     /**
39055      * Returns true if this region is currently visible.
39056      * @return {Boolean}
39057      */
39058     isVisible : function(){
39059         return this.activePanel ? true : false;
39060     },
39061     
39062     setActivePanel : function(panel){
39063         panel = this.getPanel(panel);
39064         if(this.activePanel && this.activePanel != panel){
39065             this.activePanel.setActiveState(false);
39066             this.activePanel.getEl().setLeftTop(-10000,-10000);
39067         }
39068         this.activePanel = panel;
39069         panel.setActiveState(true);
39070         if(this.box){
39071             panel.setSize(this.box.width, this.box.height);
39072         }
39073         this.fireEvent("panelactivated", this, panel);
39074         this.fireEvent("invalidated");
39075     },
39076     
39077     /**
39078      * Show the specified panel.
39079      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39080      * @return {Roo.ContentPanel} The shown panel or null
39081      */
39082     showPanel : function(panel){
39083         panel = this.getPanel(panel);
39084         if(panel){
39085             this.setActivePanel(panel);
39086         }
39087         return panel;
39088     },
39089     
39090     /**
39091      * Get the active panel for this region.
39092      * @return {Roo.ContentPanel} The active panel or null
39093      */
39094     getActivePanel : function(){
39095         return this.activePanel;
39096     },
39097     
39098     /**
39099      * Add the passed ContentPanel(s)
39100      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39101      * @return {Roo.ContentPanel} The panel added (if only one was added)
39102      */
39103     add : function(panel){
39104         if(arguments.length > 1){
39105             for(var i = 0, len = arguments.length; i < len; i++) {
39106                 this.add(arguments[i]);
39107             }
39108             return null;
39109         }
39110         if(this.hasPanel(panel)){
39111             this.showPanel(panel);
39112             return panel;
39113         }
39114         var el = panel.getEl();
39115         if(el.dom.parentNode != this.mgr.el.dom){
39116             this.mgr.el.dom.appendChild(el.dom);
39117         }
39118         if(panel.setRegion){
39119             panel.setRegion(this);
39120         }
39121         this.panels.add(panel);
39122         el.setStyle("position", "absolute");
39123         if(!panel.background){
39124             this.setActivePanel(panel);
39125             if(this.config.initialSize && this.panels.getCount()==1){
39126                 this.resizeTo(this.config.initialSize);
39127             }
39128         }
39129         this.fireEvent("paneladded", this, panel);
39130         return panel;
39131     },
39132     
39133     /**
39134      * Returns true if the panel is in this region.
39135      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39136      * @return {Boolean}
39137      */
39138     hasPanel : function(panel){
39139         if(typeof panel == "object"){ // must be panel obj
39140             panel = panel.getId();
39141         }
39142         return this.getPanel(panel) ? true : false;
39143     },
39144     
39145     /**
39146      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39147      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39148      * @param {Boolean} preservePanel Overrides the config preservePanel option
39149      * @return {Roo.ContentPanel} The panel that was removed
39150      */
39151     remove : function(panel, preservePanel){
39152         panel = this.getPanel(panel);
39153         if(!panel){
39154             return null;
39155         }
39156         var e = {};
39157         this.fireEvent("beforeremove", this, panel, e);
39158         if(e.cancel === true){
39159             return null;
39160         }
39161         var panelId = panel.getId();
39162         this.panels.removeKey(panelId);
39163         return panel;
39164     },
39165     
39166     /**
39167      * Returns the panel specified or null if it's not in this region.
39168      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39169      * @return {Roo.ContentPanel}
39170      */
39171     getPanel : function(id){
39172         if(typeof id == "object"){ // must be panel obj
39173             return id;
39174         }
39175         return this.panels.get(id);
39176     },
39177     
39178     /**
39179      * Returns this regions position (north/south/east/west/center).
39180      * @return {String} 
39181      */
39182     getPosition: function(){
39183         return this.position;    
39184     }
39185 });/*
39186  * Based on:
39187  * Ext JS Library 1.1.1
39188  * Copyright(c) 2006-2007, Ext JS, LLC.
39189  *
39190  * Originally Released Under LGPL - original licence link has changed is not relivant.
39191  *
39192  * Fork - LGPL
39193  * <script type="text/javascript">
39194  */
39195  
39196 /**
39197  * @class Roo.bootstrap.layout.Region
39198  * @extends Roo.bootstrap.layout.Basic
39199  * This class represents a region in a layout manager.
39200  
39201  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39202  * @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})
39203  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39204  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39205  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39206  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39207  * @cfg {String}    title           The title for the region (overrides panel titles)
39208  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39209  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39210  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39211  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39212  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39213  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39214  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39215  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39216  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39217  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39218
39219  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39220  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39221  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39222  * @cfg {Number}    width           For East/West panels
39223  * @cfg {Number}    height          For North/South panels
39224  * @cfg {Boolean}   split           To show the splitter
39225  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39226  * 
39227  * @cfg {string}   cls             Extra CSS classes to add to region
39228  * 
39229  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39230  * @cfg {string}   region  the region that it inhabits..
39231  *
39232
39233  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39234  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39235
39236  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39237  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39238  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39239  */
39240 Roo.bootstrap.layout.Region = function(config)
39241 {
39242     this.applyConfig(config);
39243
39244     var mgr = config.mgr;
39245     var pos = config.region;
39246     config.skipConfig = true;
39247     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39248     
39249     if (mgr.el) {
39250         this.onRender(mgr.el);   
39251     }
39252      
39253     this.visible = true;
39254     this.collapsed = false;
39255     this.unrendered_panels = [];
39256 };
39257
39258 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39259
39260     position: '', // set by wrapper (eg. north/south etc..)
39261     unrendered_panels : null,  // unrendered panels.
39262     
39263     tabPosition : false,
39264     
39265     mgr: false, // points to 'Border'
39266     
39267     
39268     createBody : function(){
39269         /** This region's body element 
39270         * @type Roo.Element */
39271         this.bodyEl = this.el.createChild({
39272                 tag: "div",
39273                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39274         });
39275     },
39276
39277     onRender: function(ctr, pos)
39278     {
39279         var dh = Roo.DomHelper;
39280         /** This region's container element 
39281         * @type Roo.Element */
39282         this.el = dh.append(ctr.dom, {
39283                 tag: "div",
39284                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39285             }, true);
39286         /** This region's title element 
39287         * @type Roo.Element */
39288     
39289         this.titleEl = dh.append(this.el.dom,  {
39290                 tag: "div",
39291                 unselectable: "on",
39292                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39293                 children:[
39294                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39295                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39296                 ]
39297             }, true);
39298         
39299         this.titleEl.enableDisplayMode();
39300         /** This region's title text element 
39301         * @type HTMLElement */
39302         this.titleTextEl = this.titleEl.dom.firstChild;
39303         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39304         /*
39305         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39306         this.closeBtn.enableDisplayMode();
39307         this.closeBtn.on("click", this.closeClicked, this);
39308         this.closeBtn.hide();
39309     */
39310         this.createBody(this.config);
39311         if(this.config.hideWhenEmpty){
39312             this.hide();
39313             this.on("paneladded", this.validateVisibility, this);
39314             this.on("panelremoved", this.validateVisibility, this);
39315         }
39316         if(this.autoScroll){
39317             this.bodyEl.setStyle("overflow", "auto");
39318         }else{
39319             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39320         }
39321         //if(c.titlebar !== false){
39322             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39323                 this.titleEl.hide();
39324             }else{
39325                 this.titleEl.show();
39326                 if(this.config.title){
39327                     this.titleTextEl.innerHTML = this.config.title;
39328                 }
39329             }
39330         //}
39331         if(this.config.collapsed){
39332             this.collapse(true);
39333         }
39334         if(this.config.hidden){
39335             this.hide();
39336         }
39337         
39338         if (this.unrendered_panels && this.unrendered_panels.length) {
39339             for (var i =0;i< this.unrendered_panels.length; i++) {
39340                 this.add(this.unrendered_panels[i]);
39341             }
39342             this.unrendered_panels = null;
39343             
39344         }
39345         
39346     },
39347     
39348     applyConfig : function(c)
39349     {
39350         /*
39351          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39352             var dh = Roo.DomHelper;
39353             if(c.titlebar !== false){
39354                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39355                 this.collapseBtn.on("click", this.collapse, this);
39356                 this.collapseBtn.enableDisplayMode();
39357                 /*
39358                 if(c.showPin === true || this.showPin){
39359                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39360                     this.stickBtn.enableDisplayMode();
39361                     this.stickBtn.on("click", this.expand, this);
39362                     this.stickBtn.hide();
39363                 }
39364                 
39365             }
39366             */
39367             /** This region's collapsed element
39368             * @type Roo.Element */
39369             /*
39370              *
39371             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39372                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39373             ]}, true);
39374             
39375             if(c.floatable !== false){
39376                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39377                this.collapsedEl.on("click", this.collapseClick, this);
39378             }
39379
39380             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39381                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39382                    id: "message", unselectable: "on", style:{"float":"left"}});
39383                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39384              }
39385             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39386             this.expandBtn.on("click", this.expand, this);
39387             
39388         }
39389         
39390         if(this.collapseBtn){
39391             this.collapseBtn.setVisible(c.collapsible == true);
39392         }
39393         
39394         this.cmargins = c.cmargins || this.cmargins ||
39395                          (this.position == "west" || this.position == "east" ?
39396                              {top: 0, left: 2, right:2, bottom: 0} :
39397                              {top: 2, left: 0, right:0, bottom: 2});
39398         */
39399         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39400         
39401         
39402         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39403         
39404         this.autoScroll = c.autoScroll || false;
39405         
39406         
39407        
39408         
39409         this.duration = c.duration || .30;
39410         this.slideDuration = c.slideDuration || .45;
39411         this.config = c;
39412        
39413     },
39414     /**
39415      * Returns true if this region is currently visible.
39416      * @return {Boolean}
39417      */
39418     isVisible : function(){
39419         return this.visible;
39420     },
39421
39422     /**
39423      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39424      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39425      */
39426     //setCollapsedTitle : function(title){
39427     //    title = title || "&#160;";
39428      //   if(this.collapsedTitleTextEl){
39429       //      this.collapsedTitleTextEl.innerHTML = title;
39430        // }
39431     //},
39432
39433     getBox : function(){
39434         var b;
39435       //  if(!this.collapsed){
39436             b = this.el.getBox(false, true);
39437        // }else{
39438           //  b = this.collapsedEl.getBox(false, true);
39439         //}
39440         return b;
39441     },
39442
39443     getMargins : function(){
39444         return this.margins;
39445         //return this.collapsed ? this.cmargins : this.margins;
39446     },
39447 /*
39448     highlight : function(){
39449         this.el.addClass("x-layout-panel-dragover");
39450     },
39451
39452     unhighlight : function(){
39453         this.el.removeClass("x-layout-panel-dragover");
39454     },
39455 */
39456     updateBox : function(box)
39457     {
39458         if (!this.bodyEl) {
39459             return; // not rendered yet..
39460         }
39461         
39462         this.box = box;
39463         if(!this.collapsed){
39464             this.el.dom.style.left = box.x + "px";
39465             this.el.dom.style.top = box.y + "px";
39466             this.updateBody(box.width, box.height);
39467         }else{
39468             this.collapsedEl.dom.style.left = box.x + "px";
39469             this.collapsedEl.dom.style.top = box.y + "px";
39470             this.collapsedEl.setSize(box.width, box.height);
39471         }
39472         if(this.tabs){
39473             this.tabs.autoSizeTabs();
39474         }
39475     },
39476
39477     updateBody : function(w, h)
39478     {
39479         if(w !== null){
39480             this.el.setWidth(w);
39481             w -= this.el.getBorderWidth("rl");
39482             if(this.config.adjustments){
39483                 w += this.config.adjustments[0];
39484             }
39485         }
39486         if(h !== null && h > 0){
39487             this.el.setHeight(h);
39488             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39489             h -= this.el.getBorderWidth("tb");
39490             if(this.config.adjustments){
39491                 h += this.config.adjustments[1];
39492             }
39493             this.bodyEl.setHeight(h);
39494             if(this.tabs){
39495                 h = this.tabs.syncHeight(h);
39496             }
39497         }
39498         if(this.panelSize){
39499             w = w !== null ? w : this.panelSize.width;
39500             h = h !== null ? h : this.panelSize.height;
39501         }
39502         if(this.activePanel){
39503             var el = this.activePanel.getEl();
39504             w = w !== null ? w : el.getWidth();
39505             h = h !== null ? h : el.getHeight();
39506             this.panelSize = {width: w, height: h};
39507             this.activePanel.setSize(w, h);
39508         }
39509         if(Roo.isIE && this.tabs){
39510             this.tabs.el.repaint();
39511         }
39512     },
39513
39514     /**
39515      * Returns the container element for this region.
39516      * @return {Roo.Element}
39517      */
39518     getEl : function(){
39519         return this.el;
39520     },
39521
39522     /**
39523      * Hides this region.
39524      */
39525     hide : function(){
39526         //if(!this.collapsed){
39527             this.el.dom.style.left = "-2000px";
39528             this.el.hide();
39529         //}else{
39530          //   this.collapsedEl.dom.style.left = "-2000px";
39531          //   this.collapsedEl.hide();
39532        // }
39533         this.visible = false;
39534         this.fireEvent("visibilitychange", this, false);
39535     },
39536
39537     /**
39538      * Shows this region if it was previously hidden.
39539      */
39540     show : function(){
39541         //if(!this.collapsed){
39542             this.el.show();
39543         //}else{
39544         //    this.collapsedEl.show();
39545        // }
39546         this.visible = true;
39547         this.fireEvent("visibilitychange", this, true);
39548     },
39549 /*
39550     closeClicked : function(){
39551         if(this.activePanel){
39552             this.remove(this.activePanel);
39553         }
39554     },
39555
39556     collapseClick : function(e){
39557         if(this.isSlid){
39558            e.stopPropagation();
39559            this.slideIn();
39560         }else{
39561            e.stopPropagation();
39562            this.slideOut();
39563         }
39564     },
39565 */
39566     /**
39567      * Collapses this region.
39568      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39569      */
39570     /*
39571     collapse : function(skipAnim, skipCheck = false){
39572         if(this.collapsed) {
39573             return;
39574         }
39575         
39576         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39577             
39578             this.collapsed = true;
39579             if(this.split){
39580                 this.split.el.hide();
39581             }
39582             if(this.config.animate && skipAnim !== true){
39583                 this.fireEvent("invalidated", this);
39584                 this.animateCollapse();
39585             }else{
39586                 this.el.setLocation(-20000,-20000);
39587                 this.el.hide();
39588                 this.collapsedEl.show();
39589                 this.fireEvent("collapsed", this);
39590                 this.fireEvent("invalidated", this);
39591             }
39592         }
39593         
39594     },
39595 */
39596     animateCollapse : function(){
39597         // overridden
39598     },
39599
39600     /**
39601      * Expands this region if it was previously collapsed.
39602      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39603      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39604      */
39605     /*
39606     expand : function(e, skipAnim){
39607         if(e) {
39608             e.stopPropagation();
39609         }
39610         if(!this.collapsed || this.el.hasActiveFx()) {
39611             return;
39612         }
39613         if(this.isSlid){
39614             this.afterSlideIn();
39615             skipAnim = true;
39616         }
39617         this.collapsed = false;
39618         if(this.config.animate && skipAnim !== true){
39619             this.animateExpand();
39620         }else{
39621             this.el.show();
39622             if(this.split){
39623                 this.split.el.show();
39624             }
39625             this.collapsedEl.setLocation(-2000,-2000);
39626             this.collapsedEl.hide();
39627             this.fireEvent("invalidated", this);
39628             this.fireEvent("expanded", this);
39629         }
39630     },
39631 */
39632     animateExpand : function(){
39633         // overridden
39634     },
39635
39636     initTabs : function()
39637     {
39638         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39639         
39640         var ts = new Roo.bootstrap.panel.Tabs({
39641             el: this.bodyEl.dom,
39642             region : this,
39643             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39644             disableTooltips: this.config.disableTabTips,
39645             toolbar : this.config.toolbar
39646         });
39647         
39648         if(this.config.hideTabs){
39649             ts.stripWrap.setDisplayed(false);
39650         }
39651         this.tabs = ts;
39652         ts.resizeTabs = this.config.resizeTabs === true;
39653         ts.minTabWidth = this.config.minTabWidth || 40;
39654         ts.maxTabWidth = this.config.maxTabWidth || 250;
39655         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39656         ts.monitorResize = false;
39657         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39658         ts.bodyEl.addClass('roo-layout-tabs-body');
39659         this.panels.each(this.initPanelAsTab, this);
39660     },
39661
39662     initPanelAsTab : function(panel){
39663         var ti = this.tabs.addTab(
39664             panel.getEl().id,
39665             panel.getTitle(),
39666             null,
39667             this.config.closeOnTab && panel.isClosable(),
39668             panel.tpl
39669         );
39670         if(panel.tabTip !== undefined){
39671             ti.setTooltip(panel.tabTip);
39672         }
39673         ti.on("activate", function(){
39674               this.setActivePanel(panel);
39675         }, this);
39676         
39677         if(this.config.closeOnTab){
39678             ti.on("beforeclose", function(t, e){
39679                 e.cancel = true;
39680                 this.remove(panel);
39681             }, this);
39682         }
39683         
39684         panel.tabItem = ti;
39685         
39686         return ti;
39687     },
39688
39689     updatePanelTitle : function(panel, title)
39690     {
39691         if(this.activePanel == panel){
39692             this.updateTitle(title);
39693         }
39694         if(this.tabs){
39695             var ti = this.tabs.getTab(panel.getEl().id);
39696             ti.setText(title);
39697             if(panel.tabTip !== undefined){
39698                 ti.setTooltip(panel.tabTip);
39699             }
39700         }
39701     },
39702
39703     updateTitle : function(title){
39704         if(this.titleTextEl && !this.config.title){
39705             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39706         }
39707     },
39708
39709     setActivePanel : function(panel)
39710     {
39711         panel = this.getPanel(panel);
39712         if(this.activePanel && this.activePanel != panel){
39713             if(this.activePanel.setActiveState(false) === false){
39714                 return;
39715             }
39716         }
39717         this.activePanel = panel;
39718         panel.setActiveState(true);
39719         if(this.panelSize){
39720             panel.setSize(this.panelSize.width, this.panelSize.height);
39721         }
39722         if(this.closeBtn){
39723             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39724         }
39725         this.updateTitle(panel.getTitle());
39726         if(this.tabs){
39727             this.fireEvent("invalidated", this);
39728         }
39729         this.fireEvent("panelactivated", this, panel);
39730     },
39731
39732     /**
39733      * Shows the specified panel.
39734      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39735      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39736      */
39737     showPanel : function(panel)
39738     {
39739         panel = this.getPanel(panel);
39740         if(panel){
39741             if(this.tabs){
39742                 var tab = this.tabs.getTab(panel.getEl().id);
39743                 if(tab.isHidden()){
39744                     this.tabs.unhideTab(tab.id);
39745                 }
39746                 tab.activate();
39747             }else{
39748                 this.setActivePanel(panel);
39749             }
39750         }
39751         return panel;
39752     },
39753
39754     /**
39755      * Get the active panel for this region.
39756      * @return {Roo.ContentPanel} The active panel or null
39757      */
39758     getActivePanel : function(){
39759         return this.activePanel;
39760     },
39761
39762     validateVisibility : function(){
39763         if(this.panels.getCount() < 1){
39764             this.updateTitle("&#160;");
39765             this.closeBtn.hide();
39766             this.hide();
39767         }else{
39768             if(!this.isVisible()){
39769                 this.show();
39770             }
39771         }
39772     },
39773
39774     /**
39775      * Adds the passed ContentPanel(s) to this region.
39776      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39777      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39778      */
39779     add : function(panel)
39780     {
39781         if(arguments.length > 1){
39782             for(var i = 0, len = arguments.length; i < len; i++) {
39783                 this.add(arguments[i]);
39784             }
39785             return null;
39786         }
39787         
39788         // if we have not been rendered yet, then we can not really do much of this..
39789         if (!this.bodyEl) {
39790             this.unrendered_panels.push(panel);
39791             return panel;
39792         }
39793         
39794         
39795         
39796         
39797         if(this.hasPanel(panel)){
39798             this.showPanel(panel);
39799             return panel;
39800         }
39801         panel.setRegion(this);
39802         this.panels.add(panel);
39803        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39804             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39805             // and hide them... ???
39806             this.bodyEl.dom.appendChild(panel.getEl().dom);
39807             if(panel.background !== true){
39808                 this.setActivePanel(panel);
39809             }
39810             this.fireEvent("paneladded", this, panel);
39811             return panel;
39812         }
39813         */
39814         if(!this.tabs){
39815             this.initTabs();
39816         }else{
39817             this.initPanelAsTab(panel);
39818         }
39819         
39820         
39821         if(panel.background !== true){
39822             this.tabs.activate(panel.getEl().id);
39823         }
39824         this.fireEvent("paneladded", this, panel);
39825         return panel;
39826     },
39827
39828     /**
39829      * Hides the tab for the specified panel.
39830      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39831      */
39832     hidePanel : function(panel){
39833         if(this.tabs && (panel = this.getPanel(panel))){
39834             this.tabs.hideTab(panel.getEl().id);
39835         }
39836     },
39837
39838     /**
39839      * Unhides the tab for a previously hidden panel.
39840      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39841      */
39842     unhidePanel : function(panel){
39843         if(this.tabs && (panel = this.getPanel(panel))){
39844             this.tabs.unhideTab(panel.getEl().id);
39845         }
39846     },
39847
39848     clearPanels : function(){
39849         while(this.panels.getCount() > 0){
39850              this.remove(this.panels.first());
39851         }
39852     },
39853
39854     /**
39855      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39856      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39857      * @param {Boolean} preservePanel Overrides the config preservePanel option
39858      * @return {Roo.ContentPanel} The panel that was removed
39859      */
39860     remove : function(panel, preservePanel)
39861     {
39862         panel = this.getPanel(panel);
39863         if(!panel){
39864             return null;
39865         }
39866         var e = {};
39867         this.fireEvent("beforeremove", this, panel, e);
39868         if(e.cancel === true){
39869             return null;
39870         }
39871         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39872         var panelId = panel.getId();
39873         this.panels.removeKey(panelId);
39874         if(preservePanel){
39875             document.body.appendChild(panel.getEl().dom);
39876         }
39877         if(this.tabs){
39878             this.tabs.removeTab(panel.getEl().id);
39879         }else if (!preservePanel){
39880             this.bodyEl.dom.removeChild(panel.getEl().dom);
39881         }
39882         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39883             var p = this.panels.first();
39884             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39885             tempEl.appendChild(p.getEl().dom);
39886             this.bodyEl.update("");
39887             this.bodyEl.dom.appendChild(p.getEl().dom);
39888             tempEl = null;
39889             this.updateTitle(p.getTitle());
39890             this.tabs = null;
39891             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39892             this.setActivePanel(p);
39893         }
39894         panel.setRegion(null);
39895         if(this.activePanel == panel){
39896             this.activePanel = null;
39897         }
39898         if(this.config.autoDestroy !== false && preservePanel !== true){
39899             try{panel.destroy();}catch(e){}
39900         }
39901         this.fireEvent("panelremoved", this, panel);
39902         return panel;
39903     },
39904
39905     /**
39906      * Returns the TabPanel component used by this region
39907      * @return {Roo.TabPanel}
39908      */
39909     getTabs : function(){
39910         return this.tabs;
39911     },
39912
39913     createTool : function(parentEl, className){
39914         var btn = Roo.DomHelper.append(parentEl, {
39915             tag: "div",
39916             cls: "x-layout-tools-button",
39917             children: [ {
39918                 tag: "div",
39919                 cls: "roo-layout-tools-button-inner " + className,
39920                 html: "&#160;"
39921             }]
39922         }, true);
39923         btn.addClassOnOver("roo-layout-tools-button-over");
39924         return btn;
39925     }
39926 });/*
39927  * Based on:
39928  * Ext JS Library 1.1.1
39929  * Copyright(c) 2006-2007, Ext JS, LLC.
39930  *
39931  * Originally Released Under LGPL - original licence link has changed is not relivant.
39932  *
39933  * Fork - LGPL
39934  * <script type="text/javascript">
39935  */
39936  
39937
39938
39939 /**
39940  * @class Roo.SplitLayoutRegion
39941  * @extends Roo.LayoutRegion
39942  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39943  */
39944 Roo.bootstrap.layout.Split = function(config){
39945     this.cursor = config.cursor;
39946     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39947 };
39948
39949 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39950 {
39951     splitTip : "Drag to resize.",
39952     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39953     useSplitTips : false,
39954
39955     applyConfig : function(config){
39956         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39957     },
39958     
39959     onRender : function(ctr,pos) {
39960         
39961         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39962         if(!this.config.split){
39963             return;
39964         }
39965         if(!this.split){
39966             
39967             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39968                             tag: "div",
39969                             id: this.el.id + "-split",
39970                             cls: "roo-layout-split roo-layout-split-"+this.position,
39971                             html: "&#160;"
39972             });
39973             /** The SplitBar for this region 
39974             * @type Roo.SplitBar */
39975             // does not exist yet...
39976             Roo.log([this.position, this.orientation]);
39977             
39978             this.split = new Roo.bootstrap.SplitBar({
39979                 dragElement : splitEl,
39980                 resizingElement: this.el,
39981                 orientation : this.orientation
39982             });
39983             
39984             this.split.on("moved", this.onSplitMove, this);
39985             this.split.useShim = this.config.useShim === true;
39986             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39987             if(this.useSplitTips){
39988                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39989             }
39990             //if(config.collapsible){
39991             //    this.split.el.on("dblclick", this.collapse,  this);
39992             //}
39993         }
39994         if(typeof this.config.minSize != "undefined"){
39995             this.split.minSize = this.config.minSize;
39996         }
39997         if(typeof this.config.maxSize != "undefined"){
39998             this.split.maxSize = this.config.maxSize;
39999         }
40000         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
40001             this.hideSplitter();
40002         }
40003         
40004     },
40005
40006     getHMaxSize : function(){
40007          var cmax = this.config.maxSize || 10000;
40008          var center = this.mgr.getRegion("center");
40009          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40010     },
40011
40012     getVMaxSize : function(){
40013          var cmax = this.config.maxSize || 10000;
40014          var center = this.mgr.getRegion("center");
40015          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40016     },
40017
40018     onSplitMove : function(split, newSize){
40019         this.fireEvent("resized", this, newSize);
40020     },
40021     
40022     /** 
40023      * Returns the {@link Roo.SplitBar} for this region.
40024      * @return {Roo.SplitBar}
40025      */
40026     getSplitBar : function(){
40027         return this.split;
40028     },
40029     
40030     hide : function(){
40031         this.hideSplitter();
40032         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40033     },
40034
40035     hideSplitter : function(){
40036         if(this.split){
40037             this.split.el.setLocation(-2000,-2000);
40038             this.split.el.hide();
40039         }
40040     },
40041
40042     show : function(){
40043         if(this.split){
40044             this.split.el.show();
40045         }
40046         Roo.bootstrap.layout.Split.superclass.show.call(this);
40047     },
40048     
40049     beforeSlide: function(){
40050         if(Roo.isGecko){// firefox overflow auto bug workaround
40051             this.bodyEl.clip();
40052             if(this.tabs) {
40053                 this.tabs.bodyEl.clip();
40054             }
40055             if(this.activePanel){
40056                 this.activePanel.getEl().clip();
40057                 
40058                 if(this.activePanel.beforeSlide){
40059                     this.activePanel.beforeSlide();
40060                 }
40061             }
40062         }
40063     },
40064     
40065     afterSlide : function(){
40066         if(Roo.isGecko){// firefox overflow auto bug workaround
40067             this.bodyEl.unclip();
40068             if(this.tabs) {
40069                 this.tabs.bodyEl.unclip();
40070             }
40071             if(this.activePanel){
40072                 this.activePanel.getEl().unclip();
40073                 if(this.activePanel.afterSlide){
40074                     this.activePanel.afterSlide();
40075                 }
40076             }
40077         }
40078     },
40079
40080     initAutoHide : function(){
40081         if(this.autoHide !== false){
40082             if(!this.autoHideHd){
40083                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40084                 this.autoHideHd = {
40085                     "mouseout": function(e){
40086                         if(!e.within(this.el, true)){
40087                             st.delay(500);
40088                         }
40089                     },
40090                     "mouseover" : function(e){
40091                         st.cancel();
40092                     },
40093                     scope : this
40094                 };
40095             }
40096             this.el.on(this.autoHideHd);
40097         }
40098     },
40099
40100     clearAutoHide : function(){
40101         if(this.autoHide !== false){
40102             this.el.un("mouseout", this.autoHideHd.mouseout);
40103             this.el.un("mouseover", this.autoHideHd.mouseover);
40104         }
40105     },
40106
40107     clearMonitor : function(){
40108         Roo.get(document).un("click", this.slideInIf, this);
40109     },
40110
40111     // these names are backwards but not changed for compat
40112     slideOut : function(){
40113         if(this.isSlid || this.el.hasActiveFx()){
40114             return;
40115         }
40116         this.isSlid = true;
40117         if(this.collapseBtn){
40118             this.collapseBtn.hide();
40119         }
40120         this.closeBtnState = this.closeBtn.getStyle('display');
40121         this.closeBtn.hide();
40122         if(this.stickBtn){
40123             this.stickBtn.show();
40124         }
40125         this.el.show();
40126         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40127         this.beforeSlide();
40128         this.el.setStyle("z-index", 10001);
40129         this.el.slideIn(this.getSlideAnchor(), {
40130             callback: function(){
40131                 this.afterSlide();
40132                 this.initAutoHide();
40133                 Roo.get(document).on("click", this.slideInIf, this);
40134                 this.fireEvent("slideshow", this);
40135             },
40136             scope: this,
40137             block: true
40138         });
40139     },
40140
40141     afterSlideIn : function(){
40142         this.clearAutoHide();
40143         this.isSlid = false;
40144         this.clearMonitor();
40145         this.el.setStyle("z-index", "");
40146         if(this.collapseBtn){
40147             this.collapseBtn.show();
40148         }
40149         this.closeBtn.setStyle('display', this.closeBtnState);
40150         if(this.stickBtn){
40151             this.stickBtn.hide();
40152         }
40153         this.fireEvent("slidehide", this);
40154     },
40155
40156     slideIn : function(cb){
40157         if(!this.isSlid || this.el.hasActiveFx()){
40158             Roo.callback(cb);
40159             return;
40160         }
40161         this.isSlid = false;
40162         this.beforeSlide();
40163         this.el.slideOut(this.getSlideAnchor(), {
40164             callback: function(){
40165                 this.el.setLeftTop(-10000, -10000);
40166                 this.afterSlide();
40167                 this.afterSlideIn();
40168                 Roo.callback(cb);
40169             },
40170             scope: this,
40171             block: true
40172         });
40173     },
40174     
40175     slideInIf : function(e){
40176         if(!e.within(this.el)){
40177             this.slideIn();
40178         }
40179     },
40180
40181     animateCollapse : function(){
40182         this.beforeSlide();
40183         this.el.setStyle("z-index", 20000);
40184         var anchor = this.getSlideAnchor();
40185         this.el.slideOut(anchor, {
40186             callback : function(){
40187                 this.el.setStyle("z-index", "");
40188                 this.collapsedEl.slideIn(anchor, {duration:.3});
40189                 this.afterSlide();
40190                 this.el.setLocation(-10000,-10000);
40191                 this.el.hide();
40192                 this.fireEvent("collapsed", this);
40193             },
40194             scope: this,
40195             block: true
40196         });
40197     },
40198
40199     animateExpand : function(){
40200         this.beforeSlide();
40201         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40202         this.el.setStyle("z-index", 20000);
40203         this.collapsedEl.hide({
40204             duration:.1
40205         });
40206         this.el.slideIn(this.getSlideAnchor(), {
40207             callback : function(){
40208                 this.el.setStyle("z-index", "");
40209                 this.afterSlide();
40210                 if(this.split){
40211                     this.split.el.show();
40212                 }
40213                 this.fireEvent("invalidated", this);
40214                 this.fireEvent("expanded", this);
40215             },
40216             scope: this,
40217             block: true
40218         });
40219     },
40220
40221     anchors : {
40222         "west" : "left",
40223         "east" : "right",
40224         "north" : "top",
40225         "south" : "bottom"
40226     },
40227
40228     sanchors : {
40229         "west" : "l",
40230         "east" : "r",
40231         "north" : "t",
40232         "south" : "b"
40233     },
40234
40235     canchors : {
40236         "west" : "tl-tr",
40237         "east" : "tr-tl",
40238         "north" : "tl-bl",
40239         "south" : "bl-tl"
40240     },
40241
40242     getAnchor : function(){
40243         return this.anchors[this.position];
40244     },
40245
40246     getCollapseAnchor : function(){
40247         return this.canchors[this.position];
40248     },
40249
40250     getSlideAnchor : function(){
40251         return this.sanchors[this.position];
40252     },
40253
40254     getAlignAdj : function(){
40255         var cm = this.cmargins;
40256         switch(this.position){
40257             case "west":
40258                 return [0, 0];
40259             break;
40260             case "east":
40261                 return [0, 0];
40262             break;
40263             case "north":
40264                 return [0, 0];
40265             break;
40266             case "south":
40267                 return [0, 0];
40268             break;
40269         }
40270     },
40271
40272     getExpandAdj : function(){
40273         var c = this.collapsedEl, cm = this.cmargins;
40274         switch(this.position){
40275             case "west":
40276                 return [-(cm.right+c.getWidth()+cm.left), 0];
40277             break;
40278             case "east":
40279                 return [cm.right+c.getWidth()+cm.left, 0];
40280             break;
40281             case "north":
40282                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40283             break;
40284             case "south":
40285                 return [0, cm.top+cm.bottom+c.getHeight()];
40286             break;
40287         }
40288     }
40289 });/*
40290  * Based on:
40291  * Ext JS Library 1.1.1
40292  * Copyright(c) 2006-2007, Ext JS, LLC.
40293  *
40294  * Originally Released Under LGPL - original licence link has changed is not relivant.
40295  *
40296  * Fork - LGPL
40297  * <script type="text/javascript">
40298  */
40299 /*
40300  * These classes are private internal classes
40301  */
40302 Roo.bootstrap.layout.Center = function(config){
40303     config.region = "center";
40304     Roo.bootstrap.layout.Region.call(this, config);
40305     this.visible = true;
40306     this.minWidth = config.minWidth || 20;
40307     this.minHeight = config.minHeight || 20;
40308 };
40309
40310 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40311     hide : function(){
40312         // center panel can't be hidden
40313     },
40314     
40315     show : function(){
40316         // center panel can't be hidden
40317     },
40318     
40319     getMinWidth: function(){
40320         return this.minWidth;
40321     },
40322     
40323     getMinHeight: function(){
40324         return this.minHeight;
40325     }
40326 });
40327
40328
40329
40330
40331  
40332
40333
40334
40335
40336
40337
40338 Roo.bootstrap.layout.North = function(config)
40339 {
40340     config.region = 'north';
40341     config.cursor = 'n-resize';
40342     
40343     Roo.bootstrap.layout.Split.call(this, config);
40344     
40345     
40346     if(this.split){
40347         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40348         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40349         this.split.el.addClass("roo-layout-split-v");
40350     }
40351     //var size = config.initialSize || config.height;
40352     //if(this.el && typeof size != "undefined"){
40353     //    this.el.setHeight(size);
40354     //}
40355 };
40356 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40357 {
40358     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40359      
40360      
40361     onRender : function(ctr, pos)
40362     {
40363         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40364         var size = this.config.initialSize || this.config.height;
40365         if(this.el && typeof size != "undefined"){
40366             this.el.setHeight(size);
40367         }
40368     
40369     },
40370     
40371     getBox : function(){
40372         if(this.collapsed){
40373             return this.collapsedEl.getBox();
40374         }
40375         var box = this.el.getBox();
40376         if(this.split){
40377             box.height += this.split.el.getHeight();
40378         }
40379         return box;
40380     },
40381     
40382     updateBox : function(box){
40383         if(this.split && !this.collapsed){
40384             box.height -= this.split.el.getHeight();
40385             this.split.el.setLeft(box.x);
40386             this.split.el.setTop(box.y+box.height);
40387             this.split.el.setWidth(box.width);
40388         }
40389         if(this.collapsed){
40390             this.updateBody(box.width, null);
40391         }
40392         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40393     }
40394 });
40395
40396
40397
40398
40399
40400 Roo.bootstrap.layout.South = function(config){
40401     config.region = 'south';
40402     config.cursor = 's-resize';
40403     Roo.bootstrap.layout.Split.call(this, config);
40404     if(this.split){
40405         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40406         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40407         this.split.el.addClass("roo-layout-split-v");
40408     }
40409     
40410 };
40411
40412 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40413     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40414     
40415     onRender : function(ctr, pos)
40416     {
40417         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40418         var size = this.config.initialSize || this.config.height;
40419         if(this.el && typeof size != "undefined"){
40420             this.el.setHeight(size);
40421         }
40422     
40423     },
40424     
40425     getBox : function(){
40426         if(this.collapsed){
40427             return this.collapsedEl.getBox();
40428         }
40429         var box = this.el.getBox();
40430         if(this.split){
40431             var sh = this.split.el.getHeight();
40432             box.height += sh;
40433             box.y -= sh;
40434         }
40435         return box;
40436     },
40437     
40438     updateBox : function(box){
40439         if(this.split && !this.collapsed){
40440             var sh = this.split.el.getHeight();
40441             box.height -= sh;
40442             box.y += sh;
40443             this.split.el.setLeft(box.x);
40444             this.split.el.setTop(box.y-sh);
40445             this.split.el.setWidth(box.width);
40446         }
40447         if(this.collapsed){
40448             this.updateBody(box.width, null);
40449         }
40450         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40451     }
40452 });
40453
40454 Roo.bootstrap.layout.East = function(config){
40455     config.region = "east";
40456     config.cursor = "e-resize";
40457     Roo.bootstrap.layout.Split.call(this, config);
40458     if(this.split){
40459         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40460         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40461         this.split.el.addClass("roo-layout-split-h");
40462     }
40463     
40464 };
40465 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40466     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40467     
40468     onRender : function(ctr, pos)
40469     {
40470         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40471         var size = this.config.initialSize || this.config.width;
40472         if(this.el && typeof size != "undefined"){
40473             this.el.setWidth(size);
40474         }
40475     
40476     },
40477     
40478     getBox : function(){
40479         if(this.collapsed){
40480             return this.collapsedEl.getBox();
40481         }
40482         var box = this.el.getBox();
40483         if(this.split){
40484             var sw = this.split.el.getWidth();
40485             box.width += sw;
40486             box.x -= sw;
40487         }
40488         return box;
40489     },
40490
40491     updateBox : function(box){
40492         if(this.split && !this.collapsed){
40493             var sw = this.split.el.getWidth();
40494             box.width -= sw;
40495             this.split.el.setLeft(box.x);
40496             this.split.el.setTop(box.y);
40497             this.split.el.setHeight(box.height);
40498             box.x += sw;
40499         }
40500         if(this.collapsed){
40501             this.updateBody(null, box.height);
40502         }
40503         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40504     }
40505 });
40506
40507 Roo.bootstrap.layout.West = function(config){
40508     config.region = "west";
40509     config.cursor = "w-resize";
40510     
40511     Roo.bootstrap.layout.Split.call(this, config);
40512     if(this.split){
40513         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40514         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40515         this.split.el.addClass("roo-layout-split-h");
40516     }
40517     
40518 };
40519 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40520     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40521     
40522     onRender: function(ctr, pos)
40523     {
40524         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40525         var size = this.config.initialSize || this.config.width;
40526         if(typeof size != "undefined"){
40527             this.el.setWidth(size);
40528         }
40529     },
40530     
40531     getBox : function(){
40532         if(this.collapsed){
40533             return this.collapsedEl.getBox();
40534         }
40535         var box = this.el.getBox();
40536         if (box.width == 0) {
40537             box.width = this.config.width; // kludge?
40538         }
40539         if(this.split){
40540             box.width += this.split.el.getWidth();
40541         }
40542         return box;
40543     },
40544     
40545     updateBox : function(box){
40546         if(this.split && !this.collapsed){
40547             var sw = this.split.el.getWidth();
40548             box.width -= sw;
40549             this.split.el.setLeft(box.x+box.width);
40550             this.split.el.setTop(box.y);
40551             this.split.el.setHeight(box.height);
40552         }
40553         if(this.collapsed){
40554             this.updateBody(null, box.height);
40555         }
40556         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40557     }
40558 });Roo.namespace("Roo.bootstrap.panel");/*
40559  * Based on:
40560  * Ext JS Library 1.1.1
40561  * Copyright(c) 2006-2007, Ext JS, LLC.
40562  *
40563  * Originally Released Under LGPL - original licence link has changed is not relivant.
40564  *
40565  * Fork - LGPL
40566  * <script type="text/javascript">
40567  */
40568 /**
40569  * @class Roo.ContentPanel
40570  * @extends Roo.util.Observable
40571  * @builder-top
40572  * A basic ContentPanel element.
40573  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40574  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40575  * @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
40576  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40577  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40578  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40579  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40580  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40581  * @cfg {String} title          The title for this panel
40582  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40583  * @cfg {String} url            Calls {@link #setUrl} with this value
40584  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40585  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40586  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40587  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40588  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40589  * @cfg {Boolean} badges render the badges
40590  * @cfg {String} cls  extra classes to use  
40591  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40592
40593  * @constructor
40594  * Create a new ContentPanel.
40595  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40596  * @param {String/Object} config A string to set only the title or a config object
40597  * @param {String} content (optional) Set the HTML content for this panel
40598  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40599  */
40600 Roo.bootstrap.panel.Content = function( config){
40601     
40602     this.tpl = config.tpl || false;
40603     
40604     var el = config.el;
40605     var content = config.content;
40606
40607     if(config.autoCreate){ // xtype is available if this is called from factory
40608         el = Roo.id();
40609     }
40610     this.el = Roo.get(el);
40611     if(!this.el && config && config.autoCreate){
40612         if(typeof config.autoCreate == "object"){
40613             if(!config.autoCreate.id){
40614                 config.autoCreate.id = config.id||el;
40615             }
40616             this.el = Roo.DomHelper.append(document.body,
40617                         config.autoCreate, true);
40618         }else{
40619             var elcfg =  {
40620                 tag: "div",
40621                 cls: (config.cls || '') +
40622                     (config.background ? ' bg-' + config.background : '') +
40623                     " roo-layout-inactive-content",
40624                 id: config.id||el
40625             };
40626             if (config.iframe) {
40627                 elcfg.cn = [
40628                     {
40629                         tag : 'iframe',
40630                         style : 'border: 0px',
40631                         src : 'about:blank'
40632                     }
40633                 ];
40634             }
40635               
40636             if (config.html) {
40637                 elcfg.html = config.html;
40638                 
40639             }
40640                         
40641             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40642             if (config.iframe) {
40643                 this.iframeEl = this.el.select('iframe',true).first();
40644             }
40645             
40646         }
40647     } 
40648     this.closable = false;
40649     this.loaded = false;
40650     this.active = false;
40651    
40652       
40653     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40654         
40655         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40656         
40657         this.wrapEl = this.el; //this.el.wrap();
40658         var ti = [];
40659         if (config.toolbar.items) {
40660             ti = config.toolbar.items ;
40661             delete config.toolbar.items ;
40662         }
40663         
40664         var nitems = [];
40665         this.toolbar.render(this.wrapEl, 'before');
40666         for(var i =0;i < ti.length;i++) {
40667           //  Roo.log(['add child', items[i]]);
40668             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40669         }
40670         this.toolbar.items = nitems;
40671         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40672         delete config.toolbar;
40673         
40674     }
40675     /*
40676     // xtype created footer. - not sure if will work as we normally have to render first..
40677     if (this.footer && !this.footer.el && this.footer.xtype) {
40678         if (!this.wrapEl) {
40679             this.wrapEl = this.el.wrap();
40680         }
40681     
40682         this.footer.container = this.wrapEl.createChild();
40683          
40684         this.footer = Roo.factory(this.footer, Roo);
40685         
40686     }
40687     */
40688     
40689      if(typeof config == "string"){
40690         this.title = config;
40691     }else{
40692         Roo.apply(this, config);
40693     }
40694     
40695     if(this.resizeEl){
40696         this.resizeEl = Roo.get(this.resizeEl, true);
40697     }else{
40698         this.resizeEl = this.el;
40699     }
40700     // handle view.xtype
40701     
40702  
40703     
40704     
40705     this.addEvents({
40706         /**
40707          * @event activate
40708          * Fires when this panel is activated. 
40709          * @param {Roo.ContentPanel} this
40710          */
40711         "activate" : true,
40712         /**
40713          * @event deactivate
40714          * Fires when this panel is activated. 
40715          * @param {Roo.ContentPanel} this
40716          */
40717         "deactivate" : true,
40718
40719         /**
40720          * @event resize
40721          * Fires when this panel is resized if fitToFrame is true.
40722          * @param {Roo.ContentPanel} this
40723          * @param {Number} width The width after any component adjustments
40724          * @param {Number} height The height after any component adjustments
40725          */
40726         "resize" : true,
40727         
40728          /**
40729          * @event render
40730          * Fires when this tab is created
40731          * @param {Roo.ContentPanel} this
40732          */
40733         "render" : true,
40734         
40735           /**
40736          * @event scroll
40737          * Fires when this content is scrolled
40738          * @param {Roo.ContentPanel} this
40739          * @param {Event} scrollEvent
40740          */
40741         "scroll" : true
40742         
40743         
40744         
40745     });
40746     
40747
40748     
40749     
40750     if(this.autoScroll && !this.iframe){
40751         this.resizeEl.setStyle("overflow", "auto");
40752         this.resizeEl.on('scroll', this.onScroll, this);
40753     } else {
40754         // fix randome scrolling
40755         //this.el.on('scroll', function() {
40756         //    Roo.log('fix random scolling');
40757         //    this.scrollTo('top',0); 
40758         //});
40759     }
40760     content = content || this.content;
40761     if(content){
40762         this.setContent(content);
40763     }
40764     if(config && config.url){
40765         this.setUrl(this.url, this.params, this.loadOnce);
40766     }
40767     
40768     
40769     
40770     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40771     
40772     if (this.view && typeof(this.view.xtype) != 'undefined') {
40773         this.view.el = this.el.appendChild(document.createElement("div"));
40774         this.view = Roo.factory(this.view); 
40775         this.view.render  &&  this.view.render(false, '');  
40776     }
40777     
40778     
40779     this.fireEvent('render', this);
40780 };
40781
40782 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40783     
40784     cls : '',
40785     background : '',
40786     
40787     tabTip : '',
40788     
40789     iframe : false,
40790     iframeEl : false,
40791     
40792     /* Resize Element - use this to work out scroll etc. */
40793     resizeEl : false,
40794     
40795     setRegion : function(region){
40796         this.region = region;
40797         this.setActiveClass(region && !this.background);
40798     },
40799     
40800     
40801     setActiveClass: function(state)
40802     {
40803         if(state){
40804            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40805            this.el.setStyle('position','relative');
40806         }else{
40807            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40808            this.el.setStyle('position', 'absolute');
40809         } 
40810     },
40811     
40812     /**
40813      * Returns the toolbar for this Panel if one was configured. 
40814      * @return {Roo.Toolbar} 
40815      */
40816     getToolbar : function(){
40817         return this.toolbar;
40818     },
40819     
40820     setActiveState : function(active)
40821     {
40822         this.active = active;
40823         this.setActiveClass(active);
40824         if(!active){
40825             if(this.fireEvent("deactivate", this) === false){
40826                 return false;
40827             }
40828             return true;
40829         }
40830         this.fireEvent("activate", this);
40831         return true;
40832     },
40833     /**
40834      * Updates this panel's element (not for iframe)
40835      * @param {String} content The new content
40836      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40837     */
40838     setContent : function(content, loadScripts){
40839         if (this.iframe) {
40840             return;
40841         }
40842         
40843         this.el.update(content, loadScripts);
40844     },
40845
40846     ignoreResize : function(w, h){
40847         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40848             return true;
40849         }else{
40850             this.lastSize = {width: w, height: h};
40851             return false;
40852         }
40853     },
40854     /**
40855      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40856      * @return {Roo.UpdateManager} The UpdateManager
40857      */
40858     getUpdateManager : function(){
40859         if (this.iframe) {
40860             return false;
40861         }
40862         return this.el.getUpdateManager();
40863     },
40864      /**
40865      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40866      * Does not work with IFRAME contents
40867      * @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:
40868 <pre><code>
40869 panel.load({
40870     url: "your-url.php",
40871     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40872     callback: yourFunction,
40873     scope: yourObject, //(optional scope)
40874     discardUrl: false,
40875     nocache: false,
40876     text: "Loading...",
40877     timeout: 30,
40878     scripts: false
40879 });
40880 </code></pre>
40881      
40882      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40883      * 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.
40884      * @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}
40885      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40886      * @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.
40887      * @return {Roo.ContentPanel} this
40888      */
40889     load : function(){
40890         
40891         if (this.iframe) {
40892             return this;
40893         }
40894         
40895         var um = this.el.getUpdateManager();
40896         um.update.apply(um, arguments);
40897         return this;
40898     },
40899
40900
40901     /**
40902      * 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.
40903      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40904      * @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)
40905      * @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)
40906      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40907      */
40908     setUrl : function(url, params, loadOnce){
40909         if (this.iframe) {
40910             this.iframeEl.dom.src = url;
40911             return false;
40912         }
40913         
40914         if(this.refreshDelegate){
40915             this.removeListener("activate", this.refreshDelegate);
40916         }
40917         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40918         this.on("activate", this.refreshDelegate);
40919         return this.el.getUpdateManager();
40920     },
40921     
40922     _handleRefresh : function(url, params, loadOnce){
40923         if(!loadOnce || !this.loaded){
40924             var updater = this.el.getUpdateManager();
40925             updater.update(url, params, this._setLoaded.createDelegate(this));
40926         }
40927     },
40928     
40929     _setLoaded : function(){
40930         this.loaded = true;
40931     }, 
40932     
40933     /**
40934      * Returns this panel's id
40935      * @return {String} 
40936      */
40937     getId : function(){
40938         return this.el.id;
40939     },
40940     
40941     /** 
40942      * Returns this panel's element - used by regiosn to add.
40943      * @return {Roo.Element} 
40944      */
40945     getEl : function(){
40946         return this.wrapEl || this.el;
40947     },
40948     
40949    
40950     
40951     adjustForComponents : function(width, height)
40952     {
40953         //Roo.log('adjustForComponents ');
40954         if(this.resizeEl != this.el){
40955             width -= this.el.getFrameWidth('lr');
40956             height -= this.el.getFrameWidth('tb');
40957         }
40958         if(this.toolbar){
40959             var te = this.toolbar.getEl();
40960             te.setWidth(width);
40961             height -= te.getHeight();
40962         }
40963         if(this.footer){
40964             var te = this.footer.getEl();
40965             te.setWidth(width);
40966             height -= te.getHeight();
40967         }
40968         
40969         
40970         if(this.adjustments){
40971             width += this.adjustments[0];
40972             height += this.adjustments[1];
40973         }
40974         return {"width": width, "height": height};
40975     },
40976     
40977     setSize : function(width, height){
40978         if(this.fitToFrame && !this.ignoreResize(width, height)){
40979             if(this.fitContainer && this.resizeEl != this.el){
40980                 this.el.setSize(width, height);
40981             }
40982             var size = this.adjustForComponents(width, height);
40983             if (this.iframe) {
40984                 this.iframeEl.setSize(width,height);
40985             }
40986             
40987             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40988             this.fireEvent('resize', this, size.width, size.height);
40989             
40990             
40991         }
40992     },
40993     
40994     /**
40995      * Returns this panel's title
40996      * @return {String} 
40997      */
40998     getTitle : function(){
40999         
41000         if (typeof(this.title) != 'object') {
41001             return this.title;
41002         }
41003         
41004         var t = '';
41005         for (var k in this.title) {
41006             if (!this.title.hasOwnProperty(k)) {
41007                 continue;
41008             }
41009             
41010             if (k.indexOf('-') >= 0) {
41011                 var s = k.split('-');
41012                 for (var i = 0; i<s.length; i++) {
41013                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41014                 }
41015             } else {
41016                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41017             }
41018         }
41019         return t;
41020     },
41021     
41022     /**
41023      * Set this panel's title
41024      * @param {String} title
41025      */
41026     setTitle : function(title){
41027         this.title = title;
41028         if(this.region){
41029             this.region.updatePanelTitle(this, title);
41030         }
41031     },
41032     
41033     /**
41034      * Returns true is this panel was configured to be closable
41035      * @return {Boolean} 
41036      */
41037     isClosable : function(){
41038         return this.closable;
41039     },
41040     
41041     beforeSlide : function(){
41042         this.el.clip();
41043         this.resizeEl.clip();
41044     },
41045     
41046     afterSlide : function(){
41047         this.el.unclip();
41048         this.resizeEl.unclip();
41049     },
41050     
41051     /**
41052      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41053      *   Will fail silently if the {@link #setUrl} method has not been called.
41054      *   This does not activate the panel, just updates its content.
41055      */
41056     refresh : function(){
41057         if(this.refreshDelegate){
41058            this.loaded = false;
41059            this.refreshDelegate();
41060         }
41061     },
41062     
41063     /**
41064      * Destroys this panel
41065      */
41066     destroy : function(){
41067         this.el.removeAllListeners();
41068         var tempEl = document.createElement("span");
41069         tempEl.appendChild(this.el.dom);
41070         tempEl.innerHTML = "";
41071         this.el.remove();
41072         this.el = null;
41073     },
41074     
41075     /**
41076      * form - if the content panel contains a form - this is a reference to it.
41077      * @type {Roo.form.Form}
41078      */
41079     form : false,
41080     /**
41081      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41082      *    This contains a reference to it.
41083      * @type {Roo.View}
41084      */
41085     view : false,
41086     
41087       /**
41088      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41089      * <pre><code>
41090
41091 layout.addxtype({
41092        xtype : 'Form',
41093        items: [ .... ]
41094    }
41095 );
41096
41097 </code></pre>
41098      * @param {Object} cfg Xtype definition of item to add.
41099      */
41100     
41101     
41102     getChildContainer: function () {
41103         return this.getEl();
41104     },
41105     
41106     
41107     onScroll : function(e)
41108     {
41109         this.fireEvent('scroll', this, e);
41110     }
41111     
41112     
41113     /*
41114         var  ret = new Roo.factory(cfg);
41115         return ret;
41116         
41117         
41118         // add form..
41119         if (cfg.xtype.match(/^Form$/)) {
41120             
41121             var el;
41122             //if (this.footer) {
41123             //    el = this.footer.container.insertSibling(false, 'before');
41124             //} else {
41125                 el = this.el.createChild();
41126             //}
41127
41128             this.form = new  Roo.form.Form(cfg);
41129             
41130             
41131             if ( this.form.allItems.length) {
41132                 this.form.render(el.dom);
41133             }
41134             return this.form;
41135         }
41136         // should only have one of theses..
41137         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41138             // views.. should not be just added - used named prop 'view''
41139             
41140             cfg.el = this.el.appendChild(document.createElement("div"));
41141             // factory?
41142             
41143             var ret = new Roo.factory(cfg);
41144              
41145              ret.render && ret.render(false, ''); // render blank..
41146             this.view = ret;
41147             return ret;
41148         }
41149         return false;
41150     }
41151     \*/
41152 });
41153  
41154 /**
41155  * @class Roo.bootstrap.panel.Grid
41156  * @extends Roo.bootstrap.panel.Content
41157  * @constructor
41158  * Create a new GridPanel.
41159  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41160  * @param {Object} config A the config object
41161   
41162  */
41163
41164
41165
41166 Roo.bootstrap.panel.Grid = function(config)
41167 {
41168     
41169       
41170     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41171         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41172
41173     config.el = this.wrapper;
41174     //this.el = this.wrapper;
41175     
41176       if (config.container) {
41177         // ctor'ed from a Border/panel.grid
41178         
41179         
41180         this.wrapper.setStyle("overflow", "hidden");
41181         this.wrapper.addClass('roo-grid-container');
41182
41183     }
41184     
41185     
41186     if(config.toolbar){
41187         var tool_el = this.wrapper.createChild();    
41188         this.toolbar = Roo.factory(config.toolbar);
41189         var ti = [];
41190         if (config.toolbar.items) {
41191             ti = config.toolbar.items ;
41192             delete config.toolbar.items ;
41193         }
41194         
41195         var nitems = [];
41196         this.toolbar.render(tool_el);
41197         for(var i =0;i < ti.length;i++) {
41198           //  Roo.log(['add child', items[i]]);
41199             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41200         }
41201         this.toolbar.items = nitems;
41202         
41203         delete config.toolbar;
41204     }
41205     
41206     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41207     config.grid.scrollBody = true;;
41208     config.grid.monitorWindowResize = false; // turn off autosizing
41209     config.grid.autoHeight = false;
41210     config.grid.autoWidth = false;
41211     
41212     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41213     
41214     if (config.background) {
41215         // render grid on panel activation (if panel background)
41216         this.on('activate', function(gp) {
41217             if (!gp.grid.rendered) {
41218                 gp.grid.render(this.wrapper);
41219                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41220             }
41221         });
41222             
41223     } else {
41224         this.grid.render(this.wrapper);
41225         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41226
41227     }
41228     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41229     // ??? needed ??? config.el = this.wrapper;
41230     
41231     
41232     
41233   
41234     // xtype created footer. - not sure if will work as we normally have to render first..
41235     if (this.footer && !this.footer.el && this.footer.xtype) {
41236         
41237         var ctr = this.grid.getView().getFooterPanel(true);
41238         this.footer.dataSource = this.grid.dataSource;
41239         this.footer = Roo.factory(this.footer, Roo);
41240         this.footer.render(ctr);
41241         
41242     }
41243     
41244     
41245     
41246     
41247      
41248 };
41249
41250 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41251     getId : function(){
41252         return this.grid.id;
41253     },
41254     
41255     /**
41256      * Returns the grid for this panel
41257      * @return {Roo.bootstrap.Table} 
41258      */
41259     getGrid : function(){
41260         return this.grid;    
41261     },
41262     
41263     setSize : function(width, height){
41264         if(!this.ignoreResize(width, height)){
41265             var grid = this.grid;
41266             var size = this.adjustForComponents(width, height);
41267             // tfoot is not a footer?
41268           
41269             
41270             var gridel = grid.getGridEl();
41271             gridel.setSize(size.width, size.height);
41272             
41273             var tbd = grid.getGridEl().select('tbody', true).first();
41274             var thd = grid.getGridEl().select('thead',true).first();
41275             var tbf= grid.getGridEl().select('tfoot', true).first();
41276
41277             if (tbf) {
41278                 size.height -= tbf.getHeight();
41279             }
41280             if (thd) {
41281                 size.height -= thd.getHeight();
41282             }
41283             
41284             tbd.setSize(size.width, size.height );
41285             // this is for the account management tab -seems to work there.
41286             var thd = grid.getGridEl().select('thead',true).first();
41287             //if (tbd) {
41288             //    tbd.setSize(size.width, size.height - thd.getHeight());
41289             //}
41290              
41291             grid.autoSize();
41292         }
41293     },
41294      
41295     
41296     
41297     beforeSlide : function(){
41298         this.grid.getView().scroller.clip();
41299     },
41300     
41301     afterSlide : function(){
41302         this.grid.getView().scroller.unclip();
41303     },
41304     
41305     destroy : function(){
41306         this.grid.destroy();
41307         delete this.grid;
41308         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41309     }
41310 });
41311
41312 /**
41313  * @class Roo.bootstrap.panel.Nest
41314  * @extends Roo.bootstrap.panel.Content
41315  * @constructor
41316  * Create a new Panel, that can contain a layout.Border.
41317  * 
41318  * 
41319  * @param {Roo.BorderLayout} layout The layout for this panel
41320  * @param {String/Object} config A string to set only the title or a config object
41321  */
41322 Roo.bootstrap.panel.Nest = function(config)
41323 {
41324     // construct with only one argument..
41325     /* FIXME - implement nicer consturctors
41326     if (layout.layout) {
41327         config = layout;
41328         layout = config.layout;
41329         delete config.layout;
41330     }
41331     if (layout.xtype && !layout.getEl) {
41332         // then layout needs constructing..
41333         layout = Roo.factory(layout, Roo);
41334     }
41335     */
41336     
41337     config.el =  config.layout.getEl();
41338     
41339     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41340     
41341     config.layout.monitorWindowResize = false; // turn off autosizing
41342     this.layout = config.layout;
41343     this.layout.getEl().addClass("roo-layout-nested-layout");
41344     this.layout.parent = this;
41345     
41346     
41347     
41348     
41349 };
41350
41351 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41352
41353     setSize : function(width, height){
41354         if(!this.ignoreResize(width, height)){
41355             var size = this.adjustForComponents(width, height);
41356             var el = this.layout.getEl();
41357             if (size.height < 1) {
41358                 el.setWidth(size.width);   
41359             } else {
41360                 el.setSize(size.width, size.height);
41361             }
41362             var touch = el.dom.offsetWidth;
41363             this.layout.layout();
41364             // ie requires a double layout on the first pass
41365             if(Roo.isIE && !this.initialized){
41366                 this.initialized = true;
41367                 this.layout.layout();
41368             }
41369         }
41370     },
41371     
41372     // activate all subpanels if not currently active..
41373     
41374     setActiveState : function(active){
41375         this.active = active;
41376         this.setActiveClass(active);
41377         
41378         if(!active){
41379             this.fireEvent("deactivate", this);
41380             return;
41381         }
41382         
41383         this.fireEvent("activate", this);
41384         // not sure if this should happen before or after..
41385         if (!this.layout) {
41386             return; // should not happen..
41387         }
41388         var reg = false;
41389         for (var r in this.layout.regions) {
41390             reg = this.layout.getRegion(r);
41391             if (reg.getActivePanel()) {
41392                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41393                 reg.setActivePanel(reg.getActivePanel());
41394                 continue;
41395             }
41396             if (!reg.panels.length) {
41397                 continue;
41398             }
41399             reg.showPanel(reg.getPanel(0));
41400         }
41401         
41402         
41403         
41404         
41405     },
41406     
41407     /**
41408      * Returns the nested BorderLayout for this panel
41409      * @return {Roo.BorderLayout} 
41410      */
41411     getLayout : function(){
41412         return this.layout;
41413     },
41414     
41415      /**
41416      * Adds a xtype elements to the layout of the nested panel
41417      * <pre><code>
41418
41419 panel.addxtype({
41420        xtype : 'ContentPanel',
41421        region: 'west',
41422        items: [ .... ]
41423    }
41424 );
41425
41426 panel.addxtype({
41427         xtype : 'NestedLayoutPanel',
41428         region: 'west',
41429         layout: {
41430            center: { },
41431            west: { }   
41432         },
41433         items : [ ... list of content panels or nested layout panels.. ]
41434    }
41435 );
41436 </code></pre>
41437      * @param {Object} cfg Xtype definition of item to add.
41438      */
41439     addxtype : function(cfg) {
41440         return this.layout.addxtype(cfg);
41441     
41442     }
41443 });/*
41444  * Based on:
41445  * Ext JS Library 1.1.1
41446  * Copyright(c) 2006-2007, Ext JS, LLC.
41447  *
41448  * Originally Released Under LGPL - original licence link has changed is not relivant.
41449  *
41450  * Fork - LGPL
41451  * <script type="text/javascript">
41452  */
41453 /**
41454  * @class Roo.TabPanel
41455  * @extends Roo.util.Observable
41456  * A lightweight tab container.
41457  * <br><br>
41458  * Usage:
41459  * <pre><code>
41460 // basic tabs 1, built from existing content
41461 var tabs = new Roo.TabPanel("tabs1");
41462 tabs.addTab("script", "View Script");
41463 tabs.addTab("markup", "View Markup");
41464 tabs.activate("script");
41465
41466 // more advanced tabs, built from javascript
41467 var jtabs = new Roo.TabPanel("jtabs");
41468 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41469
41470 // set up the UpdateManager
41471 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41472 var updater = tab2.getUpdateManager();
41473 updater.setDefaultUrl("ajax1.htm");
41474 tab2.on('activate', updater.refresh, updater, true);
41475
41476 // Use setUrl for Ajax loading
41477 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41478 tab3.setUrl("ajax2.htm", null, true);
41479
41480 // Disabled tab
41481 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41482 tab4.disable();
41483
41484 jtabs.activate("jtabs-1");
41485  * </code></pre>
41486  * @constructor
41487  * Create a new TabPanel.
41488  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41489  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41490  */
41491 Roo.bootstrap.panel.Tabs = function(config){
41492     /**
41493     * The container element for this TabPanel.
41494     * @type Roo.Element
41495     */
41496     this.el = Roo.get(config.el);
41497     delete config.el;
41498     if(config){
41499         if(typeof config == "boolean"){
41500             this.tabPosition = config ? "bottom" : "top";
41501         }else{
41502             Roo.apply(this, config);
41503         }
41504     }
41505     
41506     if(this.tabPosition == "bottom"){
41507         // if tabs are at the bottom = create the body first.
41508         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41509         this.el.addClass("roo-tabs-bottom");
41510     }
41511     // next create the tabs holders
41512     
41513     if (this.tabPosition == "west"){
41514         
41515         var reg = this.region; // fake it..
41516         while (reg) {
41517             if (!reg.mgr.parent) {
41518                 break;
41519             }
41520             reg = reg.mgr.parent.region;
41521         }
41522         Roo.log("got nest?");
41523         Roo.log(reg);
41524         if (reg.mgr.getRegion('west')) {
41525             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41526             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41527             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41528             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41529             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41530         
41531             
41532         }
41533         
41534         
41535     } else {
41536      
41537         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41538         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41539         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41540         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41541     }
41542     
41543     
41544     if(Roo.isIE){
41545         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41546     }
41547     
41548     // finally - if tabs are at the top, then create the body last..
41549     if(this.tabPosition != "bottom"){
41550         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41551          * @type Roo.Element
41552          */
41553         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41554         this.el.addClass("roo-tabs-top");
41555     }
41556     this.items = [];
41557
41558     this.bodyEl.setStyle("position", "relative");
41559
41560     this.active = null;
41561     this.activateDelegate = this.activate.createDelegate(this);
41562
41563     this.addEvents({
41564         /**
41565          * @event tabchange
41566          * Fires when the active tab changes
41567          * @param {Roo.TabPanel} this
41568          * @param {Roo.TabPanelItem} activePanel The new active tab
41569          */
41570         "tabchange": true,
41571         /**
41572          * @event beforetabchange
41573          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41574          * @param {Roo.TabPanel} this
41575          * @param {Object} e Set cancel to true on this object to cancel the tab change
41576          * @param {Roo.TabPanelItem} tab The tab being changed to
41577          */
41578         "beforetabchange" : true
41579     });
41580
41581     Roo.EventManager.onWindowResize(this.onResize, this);
41582     this.cpad = this.el.getPadding("lr");
41583     this.hiddenCount = 0;
41584
41585
41586     // toolbar on the tabbar support...
41587     if (this.toolbar) {
41588         alert("no toolbar support yet");
41589         this.toolbar  = false;
41590         /*
41591         var tcfg = this.toolbar;
41592         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41593         this.toolbar = new Roo.Toolbar(tcfg);
41594         if (Roo.isSafari) {
41595             var tbl = tcfg.container.child('table', true);
41596             tbl.setAttribute('width', '100%');
41597         }
41598         */
41599         
41600     }
41601    
41602
41603
41604     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41605 };
41606
41607 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41608     /*
41609      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41610      */
41611     tabPosition : "top",
41612     /*
41613      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41614      */
41615     currentTabWidth : 0,
41616     /*
41617      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41618      */
41619     minTabWidth : 40,
41620     /*
41621      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41622      */
41623     maxTabWidth : 250,
41624     /*
41625      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41626      */
41627     preferredTabWidth : 175,
41628     /*
41629      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41630      */
41631     resizeTabs : false,
41632     /*
41633      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41634      */
41635     monitorResize : true,
41636     /*
41637      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41638      */
41639     toolbar : false,  // set by caller..
41640     
41641     region : false, /// set by caller
41642     
41643     disableTooltips : true, // not used yet...
41644
41645     /**
41646      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41647      * @param {String} id The id of the div to use <b>or create</b>
41648      * @param {String} text The text for the tab
41649      * @param {String} content (optional) Content to put in the TabPanelItem body
41650      * @param {Boolean} closable (optional) True to create a close icon on the tab
41651      * @return {Roo.TabPanelItem} The created TabPanelItem
41652      */
41653     addTab : function(id, text, content, closable, tpl)
41654     {
41655         var item = new Roo.bootstrap.panel.TabItem({
41656             panel: this,
41657             id : id,
41658             text : text,
41659             closable : closable,
41660             tpl : tpl
41661         });
41662         this.addTabItem(item);
41663         if(content){
41664             item.setContent(content);
41665         }
41666         return item;
41667     },
41668
41669     /**
41670      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41671      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41672      * @return {Roo.TabPanelItem}
41673      */
41674     getTab : function(id){
41675         return this.items[id];
41676     },
41677
41678     /**
41679      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41680      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41681      */
41682     hideTab : function(id){
41683         var t = this.items[id];
41684         if(!t.isHidden()){
41685            t.setHidden(true);
41686            this.hiddenCount++;
41687            this.autoSizeTabs();
41688         }
41689     },
41690
41691     /**
41692      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41693      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41694      */
41695     unhideTab : function(id){
41696         var t = this.items[id];
41697         if(t.isHidden()){
41698            t.setHidden(false);
41699            this.hiddenCount--;
41700            this.autoSizeTabs();
41701         }
41702     },
41703
41704     /**
41705      * Adds an existing {@link Roo.TabPanelItem}.
41706      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41707      */
41708     addTabItem : function(item)
41709     {
41710         this.items[item.id] = item;
41711         this.items.push(item);
41712         this.autoSizeTabs();
41713       //  if(this.resizeTabs){
41714     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41715   //         this.autoSizeTabs();
41716 //        }else{
41717 //            item.autoSize();
41718        // }
41719     },
41720
41721     /**
41722      * Removes a {@link Roo.TabPanelItem}.
41723      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41724      */
41725     removeTab : function(id){
41726         var items = this.items;
41727         var tab = items[id];
41728         if(!tab) { return; }
41729         var index = items.indexOf(tab);
41730         if(this.active == tab && items.length > 1){
41731             var newTab = this.getNextAvailable(index);
41732             if(newTab) {
41733                 newTab.activate();
41734             }
41735         }
41736         this.stripEl.dom.removeChild(tab.pnode.dom);
41737         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41738             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41739         }
41740         items.splice(index, 1);
41741         delete this.items[tab.id];
41742         tab.fireEvent("close", tab);
41743         tab.purgeListeners();
41744         this.autoSizeTabs();
41745     },
41746
41747     getNextAvailable : function(start){
41748         var items = this.items;
41749         var index = start;
41750         // look for a next tab that will slide over to
41751         // replace the one being removed
41752         while(index < items.length){
41753             var item = items[++index];
41754             if(item && !item.isHidden()){
41755                 return item;
41756             }
41757         }
41758         // if one isn't found select the previous tab (on the left)
41759         index = start;
41760         while(index >= 0){
41761             var item = items[--index];
41762             if(item && !item.isHidden()){
41763                 return item;
41764             }
41765         }
41766         return null;
41767     },
41768
41769     /**
41770      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41771      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41772      */
41773     disableTab : function(id){
41774         var tab = this.items[id];
41775         if(tab && this.active != tab){
41776             tab.disable();
41777         }
41778     },
41779
41780     /**
41781      * Enables a {@link Roo.TabPanelItem} that is disabled.
41782      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41783      */
41784     enableTab : function(id){
41785         var tab = this.items[id];
41786         tab.enable();
41787     },
41788
41789     /**
41790      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41791      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41792      * @return {Roo.TabPanelItem} The TabPanelItem.
41793      */
41794     activate : function(id)
41795     {
41796         //Roo.log('activite:'  + id);
41797         
41798         var tab = this.items[id];
41799         if(!tab){
41800             return null;
41801         }
41802         if(tab == this.active || tab.disabled){
41803             return tab;
41804         }
41805         var e = {};
41806         this.fireEvent("beforetabchange", this, e, tab);
41807         if(e.cancel !== true && !tab.disabled){
41808             if(this.active){
41809                 this.active.hide();
41810             }
41811             this.active = this.items[id];
41812             this.active.show();
41813             this.fireEvent("tabchange", this, this.active);
41814         }
41815         return tab;
41816     },
41817
41818     /**
41819      * Gets the active {@link Roo.TabPanelItem}.
41820      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41821      */
41822     getActiveTab : function(){
41823         return this.active;
41824     },
41825
41826     /**
41827      * Updates the tab body element to fit the height of the container element
41828      * for overflow scrolling
41829      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41830      */
41831     syncHeight : function(targetHeight){
41832         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41833         var bm = this.bodyEl.getMargins();
41834         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41835         this.bodyEl.setHeight(newHeight);
41836         return newHeight;
41837     },
41838
41839     onResize : function(){
41840         if(this.monitorResize){
41841             this.autoSizeTabs();
41842         }
41843     },
41844
41845     /**
41846      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41847      */
41848     beginUpdate : function(){
41849         this.updating = true;
41850     },
41851
41852     /**
41853      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41854      */
41855     endUpdate : function(){
41856         this.updating = false;
41857         this.autoSizeTabs();
41858     },
41859
41860     /**
41861      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41862      */
41863     autoSizeTabs : function()
41864     {
41865         var count = this.items.length;
41866         var vcount = count - this.hiddenCount;
41867         
41868         if (vcount < 2) {
41869             this.stripEl.hide();
41870         } else {
41871             this.stripEl.show();
41872         }
41873         
41874         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41875             return;
41876         }
41877         
41878         
41879         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41880         var availWidth = Math.floor(w / vcount);
41881         var b = this.stripBody;
41882         if(b.getWidth() > w){
41883             var tabs = this.items;
41884             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41885             if(availWidth < this.minTabWidth){
41886                 /*if(!this.sleft){    // incomplete scrolling code
41887                     this.createScrollButtons();
41888                 }
41889                 this.showScroll();
41890                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41891             }
41892         }else{
41893             if(this.currentTabWidth < this.preferredTabWidth){
41894                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41895             }
41896         }
41897     },
41898
41899     /**
41900      * Returns the number of tabs in this TabPanel.
41901      * @return {Number}
41902      */
41903      getCount : function(){
41904          return this.items.length;
41905      },
41906
41907     /**
41908      * Resizes all the tabs to the passed width
41909      * @param {Number} The new width
41910      */
41911     setTabWidth : function(width){
41912         this.currentTabWidth = width;
41913         for(var i = 0, len = this.items.length; i < len; i++) {
41914                 if(!this.items[i].isHidden()) {
41915                 this.items[i].setWidth(width);
41916             }
41917         }
41918     },
41919
41920     /**
41921      * Destroys this TabPanel
41922      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41923      */
41924     destroy : function(removeEl){
41925         Roo.EventManager.removeResizeListener(this.onResize, this);
41926         for(var i = 0, len = this.items.length; i < len; i++){
41927             this.items[i].purgeListeners();
41928         }
41929         if(removeEl === true){
41930             this.el.update("");
41931             this.el.remove();
41932         }
41933     },
41934     
41935     createStrip : function(container)
41936     {
41937         var strip = document.createElement("nav");
41938         strip.className = Roo.bootstrap.version == 4 ?
41939             "navbar-light bg-light" : 
41940             "navbar navbar-default"; //"x-tabs-wrap";
41941         container.appendChild(strip);
41942         return strip;
41943     },
41944     
41945     createStripList : function(strip)
41946     {
41947         // div wrapper for retard IE
41948         // returns the "tr" element.
41949         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41950         //'<div class="x-tabs-strip-wrap">'+
41951           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41952           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41953         return strip.firstChild; //.firstChild.firstChild.firstChild;
41954     },
41955     createBody : function(container)
41956     {
41957         var body = document.createElement("div");
41958         Roo.id(body, "tab-body");
41959         //Roo.fly(body).addClass("x-tabs-body");
41960         Roo.fly(body).addClass("tab-content");
41961         container.appendChild(body);
41962         return body;
41963     },
41964     createItemBody :function(bodyEl, id){
41965         var body = Roo.getDom(id);
41966         if(!body){
41967             body = document.createElement("div");
41968             body.id = id;
41969         }
41970         //Roo.fly(body).addClass("x-tabs-item-body");
41971         Roo.fly(body).addClass("tab-pane");
41972          bodyEl.insertBefore(body, bodyEl.firstChild);
41973         return body;
41974     },
41975     /** @private */
41976     createStripElements :  function(stripEl, text, closable, tpl)
41977     {
41978         var td = document.createElement("li"); // was td..
41979         td.className = 'nav-item';
41980         
41981         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41982         
41983         
41984         stripEl.appendChild(td);
41985         /*if(closable){
41986             td.className = "x-tabs-closable";
41987             if(!this.closeTpl){
41988                 this.closeTpl = new Roo.Template(
41989                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41990                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41991                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41992                 );
41993             }
41994             var el = this.closeTpl.overwrite(td, {"text": text});
41995             var close = el.getElementsByTagName("div")[0];
41996             var inner = el.getElementsByTagName("em")[0];
41997             return {"el": el, "close": close, "inner": inner};
41998         } else {
41999         */
42000         // not sure what this is..
42001 //            if(!this.tabTpl){
42002                 //this.tabTpl = new Roo.Template(
42003                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42004                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42005                 //);
42006 //                this.tabTpl = new Roo.Template(
42007 //                   '<a href="#">' +
42008 //                   '<span unselectable="on"' +
42009 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42010 //                            ' >{text}</span></a>'
42011 //                );
42012 //                
42013 //            }
42014
42015
42016             var template = tpl || this.tabTpl || false;
42017             
42018             if(!template){
42019                 template =  new Roo.Template(
42020                         Roo.bootstrap.version == 4 ? 
42021                             (
42022                                 '<a class="nav-link" href="#" unselectable="on"' +
42023                                      (this.disableTooltips ? '' : ' title="{text}"') +
42024                                      ' >{text}</a>'
42025                             ) : (
42026                                 '<a class="nav-link" href="#">' +
42027                                 '<span unselectable="on"' +
42028                                          (this.disableTooltips ? '' : ' title="{text}"') +
42029                                     ' >{text}</span></a>'
42030                             )
42031                 );
42032             }
42033             
42034             switch (typeof(template)) {
42035                 case 'object' :
42036                     break;
42037                 case 'string' :
42038                     template = new Roo.Template(template);
42039                     break;
42040                 default :
42041                     break;
42042             }
42043             
42044             var el = template.overwrite(td, {"text": text});
42045             
42046             var inner = el.getElementsByTagName("span")[0];
42047             
42048             return {"el": el, "inner": inner};
42049             
42050     }
42051         
42052     
42053 });
42054
42055 /**
42056  * @class Roo.TabPanelItem
42057  * @extends Roo.util.Observable
42058  * Represents an individual item (tab plus body) in a TabPanel.
42059  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42060  * @param {String} id The id of this TabPanelItem
42061  * @param {String} text The text for the tab of this TabPanelItem
42062  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42063  */
42064 Roo.bootstrap.panel.TabItem = function(config){
42065     /**
42066      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42067      * @type Roo.TabPanel
42068      */
42069     this.tabPanel = config.panel;
42070     /**
42071      * The id for this TabPanelItem
42072      * @type String
42073      */
42074     this.id = config.id;
42075     /** @private */
42076     this.disabled = false;
42077     /** @private */
42078     this.text = config.text;
42079     /** @private */
42080     this.loaded = false;
42081     this.closable = config.closable;
42082
42083     /**
42084      * The body element for this TabPanelItem.
42085      * @type Roo.Element
42086      */
42087     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42088     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42089     this.bodyEl.setStyle("display", "block");
42090     this.bodyEl.setStyle("zoom", "1");
42091     //this.hideAction();
42092
42093     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42094     /** @private */
42095     this.el = Roo.get(els.el);
42096     this.inner = Roo.get(els.inner, true);
42097      this.textEl = Roo.bootstrap.version == 4 ?
42098         this.el : Roo.get(this.el.dom.firstChild, true);
42099
42100     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42101     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42102
42103     
42104 //    this.el.on("mousedown", this.onTabMouseDown, this);
42105     this.el.on("click", this.onTabClick, this);
42106     /** @private */
42107     if(config.closable){
42108         var c = Roo.get(els.close, true);
42109         c.dom.title = this.closeText;
42110         c.addClassOnOver("close-over");
42111         c.on("click", this.closeClick, this);
42112      }
42113
42114     this.addEvents({
42115          /**
42116          * @event activate
42117          * Fires when this tab becomes the active tab.
42118          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42119          * @param {Roo.TabPanelItem} this
42120          */
42121         "activate": true,
42122         /**
42123          * @event beforeclose
42124          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42125          * @param {Roo.TabPanelItem} this
42126          * @param {Object} e Set cancel to true on this object to cancel the close.
42127          */
42128         "beforeclose": true,
42129         /**
42130          * @event close
42131          * Fires when this tab is closed.
42132          * @param {Roo.TabPanelItem} this
42133          */
42134          "close": true,
42135         /**
42136          * @event deactivate
42137          * Fires when this tab is no longer the active tab.
42138          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42139          * @param {Roo.TabPanelItem} this
42140          */
42141          "deactivate" : true
42142     });
42143     this.hidden = false;
42144
42145     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42146 };
42147
42148 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42149            {
42150     purgeListeners : function(){
42151        Roo.util.Observable.prototype.purgeListeners.call(this);
42152        this.el.removeAllListeners();
42153     },
42154     /**
42155      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42156      */
42157     show : function(){
42158         this.status_node.addClass("active");
42159         this.showAction();
42160         if(Roo.isOpera){
42161             this.tabPanel.stripWrap.repaint();
42162         }
42163         this.fireEvent("activate", this.tabPanel, this);
42164     },
42165
42166     /**
42167      * Returns true if this tab is the active tab.
42168      * @return {Boolean}
42169      */
42170     isActive : function(){
42171         return this.tabPanel.getActiveTab() == this;
42172     },
42173
42174     /**
42175      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42176      */
42177     hide : function(){
42178         this.status_node.removeClass("active");
42179         this.hideAction();
42180         this.fireEvent("deactivate", this.tabPanel, this);
42181     },
42182
42183     hideAction : function(){
42184         this.bodyEl.hide();
42185         this.bodyEl.setStyle("position", "absolute");
42186         this.bodyEl.setLeft("-20000px");
42187         this.bodyEl.setTop("-20000px");
42188     },
42189
42190     showAction : function(){
42191         this.bodyEl.setStyle("position", "relative");
42192         this.bodyEl.setTop("");
42193         this.bodyEl.setLeft("");
42194         this.bodyEl.show();
42195     },
42196
42197     /**
42198      * Set the tooltip for the tab.
42199      * @param {String} tooltip The tab's tooltip
42200      */
42201     setTooltip : function(text){
42202         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42203             this.textEl.dom.qtip = text;
42204             this.textEl.dom.removeAttribute('title');
42205         }else{
42206             this.textEl.dom.title = text;
42207         }
42208     },
42209
42210     onTabClick : function(e){
42211         e.preventDefault();
42212         this.tabPanel.activate(this.id);
42213     },
42214
42215     onTabMouseDown : function(e){
42216         e.preventDefault();
42217         this.tabPanel.activate(this.id);
42218     },
42219 /*
42220     getWidth : function(){
42221         return this.inner.getWidth();
42222     },
42223
42224     setWidth : function(width){
42225         var iwidth = width - this.linode.getPadding("lr");
42226         this.inner.setWidth(iwidth);
42227         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42228         this.linode.setWidth(width);
42229     },
42230 */
42231     /**
42232      * Show or hide the tab
42233      * @param {Boolean} hidden True to hide or false to show.
42234      */
42235     setHidden : function(hidden){
42236         this.hidden = hidden;
42237         this.linode.setStyle("display", hidden ? "none" : "");
42238     },
42239
42240     /**
42241      * Returns true if this tab is "hidden"
42242      * @return {Boolean}
42243      */
42244     isHidden : function(){
42245         return this.hidden;
42246     },
42247
42248     /**
42249      * Returns the text for this tab
42250      * @return {String}
42251      */
42252     getText : function(){
42253         return this.text;
42254     },
42255     /*
42256     autoSize : function(){
42257         //this.el.beginMeasure();
42258         this.textEl.setWidth(1);
42259         /*
42260          *  #2804 [new] Tabs in Roojs
42261          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42262          */
42263         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42264         //this.el.endMeasure();
42265     //},
42266
42267     /**
42268      * Sets the text for the tab (Note: this also sets the tooltip text)
42269      * @param {String} text The tab's text and tooltip
42270      */
42271     setText : function(text){
42272         this.text = text;
42273         this.textEl.update(text);
42274         this.setTooltip(text);
42275         //if(!this.tabPanel.resizeTabs){
42276         //    this.autoSize();
42277         //}
42278     },
42279     /**
42280      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42281      */
42282     activate : function(){
42283         this.tabPanel.activate(this.id);
42284     },
42285
42286     /**
42287      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42288      */
42289     disable : function(){
42290         if(this.tabPanel.active != this){
42291             this.disabled = true;
42292             this.status_node.addClass("disabled");
42293         }
42294     },
42295
42296     /**
42297      * Enables this TabPanelItem if it was previously disabled.
42298      */
42299     enable : function(){
42300         this.disabled = false;
42301         this.status_node.removeClass("disabled");
42302     },
42303
42304     /**
42305      * Sets the content for this TabPanelItem.
42306      * @param {String} content The content
42307      * @param {Boolean} loadScripts true to look for and load scripts
42308      */
42309     setContent : function(content, loadScripts){
42310         this.bodyEl.update(content, loadScripts);
42311     },
42312
42313     /**
42314      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42315      * @return {Roo.UpdateManager} The UpdateManager
42316      */
42317     getUpdateManager : function(){
42318         return this.bodyEl.getUpdateManager();
42319     },
42320
42321     /**
42322      * Set a URL to be used to load the content for this TabPanelItem.
42323      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42324      * @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)
42325      * @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)
42326      * @return {Roo.UpdateManager} The UpdateManager
42327      */
42328     setUrl : function(url, params, loadOnce){
42329         if(this.refreshDelegate){
42330             this.un('activate', this.refreshDelegate);
42331         }
42332         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42333         this.on("activate", this.refreshDelegate);
42334         return this.bodyEl.getUpdateManager();
42335     },
42336
42337     /** @private */
42338     _handleRefresh : function(url, params, loadOnce){
42339         if(!loadOnce || !this.loaded){
42340             var updater = this.bodyEl.getUpdateManager();
42341             updater.update(url, params, this._setLoaded.createDelegate(this));
42342         }
42343     },
42344
42345     /**
42346      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42347      *   Will fail silently if the setUrl method has not been called.
42348      *   This does not activate the panel, just updates its content.
42349      */
42350     refresh : function(){
42351         if(this.refreshDelegate){
42352            this.loaded = false;
42353            this.refreshDelegate();
42354         }
42355     },
42356
42357     /** @private */
42358     _setLoaded : function(){
42359         this.loaded = true;
42360     },
42361
42362     /** @private */
42363     closeClick : function(e){
42364         var o = {};
42365         e.stopEvent();
42366         this.fireEvent("beforeclose", this, o);
42367         if(o.cancel !== true){
42368             this.tabPanel.removeTab(this.id);
42369         }
42370     },
42371     /**
42372      * The text displayed in the tooltip for the close icon.
42373      * @type String
42374      */
42375     closeText : "Close this tab"
42376 });
42377 /**
42378 *    This script refer to:
42379 *    Title: International Telephone Input
42380 *    Author: Jack O'Connor
42381 *    Code version:  v12.1.12
42382 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42383 **/
42384
42385 Roo.bootstrap.PhoneInputData = function() {
42386     var d = [
42387       [
42388         "Afghanistan (‫افغانستان‬‎)",
42389         "af",
42390         "93"
42391       ],
42392       [
42393         "Albania (Shqipëri)",
42394         "al",
42395         "355"
42396       ],
42397       [
42398         "Algeria (‫الجزائر‬‎)",
42399         "dz",
42400         "213"
42401       ],
42402       [
42403         "American Samoa",
42404         "as",
42405         "1684"
42406       ],
42407       [
42408         "Andorra",
42409         "ad",
42410         "376"
42411       ],
42412       [
42413         "Angola",
42414         "ao",
42415         "244"
42416       ],
42417       [
42418         "Anguilla",
42419         "ai",
42420         "1264"
42421       ],
42422       [
42423         "Antigua and Barbuda",
42424         "ag",
42425         "1268"
42426       ],
42427       [
42428         "Argentina",
42429         "ar",
42430         "54"
42431       ],
42432       [
42433         "Armenia (Հայաստան)",
42434         "am",
42435         "374"
42436       ],
42437       [
42438         "Aruba",
42439         "aw",
42440         "297"
42441       ],
42442       [
42443         "Australia",
42444         "au",
42445         "61",
42446         0
42447       ],
42448       [
42449         "Austria (Österreich)",
42450         "at",
42451         "43"
42452       ],
42453       [
42454         "Azerbaijan (Azərbaycan)",
42455         "az",
42456         "994"
42457       ],
42458       [
42459         "Bahamas",
42460         "bs",
42461         "1242"
42462       ],
42463       [
42464         "Bahrain (‫البحرين‬‎)",
42465         "bh",
42466         "973"
42467       ],
42468       [
42469         "Bangladesh (বাংলাদেশ)",
42470         "bd",
42471         "880"
42472       ],
42473       [
42474         "Barbados",
42475         "bb",
42476         "1246"
42477       ],
42478       [
42479         "Belarus (Беларусь)",
42480         "by",
42481         "375"
42482       ],
42483       [
42484         "Belgium (België)",
42485         "be",
42486         "32"
42487       ],
42488       [
42489         "Belize",
42490         "bz",
42491         "501"
42492       ],
42493       [
42494         "Benin (Bénin)",
42495         "bj",
42496         "229"
42497       ],
42498       [
42499         "Bermuda",
42500         "bm",
42501         "1441"
42502       ],
42503       [
42504         "Bhutan (འབྲུག)",
42505         "bt",
42506         "975"
42507       ],
42508       [
42509         "Bolivia",
42510         "bo",
42511         "591"
42512       ],
42513       [
42514         "Bosnia and Herzegovina (Босна и Херцеговина)",
42515         "ba",
42516         "387"
42517       ],
42518       [
42519         "Botswana",
42520         "bw",
42521         "267"
42522       ],
42523       [
42524         "Brazil (Brasil)",
42525         "br",
42526         "55"
42527       ],
42528       [
42529         "British Indian Ocean Territory",
42530         "io",
42531         "246"
42532       ],
42533       [
42534         "British Virgin Islands",
42535         "vg",
42536         "1284"
42537       ],
42538       [
42539         "Brunei",
42540         "bn",
42541         "673"
42542       ],
42543       [
42544         "Bulgaria (България)",
42545         "bg",
42546         "359"
42547       ],
42548       [
42549         "Burkina Faso",
42550         "bf",
42551         "226"
42552       ],
42553       [
42554         "Burundi (Uburundi)",
42555         "bi",
42556         "257"
42557       ],
42558       [
42559         "Cambodia (កម្ពុជា)",
42560         "kh",
42561         "855"
42562       ],
42563       [
42564         "Cameroon (Cameroun)",
42565         "cm",
42566         "237"
42567       ],
42568       [
42569         "Canada",
42570         "ca",
42571         "1",
42572         1,
42573         ["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"]
42574       ],
42575       [
42576         "Cape Verde (Kabu Verdi)",
42577         "cv",
42578         "238"
42579       ],
42580       [
42581         "Caribbean Netherlands",
42582         "bq",
42583         "599",
42584         1
42585       ],
42586       [
42587         "Cayman Islands",
42588         "ky",
42589         "1345"
42590       ],
42591       [
42592         "Central African Republic (République centrafricaine)",
42593         "cf",
42594         "236"
42595       ],
42596       [
42597         "Chad (Tchad)",
42598         "td",
42599         "235"
42600       ],
42601       [
42602         "Chile",
42603         "cl",
42604         "56"
42605       ],
42606       [
42607         "China (中国)",
42608         "cn",
42609         "86"
42610       ],
42611       [
42612         "Christmas Island",
42613         "cx",
42614         "61",
42615         2
42616       ],
42617       [
42618         "Cocos (Keeling) Islands",
42619         "cc",
42620         "61",
42621         1
42622       ],
42623       [
42624         "Colombia",
42625         "co",
42626         "57"
42627       ],
42628       [
42629         "Comoros (‫جزر القمر‬‎)",
42630         "km",
42631         "269"
42632       ],
42633       [
42634         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42635         "cd",
42636         "243"
42637       ],
42638       [
42639         "Congo (Republic) (Congo-Brazzaville)",
42640         "cg",
42641         "242"
42642       ],
42643       [
42644         "Cook Islands",
42645         "ck",
42646         "682"
42647       ],
42648       [
42649         "Costa Rica",
42650         "cr",
42651         "506"
42652       ],
42653       [
42654         "Côte d’Ivoire",
42655         "ci",
42656         "225"
42657       ],
42658       [
42659         "Croatia (Hrvatska)",
42660         "hr",
42661         "385"
42662       ],
42663       [
42664         "Cuba",
42665         "cu",
42666         "53"
42667       ],
42668       [
42669         "Curaçao",
42670         "cw",
42671         "599",
42672         0
42673       ],
42674       [
42675         "Cyprus (Κύπρος)",
42676         "cy",
42677         "357"
42678       ],
42679       [
42680         "Czech Republic (Česká republika)",
42681         "cz",
42682         "420"
42683       ],
42684       [
42685         "Denmark (Danmark)",
42686         "dk",
42687         "45"
42688       ],
42689       [
42690         "Djibouti",
42691         "dj",
42692         "253"
42693       ],
42694       [
42695         "Dominica",
42696         "dm",
42697         "1767"
42698       ],
42699       [
42700         "Dominican Republic (República Dominicana)",
42701         "do",
42702         "1",
42703         2,
42704         ["809", "829", "849"]
42705       ],
42706       [
42707         "Ecuador",
42708         "ec",
42709         "593"
42710       ],
42711       [
42712         "Egypt (‫مصر‬‎)",
42713         "eg",
42714         "20"
42715       ],
42716       [
42717         "El Salvador",
42718         "sv",
42719         "503"
42720       ],
42721       [
42722         "Equatorial Guinea (Guinea Ecuatorial)",
42723         "gq",
42724         "240"
42725       ],
42726       [
42727         "Eritrea",
42728         "er",
42729         "291"
42730       ],
42731       [
42732         "Estonia (Eesti)",
42733         "ee",
42734         "372"
42735       ],
42736       [
42737         "Ethiopia",
42738         "et",
42739         "251"
42740       ],
42741       [
42742         "Falkland Islands (Islas Malvinas)",
42743         "fk",
42744         "500"
42745       ],
42746       [
42747         "Faroe Islands (Føroyar)",
42748         "fo",
42749         "298"
42750       ],
42751       [
42752         "Fiji",
42753         "fj",
42754         "679"
42755       ],
42756       [
42757         "Finland (Suomi)",
42758         "fi",
42759         "358",
42760         0
42761       ],
42762       [
42763         "France",
42764         "fr",
42765         "33"
42766       ],
42767       [
42768         "French Guiana (Guyane française)",
42769         "gf",
42770         "594"
42771       ],
42772       [
42773         "French Polynesia (Polynésie française)",
42774         "pf",
42775         "689"
42776       ],
42777       [
42778         "Gabon",
42779         "ga",
42780         "241"
42781       ],
42782       [
42783         "Gambia",
42784         "gm",
42785         "220"
42786       ],
42787       [
42788         "Georgia (საქართველო)",
42789         "ge",
42790         "995"
42791       ],
42792       [
42793         "Germany (Deutschland)",
42794         "de",
42795         "49"
42796       ],
42797       [
42798         "Ghana (Gaana)",
42799         "gh",
42800         "233"
42801       ],
42802       [
42803         "Gibraltar",
42804         "gi",
42805         "350"
42806       ],
42807       [
42808         "Greece (Ελλάδα)",
42809         "gr",
42810         "30"
42811       ],
42812       [
42813         "Greenland (Kalaallit Nunaat)",
42814         "gl",
42815         "299"
42816       ],
42817       [
42818         "Grenada",
42819         "gd",
42820         "1473"
42821       ],
42822       [
42823         "Guadeloupe",
42824         "gp",
42825         "590",
42826         0
42827       ],
42828       [
42829         "Guam",
42830         "gu",
42831         "1671"
42832       ],
42833       [
42834         "Guatemala",
42835         "gt",
42836         "502"
42837       ],
42838       [
42839         "Guernsey",
42840         "gg",
42841         "44",
42842         1
42843       ],
42844       [
42845         "Guinea (Guinée)",
42846         "gn",
42847         "224"
42848       ],
42849       [
42850         "Guinea-Bissau (Guiné Bissau)",
42851         "gw",
42852         "245"
42853       ],
42854       [
42855         "Guyana",
42856         "gy",
42857         "592"
42858       ],
42859       [
42860         "Haiti",
42861         "ht",
42862         "509"
42863       ],
42864       [
42865         "Honduras",
42866         "hn",
42867         "504"
42868       ],
42869       [
42870         "Hong Kong (香港)",
42871         "hk",
42872         "852"
42873       ],
42874       [
42875         "Hungary (Magyarország)",
42876         "hu",
42877         "36"
42878       ],
42879       [
42880         "Iceland (Ísland)",
42881         "is",
42882         "354"
42883       ],
42884       [
42885         "India (भारत)",
42886         "in",
42887         "91"
42888       ],
42889       [
42890         "Indonesia",
42891         "id",
42892         "62"
42893       ],
42894       [
42895         "Iran (‫ایران‬‎)",
42896         "ir",
42897         "98"
42898       ],
42899       [
42900         "Iraq (‫العراق‬‎)",
42901         "iq",
42902         "964"
42903       ],
42904       [
42905         "Ireland",
42906         "ie",
42907         "353"
42908       ],
42909       [
42910         "Isle of Man",
42911         "im",
42912         "44",
42913         2
42914       ],
42915       [
42916         "Israel (‫ישראל‬‎)",
42917         "il",
42918         "972"
42919       ],
42920       [
42921         "Italy (Italia)",
42922         "it",
42923         "39",
42924         0
42925       ],
42926       [
42927         "Jamaica",
42928         "jm",
42929         "1876"
42930       ],
42931       [
42932         "Japan (日本)",
42933         "jp",
42934         "81"
42935       ],
42936       [
42937         "Jersey",
42938         "je",
42939         "44",
42940         3
42941       ],
42942       [
42943         "Jordan (‫الأردن‬‎)",
42944         "jo",
42945         "962"
42946       ],
42947       [
42948         "Kazakhstan (Казахстан)",
42949         "kz",
42950         "7",
42951         1
42952       ],
42953       [
42954         "Kenya",
42955         "ke",
42956         "254"
42957       ],
42958       [
42959         "Kiribati",
42960         "ki",
42961         "686"
42962       ],
42963       [
42964         "Kosovo",
42965         "xk",
42966         "383"
42967       ],
42968       [
42969         "Kuwait (‫الكويت‬‎)",
42970         "kw",
42971         "965"
42972       ],
42973       [
42974         "Kyrgyzstan (Кыргызстан)",
42975         "kg",
42976         "996"
42977       ],
42978       [
42979         "Laos (ລາວ)",
42980         "la",
42981         "856"
42982       ],
42983       [
42984         "Latvia (Latvija)",
42985         "lv",
42986         "371"
42987       ],
42988       [
42989         "Lebanon (‫لبنان‬‎)",
42990         "lb",
42991         "961"
42992       ],
42993       [
42994         "Lesotho",
42995         "ls",
42996         "266"
42997       ],
42998       [
42999         "Liberia",
43000         "lr",
43001         "231"
43002       ],
43003       [
43004         "Libya (‫ليبيا‬‎)",
43005         "ly",
43006         "218"
43007       ],
43008       [
43009         "Liechtenstein",
43010         "li",
43011         "423"
43012       ],
43013       [
43014         "Lithuania (Lietuva)",
43015         "lt",
43016         "370"
43017       ],
43018       [
43019         "Luxembourg",
43020         "lu",
43021         "352"
43022       ],
43023       [
43024         "Macau (澳門)",
43025         "mo",
43026         "853"
43027       ],
43028       [
43029         "Macedonia (FYROM) (Македонија)",
43030         "mk",
43031         "389"
43032       ],
43033       [
43034         "Madagascar (Madagasikara)",
43035         "mg",
43036         "261"
43037       ],
43038       [
43039         "Malawi",
43040         "mw",
43041         "265"
43042       ],
43043       [
43044         "Malaysia",
43045         "my",
43046         "60"
43047       ],
43048       [
43049         "Maldives",
43050         "mv",
43051         "960"
43052       ],
43053       [
43054         "Mali",
43055         "ml",
43056         "223"
43057       ],
43058       [
43059         "Malta",
43060         "mt",
43061         "356"
43062       ],
43063       [
43064         "Marshall Islands",
43065         "mh",
43066         "692"
43067       ],
43068       [
43069         "Martinique",
43070         "mq",
43071         "596"
43072       ],
43073       [
43074         "Mauritania (‫موريتانيا‬‎)",
43075         "mr",
43076         "222"
43077       ],
43078       [
43079         "Mauritius (Moris)",
43080         "mu",
43081         "230"
43082       ],
43083       [
43084         "Mayotte",
43085         "yt",
43086         "262",
43087         1
43088       ],
43089       [
43090         "Mexico (México)",
43091         "mx",
43092         "52"
43093       ],
43094       [
43095         "Micronesia",
43096         "fm",
43097         "691"
43098       ],
43099       [
43100         "Moldova (Republica Moldova)",
43101         "md",
43102         "373"
43103       ],
43104       [
43105         "Monaco",
43106         "mc",
43107         "377"
43108       ],
43109       [
43110         "Mongolia (Монгол)",
43111         "mn",
43112         "976"
43113       ],
43114       [
43115         "Montenegro (Crna Gora)",
43116         "me",
43117         "382"
43118       ],
43119       [
43120         "Montserrat",
43121         "ms",
43122         "1664"
43123       ],
43124       [
43125         "Morocco (‫المغرب‬‎)",
43126         "ma",
43127         "212",
43128         0
43129       ],
43130       [
43131         "Mozambique (Moçambique)",
43132         "mz",
43133         "258"
43134       ],
43135       [
43136         "Myanmar (Burma) (မြန်မာ)",
43137         "mm",
43138         "95"
43139       ],
43140       [
43141         "Namibia (Namibië)",
43142         "na",
43143         "264"
43144       ],
43145       [
43146         "Nauru",
43147         "nr",
43148         "674"
43149       ],
43150       [
43151         "Nepal (नेपाल)",
43152         "np",
43153         "977"
43154       ],
43155       [
43156         "Netherlands (Nederland)",
43157         "nl",
43158         "31"
43159       ],
43160       [
43161         "New Caledonia (Nouvelle-Calédonie)",
43162         "nc",
43163         "687"
43164       ],
43165       [
43166         "New Zealand",
43167         "nz",
43168         "64"
43169       ],
43170       [
43171         "Nicaragua",
43172         "ni",
43173         "505"
43174       ],
43175       [
43176         "Niger (Nijar)",
43177         "ne",
43178         "227"
43179       ],
43180       [
43181         "Nigeria",
43182         "ng",
43183         "234"
43184       ],
43185       [
43186         "Niue",
43187         "nu",
43188         "683"
43189       ],
43190       [
43191         "Norfolk Island",
43192         "nf",
43193         "672"
43194       ],
43195       [
43196         "North Korea (조선 민주주의 인민 공화국)",
43197         "kp",
43198         "850"
43199       ],
43200       [
43201         "Northern Mariana Islands",
43202         "mp",
43203         "1670"
43204       ],
43205       [
43206         "Norway (Norge)",
43207         "no",
43208         "47",
43209         0
43210       ],
43211       [
43212         "Oman (‫عُمان‬‎)",
43213         "om",
43214         "968"
43215       ],
43216       [
43217         "Pakistan (‫پاکستان‬‎)",
43218         "pk",
43219         "92"
43220       ],
43221       [
43222         "Palau",
43223         "pw",
43224         "680"
43225       ],
43226       [
43227         "Palestine (‫فلسطين‬‎)",
43228         "ps",
43229         "970"
43230       ],
43231       [
43232         "Panama (Panamá)",
43233         "pa",
43234         "507"
43235       ],
43236       [
43237         "Papua New Guinea",
43238         "pg",
43239         "675"
43240       ],
43241       [
43242         "Paraguay",
43243         "py",
43244         "595"
43245       ],
43246       [
43247         "Peru (Perú)",
43248         "pe",
43249         "51"
43250       ],
43251       [
43252         "Philippines",
43253         "ph",
43254         "63"
43255       ],
43256       [
43257         "Poland (Polska)",
43258         "pl",
43259         "48"
43260       ],
43261       [
43262         "Portugal",
43263         "pt",
43264         "351"
43265       ],
43266       [
43267         "Puerto Rico",
43268         "pr",
43269         "1",
43270         3,
43271         ["787", "939"]
43272       ],
43273       [
43274         "Qatar (‫قطر‬‎)",
43275         "qa",
43276         "974"
43277       ],
43278       [
43279         "Réunion (La Réunion)",
43280         "re",
43281         "262",
43282         0
43283       ],
43284       [
43285         "Romania (România)",
43286         "ro",
43287         "40"
43288       ],
43289       [
43290         "Russia (Россия)",
43291         "ru",
43292         "7",
43293         0
43294       ],
43295       [
43296         "Rwanda",
43297         "rw",
43298         "250"
43299       ],
43300       [
43301         "Saint Barthélemy",
43302         "bl",
43303         "590",
43304         1
43305       ],
43306       [
43307         "Saint Helena",
43308         "sh",
43309         "290"
43310       ],
43311       [
43312         "Saint Kitts and Nevis",
43313         "kn",
43314         "1869"
43315       ],
43316       [
43317         "Saint Lucia",
43318         "lc",
43319         "1758"
43320       ],
43321       [
43322         "Saint Martin (Saint-Martin (partie française))",
43323         "mf",
43324         "590",
43325         2
43326       ],
43327       [
43328         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43329         "pm",
43330         "508"
43331       ],
43332       [
43333         "Saint Vincent and the Grenadines",
43334         "vc",
43335         "1784"
43336       ],
43337       [
43338         "Samoa",
43339         "ws",
43340         "685"
43341       ],
43342       [
43343         "San Marino",
43344         "sm",
43345         "378"
43346       ],
43347       [
43348         "São Tomé and Príncipe (São Tomé e Príncipe)",
43349         "st",
43350         "239"
43351       ],
43352       [
43353         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43354         "sa",
43355         "966"
43356       ],
43357       [
43358         "Senegal (Sénégal)",
43359         "sn",
43360         "221"
43361       ],
43362       [
43363         "Serbia (Србија)",
43364         "rs",
43365         "381"
43366       ],
43367       [
43368         "Seychelles",
43369         "sc",
43370         "248"
43371       ],
43372       [
43373         "Sierra Leone",
43374         "sl",
43375         "232"
43376       ],
43377       [
43378         "Singapore",
43379         "sg",
43380         "65"
43381       ],
43382       [
43383         "Sint Maarten",
43384         "sx",
43385         "1721"
43386       ],
43387       [
43388         "Slovakia (Slovensko)",
43389         "sk",
43390         "421"
43391       ],
43392       [
43393         "Slovenia (Slovenija)",
43394         "si",
43395         "386"
43396       ],
43397       [
43398         "Solomon Islands",
43399         "sb",
43400         "677"
43401       ],
43402       [
43403         "Somalia (Soomaaliya)",
43404         "so",
43405         "252"
43406       ],
43407       [
43408         "South Africa",
43409         "za",
43410         "27"
43411       ],
43412       [
43413         "South Korea (대한민국)",
43414         "kr",
43415         "82"
43416       ],
43417       [
43418         "South Sudan (‫جنوب السودان‬‎)",
43419         "ss",
43420         "211"
43421       ],
43422       [
43423         "Spain (España)",
43424         "es",
43425         "34"
43426       ],
43427       [
43428         "Sri Lanka (ශ්‍රී ලංකාව)",
43429         "lk",
43430         "94"
43431       ],
43432       [
43433         "Sudan (‫السودان‬‎)",
43434         "sd",
43435         "249"
43436       ],
43437       [
43438         "Suriname",
43439         "sr",
43440         "597"
43441       ],
43442       [
43443         "Svalbard and Jan Mayen",
43444         "sj",
43445         "47",
43446         1
43447       ],
43448       [
43449         "Swaziland",
43450         "sz",
43451         "268"
43452       ],
43453       [
43454         "Sweden (Sverige)",
43455         "se",
43456         "46"
43457       ],
43458       [
43459         "Switzerland (Schweiz)",
43460         "ch",
43461         "41"
43462       ],
43463       [
43464         "Syria (‫سوريا‬‎)",
43465         "sy",
43466         "963"
43467       ],
43468       [
43469         "Taiwan (台灣)",
43470         "tw",
43471         "886"
43472       ],
43473       [
43474         "Tajikistan",
43475         "tj",
43476         "992"
43477       ],
43478       [
43479         "Tanzania",
43480         "tz",
43481         "255"
43482       ],
43483       [
43484         "Thailand (ไทย)",
43485         "th",
43486         "66"
43487       ],
43488       [
43489         "Timor-Leste",
43490         "tl",
43491         "670"
43492       ],
43493       [
43494         "Togo",
43495         "tg",
43496         "228"
43497       ],
43498       [
43499         "Tokelau",
43500         "tk",
43501         "690"
43502       ],
43503       [
43504         "Tonga",
43505         "to",
43506         "676"
43507       ],
43508       [
43509         "Trinidad and Tobago",
43510         "tt",
43511         "1868"
43512       ],
43513       [
43514         "Tunisia (‫تونس‬‎)",
43515         "tn",
43516         "216"
43517       ],
43518       [
43519         "Turkey (Türkiye)",
43520         "tr",
43521         "90"
43522       ],
43523       [
43524         "Turkmenistan",
43525         "tm",
43526         "993"
43527       ],
43528       [
43529         "Turks and Caicos Islands",
43530         "tc",
43531         "1649"
43532       ],
43533       [
43534         "Tuvalu",
43535         "tv",
43536         "688"
43537       ],
43538       [
43539         "U.S. Virgin Islands",
43540         "vi",
43541         "1340"
43542       ],
43543       [
43544         "Uganda",
43545         "ug",
43546         "256"
43547       ],
43548       [
43549         "Ukraine (Україна)",
43550         "ua",
43551         "380"
43552       ],
43553       [
43554         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43555         "ae",
43556         "971"
43557       ],
43558       [
43559         "United Kingdom",
43560         "gb",
43561         "44",
43562         0
43563       ],
43564       [
43565         "United States",
43566         "us",
43567         "1",
43568         0
43569       ],
43570       [
43571         "Uruguay",
43572         "uy",
43573         "598"
43574       ],
43575       [
43576         "Uzbekistan (Oʻzbekiston)",
43577         "uz",
43578         "998"
43579       ],
43580       [
43581         "Vanuatu",
43582         "vu",
43583         "678"
43584       ],
43585       [
43586         "Vatican City (Città del Vaticano)",
43587         "va",
43588         "39",
43589         1
43590       ],
43591       [
43592         "Venezuela",
43593         "ve",
43594         "58"
43595       ],
43596       [
43597         "Vietnam (Việt Nam)",
43598         "vn",
43599         "84"
43600       ],
43601       [
43602         "Wallis and Futuna (Wallis-et-Futuna)",
43603         "wf",
43604         "681"
43605       ],
43606       [
43607         "Western Sahara (‫الصحراء الغربية‬‎)",
43608         "eh",
43609         "212",
43610         1
43611       ],
43612       [
43613         "Yemen (‫اليمن‬‎)",
43614         "ye",
43615         "967"
43616       ],
43617       [
43618         "Zambia",
43619         "zm",
43620         "260"
43621       ],
43622       [
43623         "Zimbabwe",
43624         "zw",
43625         "263"
43626       ],
43627       [
43628         "Åland Islands",
43629         "ax",
43630         "358",
43631         1
43632       ]
43633   ];
43634   
43635   return d;
43636 }/**
43637 *    This script refer to:
43638 *    Title: International Telephone Input
43639 *    Author: Jack O'Connor
43640 *    Code version:  v12.1.12
43641 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43642 **/
43643
43644 /**
43645  * @class Roo.bootstrap.PhoneInput
43646  * @extends Roo.bootstrap.TriggerField
43647  * An input with International dial-code selection
43648  
43649  * @cfg {String} defaultDialCode default '+852'
43650  * @cfg {Array} preferedCountries default []
43651   
43652  * @constructor
43653  * Create a new PhoneInput.
43654  * @param {Object} config Configuration options
43655  */
43656
43657 Roo.bootstrap.PhoneInput = function(config) {
43658     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43659 };
43660
43661 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43662         /**
43663         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43664         */
43665         listWidth: undefined,
43666         
43667         selectedClass: 'active',
43668         
43669         invalidClass : "has-warning",
43670         
43671         validClass: 'has-success',
43672         
43673         allowed: '0123456789',
43674         
43675         max_length: 15,
43676         
43677         /**
43678          * @cfg {String} defaultDialCode The default dial code when initializing the input
43679          */
43680         defaultDialCode: '+852',
43681         
43682         /**
43683          * @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
43684          */
43685         preferedCountries: false,
43686         
43687         getAutoCreate : function()
43688         {
43689             var data = Roo.bootstrap.PhoneInputData();
43690             var align = this.labelAlign || this.parentLabelAlign();
43691             var id = Roo.id();
43692             
43693             this.allCountries = [];
43694             this.dialCodeMapping = [];
43695             
43696             for (var i = 0; i < data.length; i++) {
43697               var c = data[i];
43698               this.allCountries[i] = {
43699                 name: c[0],
43700                 iso2: c[1],
43701                 dialCode: c[2],
43702                 priority: c[3] || 0,
43703                 areaCodes: c[4] || null
43704               };
43705               this.dialCodeMapping[c[2]] = {
43706                   name: c[0],
43707                   iso2: c[1],
43708                   priority: c[3] || 0,
43709                   areaCodes: c[4] || null
43710               };
43711             }
43712             
43713             var cfg = {
43714                 cls: 'form-group',
43715                 cn: []
43716             };
43717             
43718             var input =  {
43719                 tag: 'input',
43720                 id : id,
43721                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43722                 maxlength: this.max_length,
43723                 cls : 'form-control tel-input',
43724                 autocomplete: 'new-password'
43725             };
43726             
43727             var hiddenInput = {
43728                 tag: 'input',
43729                 type: 'hidden',
43730                 cls: 'hidden-tel-input'
43731             };
43732             
43733             if (this.name) {
43734                 hiddenInput.name = this.name;
43735             }
43736             
43737             if (this.disabled) {
43738                 input.disabled = true;
43739             }
43740             
43741             var flag_container = {
43742                 tag: 'div',
43743                 cls: 'flag-box',
43744                 cn: [
43745                     {
43746                         tag: 'div',
43747                         cls: 'flag'
43748                     },
43749                     {
43750                         tag: 'div',
43751                         cls: 'caret'
43752                     }
43753                 ]
43754             };
43755             
43756             var box = {
43757                 tag: 'div',
43758                 cls: this.hasFeedback ? 'has-feedback' : '',
43759                 cn: [
43760                     hiddenInput,
43761                     input,
43762                     {
43763                         tag: 'input',
43764                         cls: 'dial-code-holder',
43765                         disabled: true
43766                     }
43767                 ]
43768             };
43769             
43770             var container = {
43771                 cls: 'roo-select2-container input-group',
43772                 cn: [
43773                     flag_container,
43774                     box
43775                 ]
43776             };
43777             
43778             if (this.fieldLabel.length) {
43779                 var indicator = {
43780                     tag: 'i',
43781                     tooltip: 'This field is required'
43782                 };
43783                 
43784                 var label = {
43785                     tag: 'label',
43786                     'for':  id,
43787                     cls: 'control-label',
43788                     cn: []
43789                 };
43790                 
43791                 var label_text = {
43792                     tag: 'span',
43793                     html: this.fieldLabel
43794                 };
43795                 
43796                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43797                 label.cn = [
43798                     indicator,
43799                     label_text
43800                 ];
43801                 
43802                 if(this.indicatorpos == 'right') {
43803                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43804                     label.cn = [
43805                         label_text,
43806                         indicator
43807                     ];
43808                 }
43809                 
43810                 if(align == 'left') {
43811                     container = {
43812                         tag: 'div',
43813                         cn: [
43814                             container
43815                         ]
43816                     };
43817                     
43818                     if(this.labelWidth > 12){
43819                         label.style = "width: " + this.labelWidth + 'px';
43820                     }
43821                     if(this.labelWidth < 13 && this.labelmd == 0){
43822                         this.labelmd = this.labelWidth;
43823                     }
43824                     if(this.labellg > 0){
43825                         label.cls += ' col-lg-' + this.labellg;
43826                         input.cls += ' col-lg-' + (12 - this.labellg);
43827                     }
43828                     if(this.labelmd > 0){
43829                         label.cls += ' col-md-' + this.labelmd;
43830                         container.cls += ' col-md-' + (12 - this.labelmd);
43831                     }
43832                     if(this.labelsm > 0){
43833                         label.cls += ' col-sm-' + this.labelsm;
43834                         container.cls += ' col-sm-' + (12 - this.labelsm);
43835                     }
43836                     if(this.labelxs > 0){
43837                         label.cls += ' col-xs-' + this.labelxs;
43838                         container.cls += ' col-xs-' + (12 - this.labelxs);
43839                     }
43840                 }
43841             }
43842             
43843             cfg.cn = [
43844                 label,
43845                 container
43846             ];
43847             
43848             var settings = this;
43849             
43850             ['xs','sm','md','lg'].map(function(size){
43851                 if (settings[size]) {
43852                     cfg.cls += ' col-' + size + '-' + settings[size];
43853                 }
43854             });
43855             
43856             this.store = new Roo.data.Store({
43857                 proxy : new Roo.data.MemoryProxy({}),
43858                 reader : new Roo.data.JsonReader({
43859                     fields : [
43860                         {
43861                             'name' : 'name',
43862                             'type' : 'string'
43863                         },
43864                         {
43865                             'name' : 'iso2',
43866                             'type' : 'string'
43867                         },
43868                         {
43869                             'name' : 'dialCode',
43870                             'type' : 'string'
43871                         },
43872                         {
43873                             'name' : 'priority',
43874                             'type' : 'string'
43875                         },
43876                         {
43877                             'name' : 'areaCodes',
43878                             'type' : 'string'
43879                         }
43880                     ]
43881                 })
43882             });
43883             
43884             if(!this.preferedCountries) {
43885                 this.preferedCountries = [
43886                     'hk',
43887                     'gb',
43888                     'us'
43889                 ];
43890             }
43891             
43892             var p = this.preferedCountries.reverse();
43893             
43894             if(p) {
43895                 for (var i = 0; i < p.length; i++) {
43896                     for (var j = 0; j < this.allCountries.length; j++) {
43897                         if(this.allCountries[j].iso2 == p[i]) {
43898                             var t = this.allCountries[j];
43899                             this.allCountries.splice(j,1);
43900                             this.allCountries.unshift(t);
43901                         }
43902                     } 
43903                 }
43904             }
43905             
43906             this.store.proxy.data = {
43907                 success: true,
43908                 data: this.allCountries
43909             };
43910             
43911             return cfg;
43912         },
43913         
43914         initEvents : function()
43915         {
43916             this.createList();
43917             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43918             
43919             this.indicator = this.indicatorEl();
43920             this.flag = this.flagEl();
43921             this.dialCodeHolder = this.dialCodeHolderEl();
43922             
43923             this.trigger = this.el.select('div.flag-box',true).first();
43924             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43925             
43926             var _this = this;
43927             
43928             (function(){
43929                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43930                 _this.list.setWidth(lw);
43931             }).defer(100);
43932             
43933             this.list.on('mouseover', this.onViewOver, this);
43934             this.list.on('mousemove', this.onViewMove, this);
43935             this.inputEl().on("keyup", this.onKeyUp, this);
43936             this.inputEl().on("keypress", this.onKeyPress, this);
43937             
43938             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43939
43940             this.view = new Roo.View(this.list, this.tpl, {
43941                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43942             });
43943             
43944             this.view.on('click', this.onViewClick, this);
43945             this.setValue(this.defaultDialCode);
43946         },
43947         
43948         onTriggerClick : function(e)
43949         {
43950             Roo.log('trigger click');
43951             if(this.disabled){
43952                 return;
43953             }
43954             
43955             if(this.isExpanded()){
43956                 this.collapse();
43957                 this.hasFocus = false;
43958             }else {
43959                 this.store.load({});
43960                 this.hasFocus = true;
43961                 this.expand();
43962             }
43963         },
43964         
43965         isExpanded : function()
43966         {
43967             return this.list.isVisible();
43968         },
43969         
43970         collapse : function()
43971         {
43972             if(!this.isExpanded()){
43973                 return;
43974             }
43975             this.list.hide();
43976             Roo.get(document).un('mousedown', this.collapseIf, this);
43977             Roo.get(document).un('mousewheel', this.collapseIf, this);
43978             this.fireEvent('collapse', this);
43979             this.validate();
43980         },
43981         
43982         expand : function()
43983         {
43984             Roo.log('expand');
43985
43986             if(this.isExpanded() || !this.hasFocus){
43987                 return;
43988             }
43989             
43990             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43991             this.list.setWidth(lw);
43992             
43993             this.list.show();
43994             this.restrictHeight();
43995             
43996             Roo.get(document).on('mousedown', this.collapseIf, this);
43997             Roo.get(document).on('mousewheel', this.collapseIf, this);
43998             
43999             this.fireEvent('expand', this);
44000         },
44001         
44002         restrictHeight : function()
44003         {
44004             this.list.alignTo(this.inputEl(), this.listAlign);
44005             this.list.alignTo(this.inputEl(), this.listAlign);
44006         },
44007         
44008         onViewOver : function(e, t)
44009         {
44010             if(this.inKeyMode){
44011                 return;
44012             }
44013             var item = this.view.findItemFromChild(t);
44014             
44015             if(item){
44016                 var index = this.view.indexOf(item);
44017                 this.select(index, false);
44018             }
44019         },
44020
44021         // private
44022         onViewClick : function(view, doFocus, el, e)
44023         {
44024             var index = this.view.getSelectedIndexes()[0];
44025             
44026             var r = this.store.getAt(index);
44027             
44028             if(r){
44029                 this.onSelect(r, index);
44030             }
44031             if(doFocus !== false && !this.blockFocus){
44032                 this.inputEl().focus();
44033             }
44034         },
44035         
44036         onViewMove : function(e, t)
44037         {
44038             this.inKeyMode = false;
44039         },
44040         
44041         select : function(index, scrollIntoView)
44042         {
44043             this.selectedIndex = index;
44044             this.view.select(index);
44045             if(scrollIntoView !== false){
44046                 var el = this.view.getNode(index);
44047                 if(el){
44048                     this.list.scrollChildIntoView(el, false);
44049                 }
44050             }
44051         },
44052         
44053         createList : function()
44054         {
44055             this.list = Roo.get(document.body).createChild({
44056                 tag: 'ul',
44057                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44058                 style: 'display:none'
44059             });
44060             
44061             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44062         },
44063         
44064         collapseIf : function(e)
44065         {
44066             var in_combo  = e.within(this.el);
44067             var in_list =  e.within(this.list);
44068             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44069             
44070             if (in_combo || in_list || is_list) {
44071                 return;
44072             }
44073             this.collapse();
44074         },
44075         
44076         onSelect : function(record, index)
44077         {
44078             if(this.fireEvent('beforeselect', this, record, index) !== false){
44079                 
44080                 this.setFlagClass(record.data.iso2);
44081                 this.setDialCode(record.data.dialCode);
44082                 this.hasFocus = false;
44083                 this.collapse();
44084                 this.fireEvent('select', this, record, index);
44085             }
44086         },
44087         
44088         flagEl : function()
44089         {
44090             var flag = this.el.select('div.flag',true).first();
44091             if(!flag){
44092                 return false;
44093             }
44094             return flag;
44095         },
44096         
44097         dialCodeHolderEl : function()
44098         {
44099             var d = this.el.select('input.dial-code-holder',true).first();
44100             if(!d){
44101                 return false;
44102             }
44103             return d;
44104         },
44105         
44106         setDialCode : function(v)
44107         {
44108             this.dialCodeHolder.dom.value = '+'+v;
44109         },
44110         
44111         setFlagClass : function(n)
44112         {
44113             this.flag.dom.className = 'flag '+n;
44114         },
44115         
44116         getValue : function()
44117         {
44118             var v = this.inputEl().getValue();
44119             if(this.dialCodeHolder) {
44120                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44121             }
44122             return v;
44123         },
44124         
44125         setValue : function(v)
44126         {
44127             var d = this.getDialCode(v);
44128             
44129             //invalid dial code
44130             if(v.length == 0 || !d || d.length == 0) {
44131                 if(this.rendered){
44132                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44133                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44134                 }
44135                 return;
44136             }
44137             
44138             //valid dial code
44139             this.setFlagClass(this.dialCodeMapping[d].iso2);
44140             this.setDialCode(d);
44141             this.inputEl().dom.value = v.replace('+'+d,'');
44142             this.hiddenEl().dom.value = this.getValue();
44143             
44144             this.validate();
44145         },
44146         
44147         getDialCode : function(v)
44148         {
44149             v = v ||  '';
44150             
44151             if (v.length == 0) {
44152                 return this.dialCodeHolder.dom.value;
44153             }
44154             
44155             var dialCode = "";
44156             if (v.charAt(0) != "+") {
44157                 return false;
44158             }
44159             var numericChars = "";
44160             for (var i = 1; i < v.length; i++) {
44161               var c = v.charAt(i);
44162               if (!isNaN(c)) {
44163                 numericChars += c;
44164                 if (this.dialCodeMapping[numericChars]) {
44165                   dialCode = v.substr(1, i);
44166                 }
44167                 if (numericChars.length == 4) {
44168                   break;
44169                 }
44170               }
44171             }
44172             return dialCode;
44173         },
44174         
44175         reset : function()
44176         {
44177             this.setValue(this.defaultDialCode);
44178             this.validate();
44179         },
44180         
44181         hiddenEl : function()
44182         {
44183             return this.el.select('input.hidden-tel-input',true).first();
44184         },
44185         
44186         // after setting val
44187         onKeyUp : function(e){
44188             this.setValue(this.getValue());
44189         },
44190         
44191         onKeyPress : function(e){
44192             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44193                 e.stopEvent();
44194             }
44195         }
44196         
44197 });
44198 /**
44199  * @class Roo.bootstrap.MoneyField
44200  * @extends Roo.bootstrap.ComboBox
44201  * Bootstrap MoneyField class
44202  * 
44203  * @constructor
44204  * Create a new MoneyField.
44205  * @param {Object} config Configuration options
44206  */
44207
44208 Roo.bootstrap.MoneyField = function(config) {
44209     
44210     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44211     
44212 };
44213
44214 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44215     
44216     /**
44217      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44218      */
44219     allowDecimals : true,
44220     /**
44221      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44222      */
44223     decimalSeparator : ".",
44224     /**
44225      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44226      */
44227     decimalPrecision : 0,
44228     /**
44229      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44230      */
44231     allowNegative : true,
44232     /**
44233      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44234      */
44235     allowZero: true,
44236     /**
44237      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44238      */
44239     minValue : Number.NEGATIVE_INFINITY,
44240     /**
44241      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44242      */
44243     maxValue : Number.MAX_VALUE,
44244     /**
44245      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44246      */
44247     minText : "The minimum value for this field is {0}",
44248     /**
44249      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44250      */
44251     maxText : "The maximum value for this field is {0}",
44252     /**
44253      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44254      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44255      */
44256     nanText : "{0} is not a valid number",
44257     /**
44258      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44259      */
44260     castInt : true,
44261     /**
44262      * @cfg {String} defaults currency of the MoneyField
44263      * value should be in lkey
44264      */
44265     defaultCurrency : false,
44266     /**
44267      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44268      */
44269     thousandsDelimiter : false,
44270     /**
44271      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44272      */
44273     max_length: false,
44274     
44275     inputlg : 9,
44276     inputmd : 9,
44277     inputsm : 9,
44278     inputxs : 6,
44279     
44280     store : false,
44281     
44282     getAutoCreate : function()
44283     {
44284         var align = this.labelAlign || this.parentLabelAlign();
44285         
44286         var id = Roo.id();
44287
44288         var cfg = {
44289             cls: 'form-group',
44290             cn: []
44291         };
44292
44293         var input =  {
44294             tag: 'input',
44295             id : id,
44296             cls : 'form-control roo-money-amount-input',
44297             autocomplete: 'new-password'
44298         };
44299         
44300         var hiddenInput = {
44301             tag: 'input',
44302             type: 'hidden',
44303             id: Roo.id(),
44304             cls: 'hidden-number-input'
44305         };
44306         
44307         if(this.max_length) {
44308             input.maxlength = this.max_length; 
44309         }
44310         
44311         if (this.name) {
44312             hiddenInput.name = this.name;
44313         }
44314
44315         if (this.disabled) {
44316             input.disabled = true;
44317         }
44318
44319         var clg = 12 - this.inputlg;
44320         var cmd = 12 - this.inputmd;
44321         var csm = 12 - this.inputsm;
44322         var cxs = 12 - this.inputxs;
44323         
44324         var container = {
44325             tag : 'div',
44326             cls : 'row roo-money-field',
44327             cn : [
44328                 {
44329                     tag : 'div',
44330                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44331                     cn : [
44332                         {
44333                             tag : 'div',
44334                             cls: 'roo-select2-container input-group',
44335                             cn: [
44336                                 {
44337                                     tag : 'input',
44338                                     cls : 'form-control roo-money-currency-input',
44339                                     autocomplete: 'new-password',
44340                                     readOnly : 1,
44341                                     name : this.currencyName
44342                                 },
44343                                 {
44344                                     tag :'span',
44345                                     cls : 'input-group-addon',
44346                                     cn : [
44347                                         {
44348                                             tag: 'span',
44349                                             cls: 'caret'
44350                                         }
44351                                     ]
44352                                 }
44353                             ]
44354                         }
44355                     ]
44356                 },
44357                 {
44358                     tag : 'div',
44359                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44360                     cn : [
44361                         {
44362                             tag: 'div',
44363                             cls: this.hasFeedback ? 'has-feedback' : '',
44364                             cn: [
44365                                 input
44366                             ]
44367                         }
44368                     ]
44369                 }
44370             ]
44371             
44372         };
44373         
44374         if (this.fieldLabel.length) {
44375             var indicator = {
44376                 tag: 'i',
44377                 tooltip: 'This field is required'
44378             };
44379
44380             var label = {
44381                 tag: 'label',
44382                 'for':  id,
44383                 cls: 'control-label',
44384                 cn: []
44385             };
44386
44387             var label_text = {
44388                 tag: 'span',
44389                 html: this.fieldLabel
44390             };
44391
44392             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44393             label.cn = [
44394                 indicator,
44395                 label_text
44396             ];
44397
44398             if(this.indicatorpos == 'right') {
44399                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44400                 label.cn = [
44401                     label_text,
44402                     indicator
44403                 ];
44404             }
44405
44406             if(align == 'left') {
44407                 container = {
44408                     tag: 'div',
44409                     cn: [
44410                         container
44411                     ]
44412                 };
44413
44414                 if(this.labelWidth > 12){
44415                     label.style = "width: " + this.labelWidth + 'px';
44416                 }
44417                 if(this.labelWidth < 13 && this.labelmd == 0){
44418                     this.labelmd = this.labelWidth;
44419                 }
44420                 if(this.labellg > 0){
44421                     label.cls += ' col-lg-' + this.labellg;
44422                     input.cls += ' col-lg-' + (12 - this.labellg);
44423                 }
44424                 if(this.labelmd > 0){
44425                     label.cls += ' col-md-' + this.labelmd;
44426                     container.cls += ' col-md-' + (12 - this.labelmd);
44427                 }
44428                 if(this.labelsm > 0){
44429                     label.cls += ' col-sm-' + this.labelsm;
44430                     container.cls += ' col-sm-' + (12 - this.labelsm);
44431                 }
44432                 if(this.labelxs > 0){
44433                     label.cls += ' col-xs-' + this.labelxs;
44434                     container.cls += ' col-xs-' + (12 - this.labelxs);
44435                 }
44436             }
44437         }
44438
44439         cfg.cn = [
44440             label,
44441             container,
44442             hiddenInput
44443         ];
44444         
44445         var settings = this;
44446
44447         ['xs','sm','md','lg'].map(function(size){
44448             if (settings[size]) {
44449                 cfg.cls += ' col-' + size + '-' + settings[size];
44450             }
44451         });
44452         
44453         return cfg;
44454     },
44455     
44456     initEvents : function()
44457     {
44458         this.indicator = this.indicatorEl();
44459         
44460         this.initCurrencyEvent();
44461         
44462         this.initNumberEvent();
44463     },
44464     
44465     initCurrencyEvent : function()
44466     {
44467         if (!this.store) {
44468             throw "can not find store for combo";
44469         }
44470         
44471         this.store = Roo.factory(this.store, Roo.data);
44472         this.store.parent = this;
44473         
44474         this.createList();
44475         
44476         this.triggerEl = this.el.select('.input-group-addon', true).first();
44477         
44478         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44479         
44480         var _this = this;
44481         
44482         (function(){
44483             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44484             _this.list.setWidth(lw);
44485         }).defer(100);
44486         
44487         this.list.on('mouseover', this.onViewOver, this);
44488         this.list.on('mousemove', this.onViewMove, this);
44489         this.list.on('scroll', this.onViewScroll, this);
44490         
44491         if(!this.tpl){
44492             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44493         }
44494         
44495         this.view = new Roo.View(this.list, this.tpl, {
44496             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44497         });
44498         
44499         this.view.on('click', this.onViewClick, this);
44500         
44501         this.store.on('beforeload', this.onBeforeLoad, this);
44502         this.store.on('load', this.onLoad, this);
44503         this.store.on('loadexception', this.onLoadException, this);
44504         
44505         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44506             "up" : function(e){
44507                 this.inKeyMode = true;
44508                 this.selectPrev();
44509             },
44510
44511             "down" : function(e){
44512                 if(!this.isExpanded()){
44513                     this.onTriggerClick();
44514                 }else{
44515                     this.inKeyMode = true;
44516                     this.selectNext();
44517                 }
44518             },
44519
44520             "enter" : function(e){
44521                 this.collapse();
44522                 
44523                 if(this.fireEvent("specialkey", this, e)){
44524                     this.onViewClick(false);
44525                 }
44526                 
44527                 return true;
44528             },
44529
44530             "esc" : function(e){
44531                 this.collapse();
44532             },
44533
44534             "tab" : function(e){
44535                 this.collapse();
44536                 
44537                 if(this.fireEvent("specialkey", this, e)){
44538                     this.onViewClick(false);
44539                 }
44540                 
44541                 return true;
44542             },
44543
44544             scope : this,
44545
44546             doRelay : function(foo, bar, hname){
44547                 if(hname == 'down' || this.scope.isExpanded()){
44548                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44549                 }
44550                 return true;
44551             },
44552
44553             forceKeyDown: true
44554         });
44555         
44556         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44557         
44558     },
44559     
44560     initNumberEvent : function(e)
44561     {
44562         this.inputEl().on("keydown" , this.fireKey,  this);
44563         this.inputEl().on("focus", this.onFocus,  this);
44564         this.inputEl().on("blur", this.onBlur,  this);
44565         
44566         this.inputEl().relayEvent('keyup', this);
44567         
44568         if(this.indicator){
44569             this.indicator.addClass('invisible');
44570         }
44571  
44572         this.originalValue = this.getValue();
44573         
44574         if(this.validationEvent == 'keyup'){
44575             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44576             this.inputEl().on('keyup', this.filterValidation, this);
44577         }
44578         else if(this.validationEvent !== false){
44579             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44580         }
44581         
44582         if(this.selectOnFocus){
44583             this.on("focus", this.preFocus, this);
44584             
44585         }
44586         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44587             this.inputEl().on("keypress", this.filterKeys, this);
44588         } else {
44589             this.inputEl().relayEvent('keypress', this);
44590         }
44591         
44592         var allowed = "0123456789";
44593         
44594         if(this.allowDecimals){
44595             allowed += this.decimalSeparator;
44596         }
44597         
44598         if(this.allowNegative){
44599             allowed += "-";
44600         }
44601         
44602         if(this.thousandsDelimiter) {
44603             allowed += ",";
44604         }
44605         
44606         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44607         
44608         var keyPress = function(e){
44609             
44610             var k = e.getKey();
44611             
44612             var c = e.getCharCode();
44613             
44614             if(
44615                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44616                     allowed.indexOf(String.fromCharCode(c)) === -1
44617             ){
44618                 e.stopEvent();
44619                 return;
44620             }
44621             
44622             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44623                 return;
44624             }
44625             
44626             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44627                 e.stopEvent();
44628             }
44629         };
44630         
44631         this.inputEl().on("keypress", keyPress, this);
44632         
44633     },
44634     
44635     onTriggerClick : function(e)
44636     {   
44637         if(this.disabled){
44638             return;
44639         }
44640         
44641         this.page = 0;
44642         this.loadNext = false;
44643         
44644         if(this.isExpanded()){
44645             this.collapse();
44646             return;
44647         }
44648         
44649         this.hasFocus = true;
44650         
44651         if(this.triggerAction == 'all') {
44652             this.doQuery(this.allQuery, true);
44653             return;
44654         }
44655         
44656         this.doQuery(this.getRawValue());
44657     },
44658     
44659     getCurrency : function()
44660     {   
44661         var v = this.currencyEl().getValue();
44662         
44663         return v;
44664     },
44665     
44666     restrictHeight : function()
44667     {
44668         this.list.alignTo(this.currencyEl(), this.listAlign);
44669         this.list.alignTo(this.currencyEl(), this.listAlign);
44670     },
44671     
44672     onViewClick : function(view, doFocus, el, e)
44673     {
44674         var index = this.view.getSelectedIndexes()[0];
44675         
44676         var r = this.store.getAt(index);
44677         
44678         if(r){
44679             this.onSelect(r, index);
44680         }
44681     },
44682     
44683     onSelect : function(record, index){
44684         
44685         if(this.fireEvent('beforeselect', this, record, index) !== false){
44686         
44687             this.setFromCurrencyData(index > -1 ? record.data : false);
44688             
44689             this.collapse();
44690             
44691             this.fireEvent('select', this, record, index);
44692         }
44693     },
44694     
44695     setFromCurrencyData : function(o)
44696     {
44697         var currency = '';
44698         
44699         this.lastCurrency = o;
44700         
44701         if (this.currencyField) {
44702             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44703         } else {
44704             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44705         }
44706         
44707         this.lastSelectionText = currency;
44708         
44709         //setting default currency
44710         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44711             this.setCurrency(this.defaultCurrency);
44712             return;
44713         }
44714         
44715         this.setCurrency(currency);
44716     },
44717     
44718     setFromData : function(o)
44719     {
44720         var c = {};
44721         
44722         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44723         
44724         this.setFromCurrencyData(c);
44725         
44726         var value = '';
44727         
44728         if (this.name) {
44729             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44730         } else {
44731             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44732         }
44733         
44734         this.setValue(value);
44735         
44736     },
44737     
44738     setCurrency : function(v)
44739     {   
44740         this.currencyValue = v;
44741         
44742         if(this.rendered){
44743             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44744             this.validate();
44745         }
44746     },
44747     
44748     setValue : function(v)
44749     {
44750         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44751         
44752         this.value = v;
44753         
44754         if(this.rendered){
44755             
44756             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44757             
44758             this.inputEl().dom.value = (v == '') ? '' :
44759                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44760             
44761             if(!this.allowZero && v === '0') {
44762                 this.hiddenEl().dom.value = '';
44763                 this.inputEl().dom.value = '';
44764             }
44765             
44766             this.validate();
44767         }
44768     },
44769     
44770     getRawValue : function()
44771     {
44772         var v = this.inputEl().getValue();
44773         
44774         return v;
44775     },
44776     
44777     getValue : function()
44778     {
44779         return this.fixPrecision(this.parseValue(this.getRawValue()));
44780     },
44781     
44782     parseValue : function(value)
44783     {
44784         if(this.thousandsDelimiter) {
44785             value += "";
44786             r = new RegExp(",", "g");
44787             value = value.replace(r, "");
44788         }
44789         
44790         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44791         return isNaN(value) ? '' : value;
44792         
44793     },
44794     
44795     fixPrecision : function(value)
44796     {
44797         if(this.thousandsDelimiter) {
44798             value += "";
44799             r = new RegExp(",", "g");
44800             value = value.replace(r, "");
44801         }
44802         
44803         var nan = isNaN(value);
44804         
44805         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44806             return nan ? '' : value;
44807         }
44808         return parseFloat(value).toFixed(this.decimalPrecision);
44809     },
44810     
44811     decimalPrecisionFcn : function(v)
44812     {
44813         return Math.floor(v);
44814     },
44815     
44816     validateValue : function(value)
44817     {
44818         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44819             return false;
44820         }
44821         
44822         var num = this.parseValue(value);
44823         
44824         if(isNaN(num)){
44825             this.markInvalid(String.format(this.nanText, value));
44826             return false;
44827         }
44828         
44829         if(num < this.minValue){
44830             this.markInvalid(String.format(this.minText, this.minValue));
44831             return false;
44832         }
44833         
44834         if(num > this.maxValue){
44835             this.markInvalid(String.format(this.maxText, this.maxValue));
44836             return false;
44837         }
44838         
44839         return true;
44840     },
44841     
44842     validate : function()
44843     {
44844         if(this.disabled || this.allowBlank){
44845             this.markValid();
44846             return true;
44847         }
44848         
44849         var currency = this.getCurrency();
44850         
44851         if(this.validateValue(this.getRawValue()) && currency.length){
44852             this.markValid();
44853             return true;
44854         }
44855         
44856         this.markInvalid();
44857         return false;
44858     },
44859     
44860     getName: function()
44861     {
44862         return this.name;
44863     },
44864     
44865     beforeBlur : function()
44866     {
44867         if(!this.castInt){
44868             return;
44869         }
44870         
44871         var v = this.parseValue(this.getRawValue());
44872         
44873         if(v || v == 0){
44874             this.setValue(v);
44875         }
44876     },
44877     
44878     onBlur : function()
44879     {
44880         this.beforeBlur();
44881         
44882         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44883             //this.el.removeClass(this.focusClass);
44884         }
44885         
44886         this.hasFocus = false;
44887         
44888         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44889             this.validate();
44890         }
44891         
44892         var v = this.getValue();
44893         
44894         if(String(v) !== String(this.startValue)){
44895             this.fireEvent('change', this, v, this.startValue);
44896         }
44897         
44898         this.fireEvent("blur", this);
44899     },
44900     
44901     inputEl : function()
44902     {
44903         return this.el.select('.roo-money-amount-input', true).first();
44904     },
44905     
44906     currencyEl : function()
44907     {
44908         return this.el.select('.roo-money-currency-input', true).first();
44909     },
44910     
44911     hiddenEl : function()
44912     {
44913         return this.el.select('input.hidden-number-input',true).first();
44914     }
44915     
44916 });/**
44917  * @class Roo.bootstrap.BezierSignature
44918  * @extends Roo.bootstrap.Component
44919  * Bootstrap BezierSignature class
44920  * This script refer to:
44921  *    Title: Signature Pad
44922  *    Author: szimek
44923  *    Availability: https://github.com/szimek/signature_pad
44924  *
44925  * @constructor
44926  * Create a new BezierSignature
44927  * @param {Object} config The config object
44928  */
44929
44930 Roo.bootstrap.BezierSignature = function(config){
44931     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44932     this.addEvents({
44933         "resize" : true
44934     });
44935 };
44936
44937 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44938 {
44939      
44940     curve_data: [],
44941     
44942     is_empty: true,
44943     
44944     mouse_btn_down: true,
44945     
44946     /**
44947      * @cfg {int} canvas height
44948      */
44949     canvas_height: '200px',
44950     
44951     /**
44952      * @cfg {float|function} Radius of a single dot.
44953      */ 
44954     dot_size: false,
44955     
44956     /**
44957      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44958      */
44959     min_width: 0.5,
44960     
44961     /**
44962      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44963      */
44964     max_width: 2.5,
44965     
44966     /**
44967      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44968      */
44969     throttle: 16,
44970     
44971     /**
44972      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44973      */
44974     min_distance: 5,
44975     
44976     /**
44977      * @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.
44978      */
44979     bg_color: 'rgba(0, 0, 0, 0)',
44980     
44981     /**
44982      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44983      */
44984     dot_color: 'black',
44985     
44986     /**
44987      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44988      */ 
44989     velocity_filter_weight: 0.7,
44990     
44991     /**
44992      * @cfg {function} Callback when stroke begin. 
44993      */
44994     onBegin: false,
44995     
44996     /**
44997      * @cfg {function} Callback when stroke end.
44998      */
44999     onEnd: false,
45000     
45001     getAutoCreate : function()
45002     {
45003         var cls = 'roo-signature column';
45004         
45005         if(this.cls){
45006             cls += ' ' + this.cls;
45007         }
45008         
45009         var col_sizes = [
45010             'lg',
45011             'md',
45012             'sm',
45013             'xs'
45014         ];
45015         
45016         for(var i = 0; i < col_sizes.length; i++) {
45017             if(this[col_sizes[i]]) {
45018                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45019             }
45020         }
45021         
45022         var cfg = {
45023             tag: 'div',
45024             cls: cls,
45025             cn: [
45026                 {
45027                     tag: 'div',
45028                     cls: 'roo-signature-body',
45029                     cn: [
45030                         {
45031                             tag: 'canvas',
45032                             cls: 'roo-signature-body-canvas',
45033                             height: this.canvas_height,
45034                             width: this.canvas_width
45035                         }
45036                     ]
45037                 },
45038                 {
45039                     tag: 'input',
45040                     type: 'file',
45041                     style: 'display: none'
45042                 }
45043             ]
45044         };
45045         
45046         return cfg;
45047     },
45048     
45049     initEvents: function() 
45050     {
45051         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45052         
45053         var canvas = this.canvasEl();
45054         
45055         // mouse && touch event swapping...
45056         canvas.dom.style.touchAction = 'none';
45057         canvas.dom.style.msTouchAction = 'none';
45058         
45059         this.mouse_btn_down = false;
45060         canvas.on('mousedown', this._handleMouseDown, this);
45061         canvas.on('mousemove', this._handleMouseMove, this);
45062         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45063         
45064         if (window.PointerEvent) {
45065             canvas.on('pointerdown', this._handleMouseDown, this);
45066             canvas.on('pointermove', this._handleMouseMove, this);
45067             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45068         }
45069         
45070         if ('ontouchstart' in window) {
45071             canvas.on('touchstart', this._handleTouchStart, this);
45072             canvas.on('touchmove', this._handleTouchMove, this);
45073             canvas.on('touchend', this._handleTouchEnd, this);
45074         }
45075         
45076         Roo.EventManager.onWindowResize(this.resize, this, true);
45077         
45078         // file input event
45079         this.fileEl().on('change', this.uploadImage, this);
45080         
45081         this.clear();
45082         
45083         this.resize();
45084     },
45085     
45086     resize: function(){
45087         
45088         var canvas = this.canvasEl().dom;
45089         var ctx = this.canvasElCtx();
45090         var img_data = false;
45091         
45092         if(canvas.width > 0) {
45093             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45094         }
45095         // setting canvas width will clean img data
45096         canvas.width = 0;
45097         
45098         var style = window.getComputedStyle ? 
45099             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45100             
45101         var padding_left = parseInt(style.paddingLeft) || 0;
45102         var padding_right = parseInt(style.paddingRight) || 0;
45103         
45104         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45105         
45106         if(img_data) {
45107             ctx.putImageData(img_data, 0, 0);
45108         }
45109     },
45110     
45111     _handleMouseDown: function(e)
45112     {
45113         if (e.browserEvent.which === 1) {
45114             this.mouse_btn_down = true;
45115             this.strokeBegin(e);
45116         }
45117     },
45118     
45119     _handleMouseMove: function (e)
45120     {
45121         if (this.mouse_btn_down) {
45122             this.strokeMoveUpdate(e);
45123         }
45124     },
45125     
45126     _handleMouseUp: function (e)
45127     {
45128         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45129             this.mouse_btn_down = false;
45130             this.strokeEnd(e);
45131         }
45132     },
45133     
45134     _handleTouchStart: function (e) {
45135         
45136         e.preventDefault();
45137         if (e.browserEvent.targetTouches.length === 1) {
45138             // var touch = e.browserEvent.changedTouches[0];
45139             // this.strokeBegin(touch);
45140             
45141              this.strokeBegin(e); // assume e catching the correct xy...
45142         }
45143     },
45144     
45145     _handleTouchMove: function (e) {
45146         e.preventDefault();
45147         // var touch = event.targetTouches[0];
45148         // _this._strokeMoveUpdate(touch);
45149         this.strokeMoveUpdate(e);
45150     },
45151     
45152     _handleTouchEnd: function (e) {
45153         var wasCanvasTouched = e.target === this.canvasEl().dom;
45154         if (wasCanvasTouched) {
45155             e.preventDefault();
45156             // var touch = event.changedTouches[0];
45157             // _this._strokeEnd(touch);
45158             this.strokeEnd(e);
45159         }
45160     },
45161     
45162     reset: function () {
45163         this._lastPoints = [];
45164         this._lastVelocity = 0;
45165         this._lastWidth = (this.min_width + this.max_width) / 2;
45166         this.canvasElCtx().fillStyle = this.dot_color;
45167     },
45168     
45169     strokeMoveUpdate: function(e)
45170     {
45171         this.strokeUpdate(e);
45172         
45173         if (this.throttle) {
45174             this.throttleStroke(this.strokeUpdate, this.throttle);
45175         }
45176         else {
45177             this.strokeUpdate(e);
45178         }
45179     },
45180     
45181     strokeBegin: function(e)
45182     {
45183         var newPointGroup = {
45184             color: this.dot_color,
45185             points: []
45186         };
45187         
45188         if (typeof this.onBegin === 'function') {
45189             this.onBegin(e);
45190         }
45191         
45192         this.curve_data.push(newPointGroup);
45193         this.reset();
45194         this.strokeUpdate(e);
45195     },
45196     
45197     strokeUpdate: function(e)
45198     {
45199         var rect = this.canvasEl().dom.getBoundingClientRect();
45200         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45201         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45202         var lastPoints = lastPointGroup.points;
45203         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45204         var isLastPointTooClose = lastPoint
45205             ? point.distanceTo(lastPoint) <= this.min_distance
45206             : false;
45207         var color = lastPointGroup.color;
45208         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45209             var curve = this.addPoint(point);
45210             if (!lastPoint) {
45211                 this.drawDot({color: color, point: point});
45212             }
45213             else if (curve) {
45214                 this.drawCurve({color: color, curve: curve});
45215             }
45216             lastPoints.push({
45217                 time: point.time,
45218                 x: point.x,
45219                 y: point.y
45220             });
45221         }
45222     },
45223     
45224     strokeEnd: function(e)
45225     {
45226         this.strokeUpdate(e);
45227         if (typeof this.onEnd === 'function') {
45228             this.onEnd(e);
45229         }
45230     },
45231     
45232     addPoint:  function (point) {
45233         var _lastPoints = this._lastPoints;
45234         _lastPoints.push(point);
45235         if (_lastPoints.length > 2) {
45236             if (_lastPoints.length === 3) {
45237                 _lastPoints.unshift(_lastPoints[0]);
45238             }
45239             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45240             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45241             _lastPoints.shift();
45242             return curve;
45243         }
45244         return null;
45245     },
45246     
45247     calculateCurveWidths: function (startPoint, endPoint) {
45248         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45249             (1 - this.velocity_filter_weight) * this._lastVelocity;
45250
45251         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45252         var widths = {
45253             end: newWidth,
45254             start: this._lastWidth
45255         };
45256         
45257         this._lastVelocity = velocity;
45258         this._lastWidth = newWidth;
45259         return widths;
45260     },
45261     
45262     drawDot: function (_a) {
45263         var color = _a.color, point = _a.point;
45264         var ctx = this.canvasElCtx();
45265         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45266         ctx.beginPath();
45267         this.drawCurveSegment(point.x, point.y, width);
45268         ctx.closePath();
45269         ctx.fillStyle = color;
45270         ctx.fill();
45271     },
45272     
45273     drawCurve: function (_a) {
45274         var color = _a.color, curve = _a.curve;
45275         var ctx = this.canvasElCtx();
45276         var widthDelta = curve.endWidth - curve.startWidth;
45277         var drawSteps = Math.floor(curve.length()) * 2;
45278         ctx.beginPath();
45279         ctx.fillStyle = color;
45280         for (var i = 0; i < drawSteps; i += 1) {
45281         var t = i / drawSteps;
45282         var tt = t * t;
45283         var ttt = tt * t;
45284         var u = 1 - t;
45285         var uu = u * u;
45286         var uuu = uu * u;
45287         var x = uuu * curve.startPoint.x;
45288         x += 3 * uu * t * curve.control1.x;
45289         x += 3 * u * tt * curve.control2.x;
45290         x += ttt * curve.endPoint.x;
45291         var y = uuu * curve.startPoint.y;
45292         y += 3 * uu * t * curve.control1.y;
45293         y += 3 * u * tt * curve.control2.y;
45294         y += ttt * curve.endPoint.y;
45295         var width = curve.startWidth + ttt * widthDelta;
45296         this.drawCurveSegment(x, y, width);
45297         }
45298         ctx.closePath();
45299         ctx.fill();
45300     },
45301     
45302     drawCurveSegment: function (x, y, width) {
45303         var ctx = this.canvasElCtx();
45304         ctx.moveTo(x, y);
45305         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45306         this.is_empty = false;
45307     },
45308     
45309     clear: function()
45310     {
45311         var ctx = this.canvasElCtx();
45312         var canvas = this.canvasEl().dom;
45313         ctx.fillStyle = this.bg_color;
45314         ctx.clearRect(0, 0, canvas.width, canvas.height);
45315         ctx.fillRect(0, 0, canvas.width, canvas.height);
45316         this.curve_data = [];
45317         this.reset();
45318         this.is_empty = true;
45319     },
45320     
45321     fileEl: function()
45322     {
45323         return  this.el.select('input',true).first();
45324     },
45325     
45326     canvasEl: function()
45327     {
45328         return this.el.select('canvas',true).first();
45329     },
45330     
45331     canvasElCtx: function()
45332     {
45333         return this.el.select('canvas',true).first().dom.getContext('2d');
45334     },
45335     
45336     getImage: function(type)
45337     {
45338         if(this.is_empty) {
45339             return false;
45340         }
45341         
45342         // encryption ?
45343         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45344     },
45345     
45346     drawFromImage: function(img_src)
45347     {
45348         var img = new Image();
45349         
45350         img.onload = function(){
45351             this.canvasElCtx().drawImage(img, 0, 0);
45352         }.bind(this);
45353         
45354         img.src = img_src;
45355         
45356         this.is_empty = false;
45357     },
45358     
45359     selectImage: function()
45360     {
45361         this.fileEl().dom.click();
45362     },
45363     
45364     uploadImage: function(e)
45365     {
45366         var reader = new FileReader();
45367         
45368         reader.onload = function(e){
45369             var img = new Image();
45370             img.onload = function(){
45371                 this.reset();
45372                 this.canvasElCtx().drawImage(img, 0, 0);
45373             }.bind(this);
45374             img.src = e.target.result;
45375         }.bind(this);
45376         
45377         reader.readAsDataURL(e.target.files[0]);
45378     },
45379     
45380     // Bezier Point Constructor
45381     Point: (function () {
45382         function Point(x, y, time) {
45383             this.x = x;
45384             this.y = y;
45385             this.time = time || Date.now();
45386         }
45387         Point.prototype.distanceTo = function (start) {
45388             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45389         };
45390         Point.prototype.equals = function (other) {
45391             return this.x === other.x && this.y === other.y && this.time === other.time;
45392         };
45393         Point.prototype.velocityFrom = function (start) {
45394             return this.time !== start.time
45395             ? this.distanceTo(start) / (this.time - start.time)
45396             : 0;
45397         };
45398         return Point;
45399     }()),
45400     
45401     
45402     // Bezier Constructor
45403     Bezier: (function () {
45404         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45405             this.startPoint = startPoint;
45406             this.control2 = control2;
45407             this.control1 = control1;
45408             this.endPoint = endPoint;
45409             this.startWidth = startWidth;
45410             this.endWidth = endWidth;
45411         }
45412         Bezier.fromPoints = function (points, widths, scope) {
45413             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45414             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45415             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45416         };
45417         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45418             var dx1 = s1.x - s2.x;
45419             var dy1 = s1.y - s2.y;
45420             var dx2 = s2.x - s3.x;
45421             var dy2 = s2.y - s3.y;
45422             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45423             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45424             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45425             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45426             var dxm = m1.x - m2.x;
45427             var dym = m1.y - m2.y;
45428             var k = l2 / (l1 + l2);
45429             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45430             var tx = s2.x - cm.x;
45431             var ty = s2.y - cm.y;
45432             return {
45433                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45434                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45435             };
45436         };
45437         Bezier.prototype.length = function () {
45438             var steps = 10;
45439             var length = 0;
45440             var px;
45441             var py;
45442             for (var i = 0; i <= steps; i += 1) {
45443                 var t = i / steps;
45444                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45445                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45446                 if (i > 0) {
45447                     var xdiff = cx - px;
45448                     var ydiff = cy - py;
45449                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45450                 }
45451                 px = cx;
45452                 py = cy;
45453             }
45454             return length;
45455         };
45456         Bezier.prototype.point = function (t, start, c1, c2, end) {
45457             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45458             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45459             + (3.0 * c2 * (1.0 - t) * t * t)
45460             + (end * t * t * t);
45461         };
45462         return Bezier;
45463     }()),
45464     
45465     throttleStroke: function(fn, wait) {
45466       if (wait === void 0) { wait = 250; }
45467       var previous = 0;
45468       var timeout = null;
45469       var result;
45470       var storedContext;
45471       var storedArgs;
45472       var later = function () {
45473           previous = Date.now();
45474           timeout = null;
45475           result = fn.apply(storedContext, storedArgs);
45476           if (!timeout) {
45477               storedContext = null;
45478               storedArgs = [];
45479           }
45480       };
45481       return function wrapper() {
45482           var args = [];
45483           for (var _i = 0; _i < arguments.length; _i++) {
45484               args[_i] = arguments[_i];
45485           }
45486           var now = Date.now();
45487           var remaining = wait - (now - previous);
45488           storedContext = this;
45489           storedArgs = args;
45490           if (remaining <= 0 || remaining > wait) {
45491               if (timeout) {
45492                   clearTimeout(timeout);
45493                   timeout = null;
45494               }
45495               previous = now;
45496               result = fn.apply(storedContext, storedArgs);
45497               if (!timeout) {
45498                   storedContext = null;
45499                   storedArgs = [];
45500               }
45501           }
45502           else if (!timeout) {
45503               timeout = window.setTimeout(later, remaining);
45504           }
45505           return result;
45506       };
45507   }
45508   
45509 });
45510
45511  
45512
45513