sync
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * @abstract
227  * @children Roo.bootstrap.Component
228  * Bootstrap Component base class
229  * @cfg {String} cls css class
230  * @cfg {String} style any extra css
231  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
232  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
233  * @cfg {string} dataId cutomer id
234  * @cfg {string} name Specifies name attribute
235  * @cfg {string} tooltip  Text for the tooltip
236  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
237  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
238  
239  * @constructor
240  * Do not use directly - it does not do anything..
241  * @param {Object} config The config object
242  */
243
244
245
246 Roo.bootstrap.Component = function(config){
247     Roo.bootstrap.Component.superclass.constructor.call(this, config);
248        
249     this.addEvents({
250         /**
251          * @event childrenrendered
252          * Fires when the children have been rendered..
253          * @param {Roo.bootstrap.Component} this
254          */
255         "childrenrendered" : true
256         
257         
258         
259     });
260     
261     
262 };
263
264 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
265     
266     
267     allowDomMove : false, // to stop relocations in parent onRender...
268     
269     cls : false,
270     
271     style : false,
272     
273     autoCreate : false,
274     
275     tooltip : null,
276     /**
277      * Initialize Events for the element
278      */
279     initEvents : function() { },
280     
281     xattr : false,
282     
283     parentId : false,
284     
285     can_build_overlaid : true,
286     
287     container_method : false,
288     
289     dataId : false,
290     
291     name : false,
292     
293     parent: function() {
294         // returns the parent component..
295         return Roo.ComponentMgr.get(this.parentId)
296         
297         
298     },
299     
300     // private
301     onRender : function(ct, position)
302     {
303        // Roo.log("Call onRender: " + this.xtype);
304         
305         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
306         
307         if(this.el){
308             if (this.el.attr('xtype')) {
309                 this.el.attr('xtypex', this.el.attr('xtype'));
310                 this.el.dom.removeAttribute('xtype');
311                 
312                 this.initEvents();
313             }
314             
315             return;
316         }
317         
318          
319         
320         var cfg = Roo.apply({},  this.getAutoCreate());
321         
322         cfg.id = this.id || Roo.id();
323         
324         // fill in the extra attributes 
325         if (this.xattr && typeof(this.xattr) =='object') {
326             for (var i in this.xattr) {
327                 cfg[i] = this.xattr[i];
328             }
329         }
330         
331         if(this.dataId){
332             cfg.dataId = this.dataId;
333         }
334         
335         if (this.cls) {
336             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
337         }
338         
339         if (this.style) { // fixme needs to support more complex style data.
340             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341         }
342         
343         if(this.name){
344             cfg.name = this.name;
345         }
346         
347         this.el = ct.createChild(cfg, position);
348         
349         if (this.tooltip) {
350             this.tooltipEl().attr('tooltip', this.tooltip);
351         }
352         
353         if(this.tabIndex !== undefined){
354             this.el.dom.setAttribute('tabIndex', this.tabIndex);
355         }
356         
357         this.initEvents();
358         
359     },
360     /**
361      * Fetch the element to add children to
362      * @return {Roo.Element} defaults to this.el
363      */
364     getChildContainer : function()
365     {
366         return this.el;
367     },
368     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
369     {
370         return Roo.get(document.body);
371     },
372     
373     /**
374      * Fetch the element to display the tooltip on.
375      * @return {Roo.Element} defaults to this.el
376      */
377     tooltipEl : function()
378     {
379         return this.el;
380     },
381         
382     addxtype  : function(tree,cntr)
383     {
384         var cn = this;
385         
386         cn = Roo.factory(tree);
387         //Roo.log(['addxtype', cn]);
388            
389         cn.parentType = this.xtype; //??
390         cn.parentId = this.id;
391         
392         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
393         if (typeof(cn.container_method) == 'string') {
394             cntr = cn.container_method;
395         }
396         
397         
398         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
399         
400         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
401         
402         var build_from_html =  Roo.XComponent.build_from_html;
403           
404         var is_body  = (tree.xtype == 'Body') ;
405           
406         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
407           
408         var self_cntr_el = Roo.get(this[cntr](false));
409         
410         // do not try and build conditional elements 
411         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412             return false;
413         }
414         
415         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
416             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
417                 return this.addxtypeChild(tree,cntr, is_body);
418             }
419             
420             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
421                 
422             if(echild){
423                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
424             }
425             
426             Roo.log('skipping render');
427             return cn;
428             
429         }
430         
431         var ret = false;
432         if (!build_from_html) {
433             return false;
434         }
435         
436         // this i think handles overlaying multiple children of the same type
437         // with the sam eelement.. - which might be buggy..
438         while (true) {
439             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
440             
441             if (!echild) {
442                 break;
443             }
444             
445             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446                 break;
447             }
448             
449             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
450         }
451        
452         return ret;
453     },
454     
455     
456     addxtypeChild : function (tree, cntr, is_body)
457     {
458         Roo.debug && Roo.log('addxtypeChild:' + cntr);
459         var cn = this;
460         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
461         
462         
463         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
464                     (typeof(tree['flexy:foreach']) != 'undefined');
465           
466     
467         
468         skip_children = false;
469         // render the element if it's not BODY.
470         if (!is_body) {
471             
472             // if parent was disabled, then do not try and create the children..
473             if(!this[cntr](true)){
474                 tree.items = [];
475                 return tree;
476             }
477            
478             cn = Roo.factory(tree);
479            
480             cn.parentType = this.xtype; //??
481             cn.parentId = this.id;
482             
483             var build_from_html =  Roo.XComponent.build_from_html;
484             
485             
486             // does the container contain child eleemnts with 'xtype' attributes.
487             // that match this xtype..
488             // note - when we render we create these as well..
489             // so we should check to see if body has xtype set.
490             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
491                
492                 var self_cntr_el = Roo.get(this[cntr](false));
493                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
494                 if (echild) { 
495                     //Roo.log(Roo.XComponent.build_from_html);
496                     //Roo.log("got echild:");
497                     //Roo.log(echild);
498                 }
499                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
500                 // and are not displayed -this causes this to use up the wrong element when matching.
501                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
502                 
503                 
504                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
505                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
506                   
507                   
508                   
509                     cn.el = echild;
510                   //  Roo.log("GOT");
511                     //echild.dom.removeAttribute('xtype');
512                 } else {
513                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
514                     Roo.debug && Roo.log(self_cntr_el);
515                     Roo.debug && Roo.log(echild);
516                     Roo.debug && Roo.log(cn);
517                 }
518             }
519            
520             
521            
522             // if object has flexy:if - then it may or may not be rendered.
523             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
524                 // skip a flexy if element.
525                 Roo.debug && Roo.log('skipping render');
526                 Roo.debug && Roo.log(tree);
527                 if (!cn.el) {
528                     Roo.debug && Roo.log('skipping all children');
529                     skip_children = true;
530                 }
531                 
532              } else {
533                  
534                 // actually if flexy:foreach is found, we really want to create 
535                 // multiple copies here...
536                 //Roo.log('render');
537                 //Roo.log(this[cntr]());
538                 // some elements do not have render methods.. like the layouts...
539                 /*
540                 if(this[cntr](true) === false){
541                     cn.items = [];
542                     return cn;
543                 }
544                 */
545                 cn.render && cn.render(this[cntr](true));
546                 
547              }
548             // then add the element..
549         }
550          
551         // handle the kids..
552         
553         var nitems = [];
554         /*
555         if (typeof (tree.menu) != 'undefined') {
556             tree.menu.parentType = cn.xtype;
557             tree.menu.triggerEl = cn.el;
558             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559             
560         }
561         */
562         if (!tree.items || !tree.items.length) {
563             cn.items = nitems;
564             //Roo.log(["no children", this]);
565             
566             return cn;
567         }
568          
569         var items = tree.items;
570         delete tree.items;
571         
572         //Roo.log(items.length);
573             // add the items..
574         if (!skip_children) {    
575             for(var i =0;i < items.length;i++) {
576               //  Roo.log(['add child', items[i]]);
577                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
578             }
579         }
580         
581         cn.items = nitems;
582         
583         //Roo.log("fire childrenrendered");
584         
585         cn.fireEvent('childrenrendered', this);
586         
587         return cn;
588     },
589     
590     /**
591      * Set the element that will be used to show or hide
592      */
593     setVisibilityEl : function(el)
594     {
595         this.visibilityEl = el;
596     },
597     
598      /**
599      * Get the element that will be used to show or hide
600      */
601     getVisibilityEl : function()
602     {
603         if (typeof(this.visibilityEl) == 'object') {
604             return this.visibilityEl;
605         }
606         
607         if (typeof(this.visibilityEl) == 'string') {
608             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
609         }
610         
611         return this.getEl();
612     },
613     
614     /**
615      * Show a component - removes 'hidden' class
616      */
617     show : function()
618     {
619         if(!this.getVisibilityEl()){
620             return;
621         }
622          
623         this.getVisibilityEl().removeClass(['hidden','d-none']);
624         
625         this.fireEvent('show', this);
626         
627         
628     },
629     /**
630      * Hide a component - adds 'hidden' class
631      */
632     hide: function()
633     {
634         if(!this.getVisibilityEl()){
635             return;
636         }
637         
638         this.getVisibilityEl().addClass(['hidden','d-none']);
639         
640         this.fireEvent('hide', this);
641         
642     }
643 });
644
645  /*
646  * - LGPL
647  *
648  * element
649  * 
650  */
651
652 /**
653  * @class Roo.bootstrap.Element
654  * @extends Roo.bootstrap.Component
655  * @children Roo.bootstrap.Component
656  * Bootstrap Element class (basically a DIV used to make random stuff )
657  * 
658  * @cfg {String} html contents of the element
659  * @cfg {String} tag tag of the element
660  * @cfg {String} cls class of the element
661  * @cfg {Boolean} preventDefault (true|false) default false
662  * @cfg {Boolean} clickable (true|false) default false
663  * @cfg {String} role default blank - set to button to force cursor pointer
664  
665  * 
666  * @constructor
667  * Create a new Element
668  * @param {Object} config The config object
669  */
670
671 Roo.bootstrap.Element = function(config){
672     Roo.bootstrap.Element.superclass.constructor.call(this, config);
673     
674     this.addEvents({
675         // raw events
676         /**
677          * @event click
678          * When a element is chick
679          * @param {Roo.bootstrap.Element} this
680          * @param {Roo.EventObject} e
681          */
682         "click" : true 
683         
684       
685     });
686 };
687
688 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
689     
690     tag: 'div',
691     cls: '',
692     html: '',
693     preventDefault: false, 
694     clickable: false,
695     tapedTwice : false,
696     role : false,
697     
698     getAutoCreate : function(){
699         
700         var cfg = {
701             tag: this.tag,
702             // cls: this.cls, double assign in parent class Component.js :: onRender
703             html: this.html
704         };
705         if (this.role !== false) {
706             cfg.role = this.role;
707         }
708         
709         return cfg;
710     },
711     
712     initEvents: function() 
713     {
714         Roo.bootstrap.Element.superclass.initEvents.call(this);
715         
716         if(this.clickable){
717             this.el.on('click', this.onClick, this);
718         }
719         
720         
721     },
722     
723     onClick : function(e)
724     {
725         if(this.preventDefault){
726             e.preventDefault();
727         }
728         
729         this.fireEvent('click', this, e); // why was this double click before?
730     },
731     
732     
733     
734
735     
736     
737     getValue : function()
738     {
739         return this.el.dom.innerHTML;
740     },
741     
742     setValue : function(value)
743     {
744         this.el.dom.innerHTML = value;
745     }
746    
747 });
748
749  
750
751  /*
752  * - LGPL
753  *
754  * dropable area
755  * 
756  */
757
758 /**
759  * @class Roo.bootstrap.DropTarget
760  * @extends Roo.bootstrap.Element
761  * Bootstrap DropTarget class
762  
763  * @cfg {string} name dropable name
764  * 
765  * @constructor
766  * Create a new Dropable Area
767  * @param {Object} config The config object
768  */
769
770 Roo.bootstrap.DropTarget = function(config){
771     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772     
773     this.addEvents({
774         // raw events
775         /**
776          * @event click
777          * When a element is chick
778          * @param {Roo.bootstrap.Element} this
779          * @param {Roo.EventObject} e
780          */
781         "drop" : true
782     });
783 };
784
785 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
786     
787     
788     getAutoCreate : function(){
789         
790          
791     },
792     
793     initEvents: function() 
794     {
795         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
796         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
797             ddGroup: this.name,
798             listeners : {
799                 drop : this.dragDrop.createDelegate(this),
800                 enter : this.dragEnter.createDelegate(this),
801                 out : this.dragOut.createDelegate(this),
802                 over : this.dragOver.createDelegate(this)
803             }
804             
805         });
806         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
807     },
808     
809     dragDrop : function(source,e,data)
810     {
811         // user has to decide how to impliment this.
812         Roo.log('drop');
813         Roo.log(this);
814         //this.fireEvent('drop', this, source, e ,data);
815         return false;
816     },
817     
818     dragEnter : function(n, dd, e, data)
819     {
820         // probably want to resize the element to match the dropped element..
821         Roo.log("enter");
822         this.originalSize = this.el.getSize();
823         this.el.setSize( n.el.getSize());
824         this.dropZone.DDM.refreshCache(this.name);
825         Roo.log([n, dd, e, data]);
826     },
827     
828     dragOut : function(value)
829     {
830         // resize back to normal
831         Roo.log("out");
832         this.el.setSize(this.originalSize);
833         this.dropZone.resetConstraints();
834     },
835     
836     dragOver : function()
837     {
838         // ??? do nothing?
839     }
840    
841 });
842
843  
844
845  /*
846  * - LGPL
847  *
848  * Body
849  *
850  */
851
852 /**
853  * @class Roo.bootstrap.Body
854  * @extends Roo.bootstrap.Component
855  * @builder-top
856  * @children Roo.bootstrap.Component
857  * @parent none
858  * Bootstrap Body class
859  *
860  * @constructor
861  * Create a new body
862  * @param {Object} config The config object
863  */
864
865 Roo.bootstrap.Body = function(config){
866
867     config = config || {};
868
869     Roo.bootstrap.Body.superclass.constructor.call(this, config);
870     this.el = Roo.get(config.el ? config.el : document.body );
871     if (this.cls && this.cls.length) {
872         Roo.get(document.body).addClass(this.cls);
873     }
874 };
875
876 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
877
878     is_body : true,// just to make sure it's constructed?
879
880         autoCreate : {
881         cls: 'container'
882     },
883     onRender : function(ct, position)
884     {
885        /* Roo.log("Roo.bootstrap.Body - onRender");
886         if (this.cls && this.cls.length) {
887             Roo.get(document.body).addClass(this.cls);
888         }
889         // style??? xttr???
890         */
891     }
892
893
894
895
896 });
897 /*
898  * - LGPL
899  *
900  * button group
901  * 
902  */
903
904
905 /**
906  * @class Roo.bootstrap.ButtonGroup
907  * @extends Roo.bootstrap.Component
908  * Bootstrap ButtonGroup class
909  * @children Roo.bootstrap.Button Roo.bootstrap.Form
910  * 
911  * @cfg {String} size lg | sm | xs (default empty normal)
912  * @cfg {String} align vertical | justified  (default none)
913  * @cfg {String} direction up | down (default down)
914  * @cfg {Boolean} toolbar false | true
915  * @cfg {Boolean} btn true | false
916  * 
917  * 
918  * @constructor
919  * Create a new Input
920  * @param {Object} config The config object
921  */
922
923 Roo.bootstrap.ButtonGroup = function(config){
924     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
925 };
926
927 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
928     
929     size: '',
930     align: '',
931     direction: '',
932     toolbar: false,
933     btn: true,
934
935     getAutoCreate : function(){
936         var cfg = {
937             cls: 'btn-group',
938             html : null
939         };
940         
941         cfg.html = this.html || cfg.html;
942         
943         if (this.toolbar) {
944             cfg = {
945                 cls: 'btn-toolbar',
946                 html: null
947             };
948             
949             return cfg;
950         }
951         
952         if (['vertical','justified'].indexOf(this.align)!==-1) {
953             cfg.cls = 'btn-group-' + this.align;
954             
955             if (this.align == 'justified') {
956                 console.log(this.items);
957             }
958         }
959         
960         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
961             cfg.cls += ' btn-group-' + this.size;
962         }
963         
964         if (this.direction == 'up') {
965             cfg.cls += ' dropup' ;
966         }
967         
968         return cfg;
969     },
970     /**
971      * Add a button to the group (similar to NavItem API.)
972      */
973     addItem : function(cfg)
974     {
975         var cn = new Roo.bootstrap.Button(cfg);
976         //this.register(cn);
977         cn.parentId = this.id;
978         cn.onRender(this.el, null);
979         return cn;
980     }
981    
982 });
983
984  /*
985  * - LGPL
986  *
987  * button
988  * 
989  */
990
991 /**
992  * @class Roo.bootstrap.Button
993  * @extends Roo.bootstrap.Component
994  * Bootstrap Button class
995  * @cfg {String} html The button content
996  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
997  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
998  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
999  * @cfg {String} size (lg|sm|xs)
1000  * @cfg {String} tag (a|input|submit)
1001  * @cfg {String} href empty or href
1002  * @cfg {Boolean} disabled default false;
1003  * @cfg {Boolean} isClose default false;
1004  * @cfg {String} glyphicon depricated - use fa
1005  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1006  * @cfg {String} badge text for badge
1007  * @cfg {String} theme (default|glow)  
1008  * @cfg {Boolean} inverse dark themed version
1009  * @cfg {Boolean} toggle is it a slidy toggle button
1010  * @cfg {Boolean} pressed   default null - if the button ahs active state
1011  * @cfg {String} ontext text for on slidy toggle state
1012  * @cfg {String} offtext text for off slidy toggle state
1013  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1014  * @cfg {Boolean} removeClass remove the standard class..
1015  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1016  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1017  * @cfg {Roo.bootstrap.Menu} menu a Menu 
1018
1019  * @constructor
1020  * Create a new button
1021  * @param {Object} config The config object
1022  */
1023
1024
1025 Roo.bootstrap.Button = function(config){
1026     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1027     
1028     this.addEvents({
1029         // raw events
1030         /**
1031          * @event click
1032          * When a button is pressed
1033          * @param {Roo.bootstrap.Button} btn
1034          * @param {Roo.EventObject} e
1035          */
1036         "click" : true,
1037         /**
1038          * @event dblclick
1039          * When a button is double clicked
1040          * @param {Roo.bootstrap.Button} btn
1041          * @param {Roo.EventObject} e
1042          */
1043         "dblclick" : true,
1044          /**
1045          * @event toggle
1046          * After the button has been toggles
1047          * @param {Roo.bootstrap.Button} btn
1048          * @param {Roo.EventObject} e
1049          * @param {boolean} pressed (also available as button.pressed)
1050          */
1051         "toggle" : true
1052     });
1053 };
1054
1055 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1056     html: false,
1057     active: false,
1058     weight: '',
1059     badge_weight: '',
1060     outline : false,
1061     size: '',
1062     tag: 'button',
1063     href: '',
1064     disabled: false,
1065     isClose: false,
1066     glyphicon: '',
1067     fa: '',
1068     badge: '',
1069     theme: 'default',
1070     inverse: false,
1071     
1072     toggle: false,
1073     ontext: 'ON',
1074     offtext: 'OFF',
1075     defaulton: true,
1076     preventDefault: true,
1077     removeClass: false,
1078     name: false,
1079     target: false,
1080     group : false,
1081      
1082     pressed : null,
1083      
1084     
1085     getAutoCreate : function(){
1086         
1087         var cfg = {
1088             tag : 'button',
1089             cls : 'roo-button',
1090             html: ''
1091         };
1092         
1093         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1094             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1095             this.tag = 'button';
1096         } else {
1097             cfg.tag = this.tag;
1098         }
1099         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1100         
1101         if (this.toggle == true) {
1102             cfg={
1103                 tag: 'div',
1104                 cls: 'slider-frame roo-button',
1105                 cn: [
1106                     {
1107                         tag: 'span',
1108                         'data-on-text':'ON',
1109                         'data-off-text':'OFF',
1110                         cls: 'slider-button',
1111                         html: this.offtext
1112                     }
1113                 ]
1114             };
1115             // why are we validating the weights?
1116             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1117                 cfg.cls +=  ' ' + this.weight;
1118             }
1119             
1120             return cfg;
1121         }
1122         
1123         if (this.isClose) {
1124             cfg.cls += ' close';
1125             
1126             cfg["aria-hidden"] = true;
1127             
1128             cfg.html = "&times;";
1129             
1130             return cfg;
1131         }
1132              
1133         
1134         if (this.theme==='default') {
1135             cfg.cls = 'btn roo-button';
1136             
1137             //if (this.parentType != 'Navbar') {
1138             this.weight = this.weight.length ?  this.weight : 'default';
1139             //}
1140             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1141                 
1142                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1143                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1144                 cfg.cls += ' btn-' + outline + weight;
1145                 if (this.weight == 'default') {
1146                     // BC
1147                     cfg.cls += ' btn-' + this.weight;
1148                 }
1149             }
1150         } else if (this.theme==='glow') {
1151             
1152             cfg.tag = 'a';
1153             cfg.cls = 'btn-glow roo-button';
1154             
1155             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1156                 
1157                 cfg.cls += ' ' + this.weight;
1158             }
1159         }
1160    
1161         
1162         if (this.inverse) {
1163             this.cls += ' inverse';
1164         }
1165         
1166         
1167         if (this.active || this.pressed === true) {
1168             cfg.cls += ' active';
1169         }
1170         
1171         if (this.disabled) {
1172             cfg.disabled = 'disabled';
1173         }
1174         
1175         if (this.items) {
1176             Roo.log('changing to ul' );
1177             cfg.tag = 'ul';
1178             this.glyphicon = 'caret';
1179             if (Roo.bootstrap.version == 4) {
1180                 this.fa = 'caret-down';
1181             }
1182             
1183         }
1184         
1185         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1186          
1187         //gsRoo.log(this.parentType);
1188         if (this.parentType === 'Navbar' && !this.parent().bar) {
1189             Roo.log('changing to li?');
1190             
1191             cfg.tag = 'li';
1192             
1193             cfg.cls = '';
1194             cfg.cn =  [{
1195                 tag : 'a',
1196                 cls : 'roo-button',
1197                 html : this.html,
1198                 href : this.href || '#'
1199             }];
1200             if (this.menu) {
1201                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1202                 cfg.cls += ' dropdown';
1203             }   
1204             
1205             delete cfg.html;
1206             
1207         }
1208         
1209        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1210         
1211         if (this.glyphicon) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'span',
1217                     cls: 'glyphicon glyphicon-' + this.glyphicon
1218                 }
1219             ];
1220         }
1221         if (this.fa) {
1222             cfg.html = ' ' + cfg.html;
1223             
1224             cfg.cn = [
1225                 {
1226                     tag: 'i',
1227                     cls: 'fa fas fa-' + this.fa
1228                 }
1229             ];
1230         }
1231         
1232         if (this.badge) {
1233             cfg.html += ' ';
1234             
1235             cfg.tag = 'a';
1236             
1237 //            cfg.cls='btn roo-button';
1238             
1239             cfg.href=this.href;
1240             
1241             var value = cfg.html;
1242             
1243             if(this.glyphicon){
1244                 value = {
1245                     tag: 'span',
1246                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1247                     html: this.html
1248                 };
1249             }
1250             if(this.fa){
1251                 value = {
1252                     tag: 'i',
1253                     cls: 'fa fas fa-' + this.fa,
1254                     html: this.html
1255                 };
1256             }
1257             
1258             var bw = this.badge_weight.length ? this.badge_weight :
1259                 (this.weight.length ? this.weight : 'secondary');
1260             bw = bw == 'default' ? 'secondary' : bw;
1261             
1262             cfg.cn = [
1263                 value,
1264                 {
1265                     tag: 'span',
1266                     cls: 'badge badge-' + bw,
1267                     html: this.badge
1268                 }
1269             ];
1270             
1271             cfg.html='';
1272         }
1273         
1274         if (this.menu) {
1275             cfg.cls += ' dropdown';
1276             cfg.html = typeof(cfg.html) != 'undefined' ?
1277                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1278         }
1279         
1280         if (cfg.tag !== 'a' && this.href !== '') {
1281             throw "Tag must be a to set href.";
1282         } else if (this.href.length > 0) {
1283             cfg.href = this.href;
1284         }
1285         
1286         if(this.removeClass){
1287             cfg.cls = '';
1288         }
1289         
1290         if(this.target){
1291             cfg.target = this.target;
1292         }
1293         
1294         return cfg;
1295     },
1296     initEvents: function() {
1297        // Roo.log('init events?');
1298 //        Roo.log(this.el.dom);
1299         // add the menu...
1300         
1301         if (typeof (this.menu) != 'undefined') {
1302             this.menu.parentType = this.xtype;
1303             this.menu.triggerEl = this.el;
1304             this.addxtype(Roo.apply({}, this.menu));
1305         }
1306
1307
1308         if (this.el.hasClass('roo-button')) {
1309              this.el.on('click', this.onClick, this);
1310              this.el.on('dblclick', this.onDblClick, this);
1311         } else {
1312              this.el.select('.roo-button').on('click', this.onClick, this);
1313              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1314              
1315         }
1316         // why?
1317         if(this.removeClass){
1318             this.el.on('click', this.onClick, this);
1319         }
1320         
1321         if (this.group === true) {
1322              if (this.pressed === false || this.pressed === true) {
1323                 // nothing
1324             } else {
1325                 this.pressed = false;
1326                 this.setActive(this.pressed);
1327             }
1328             
1329         }
1330         
1331         this.el.enableDisplayMode();
1332         
1333     },
1334     onClick : function(e)
1335     {
1336         if (this.disabled) {
1337             return;
1338         }
1339         
1340         Roo.log('button on click ');
1341         if(this.preventDefault){
1342             e.preventDefault();
1343         }
1344         
1345         if (this.group) {
1346             if (this.pressed) {
1347                 // do nothing -
1348                 return;
1349             }
1350             this.setActive(true);
1351             var pi = this.parent().items;
1352             for (var i = 0;i < pi.length;i++) {
1353                 if (this == pi[i]) {
1354                     continue;
1355                 }
1356                 if (pi[i].el.hasClass('roo-button')) {
1357                     pi[i].setActive(false);
1358                 }
1359             }
1360             this.fireEvent('click', this, e);            
1361             return;
1362         }
1363         
1364         if (this.pressed === true || this.pressed === false) {
1365             this.toggleActive(e);
1366         }
1367         
1368         
1369         this.fireEvent('click', this, e);
1370     },
1371     onDblClick: function(e)
1372     {
1373         if (this.disabled) {
1374             return;
1375         }
1376         if(this.preventDefault){
1377             e.preventDefault();
1378         }
1379         this.fireEvent('dblclick', this, e);
1380     },
1381     /**
1382      * Enables this button
1383      */
1384     enable : function()
1385     {
1386         this.disabled = false;
1387         this.el.removeClass('disabled');
1388         this.el.dom.removeAttribute("disabled");
1389     },
1390     
1391     /**
1392      * Disable this button
1393      */
1394     disable : function()
1395     {
1396         this.disabled = true;
1397         this.el.addClass('disabled');
1398         this.el.attr("disabled", "disabled")
1399     },
1400      /**
1401      * sets the active state on/off, 
1402      * @param {Boolean} state (optional) Force a particular state
1403      */
1404     setActive : function(v) {
1405         
1406         this.el[v ? 'addClass' : 'removeClass']('active');
1407         this.pressed = v;
1408     },
1409      /**
1410      * toggles the current active state 
1411      */
1412     toggleActive : function(e)
1413     {
1414         this.setActive(!this.pressed); // this modifies pressed...
1415         this.fireEvent('toggle', this, e, this.pressed);
1416     },
1417      /**
1418      * get the current active state
1419      * @return {boolean} true if it's active
1420      */
1421     isActive : function()
1422     {
1423         return this.el.hasClass('active');
1424     },
1425     /**
1426      * set the text of the first selected button
1427      */
1428     setText : function(str)
1429     {
1430         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1431     },
1432     /**
1433      * get the text of the first selected button
1434      */
1435     getText : function()
1436     {
1437         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1438     },
1439     
1440     setWeight : function(str)
1441     {
1442         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1443         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1444         this.weight = str;
1445         var outline = this.outline ? 'outline-' : '';
1446         if (str == 'default') {
1447             this.el.addClass('btn-default btn-outline-secondary');        
1448             return;
1449         }
1450         this.el.addClass('btn-' + outline + str);        
1451     }
1452     
1453     
1454 });
1455 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1456
1457 Roo.bootstrap.Button.weights = [
1458     'default',
1459     'secondary' ,
1460     'primary',
1461     'success',
1462     'info',
1463     'warning',
1464     'danger',
1465     'link',
1466     'light',
1467     'dark'              
1468    
1469 ];/*
1470  * - LGPL
1471  *
1472  * column
1473  * 
1474  */
1475
1476 /**
1477  * @class Roo.bootstrap.Column
1478  * @extends Roo.bootstrap.Component
1479  * @children Roo.bootstrap.Component
1480  * Bootstrap Column class
1481  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1482  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1483  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1484  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1485  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1486  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1487  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1488  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1489  *
1490  * 
1491  * @cfg {Boolean} hidden (true|false) hide the element
1492  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1493  * @cfg {String} fa (ban|check|...) font awesome icon
1494  * @cfg {Number} fasize (1|2|....) font awsome size
1495
1496  * @cfg {String} icon (info-sign|check|...) glyphicon name
1497
1498  * @cfg {String} html content of column.
1499  * 
1500  * @constructor
1501  * Create a new Column
1502  * @param {Object} config The config object
1503  */
1504
1505 Roo.bootstrap.Column = function(config){
1506     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1507 };
1508
1509 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1510     
1511     xs: false,
1512     sm: false,
1513     md: false,
1514     lg: false,
1515     xsoff: false,
1516     smoff: false,
1517     mdoff: false,
1518     lgoff: false,
1519     html: '',
1520     offset: 0,
1521     alert: false,
1522     fa: false,
1523     icon : false,
1524     hidden : false,
1525     fasize : 1,
1526     
1527     getAutoCreate : function(){
1528         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1529         
1530         cfg = {
1531             tag: 'div',
1532             cls: 'column'
1533         };
1534         
1535         var settings=this;
1536         var sizes =   ['xs','sm','md','lg'];
1537         sizes.map(function(size ,ix){
1538             //Roo.log( size + ':' + settings[size]);
1539             
1540             if (settings[size+'off'] !== false) {
1541                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1542             }
1543             
1544             if (settings[size] === false) {
1545                 return;
1546             }
1547             
1548             if (!settings[size]) { // 0 = hidden
1549                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1550                 // bootsrap4
1551                 for (var i = ix; i > -1; i--) {
1552                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1553                 }
1554                 
1555                 
1556                 return;
1557             }
1558             cfg.cls += ' col-' + size + '-' + settings[size] + (
1559                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1560             );
1561             
1562         });
1563         
1564         if (this.hidden) {
1565             cfg.cls += ' hidden';
1566         }
1567         
1568         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1569             cfg.cls +=' alert alert-' + this.alert;
1570         }
1571         
1572         
1573         if (this.html.length) {
1574             cfg.html = this.html;
1575         }
1576         if (this.fa) {
1577             var fasize = '';
1578             if (this.fasize > 1) {
1579                 fasize = ' fa-' + this.fasize + 'x';
1580             }
1581             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1582             
1583             
1584         }
1585         if (this.icon) {
1586             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1587         }
1588         
1589         return cfg;
1590     }
1591    
1592 });
1593
1594  
1595
1596  /*
1597  * - LGPL
1598  *
1599  * page container.
1600  * 
1601  */
1602
1603
1604 /**
1605  * @class Roo.bootstrap.Container
1606  * @extends Roo.bootstrap.Component
1607  * @builder-top
1608  * @children Roo.bootstrap.Component
1609  * Bootstrap Container class
1610  * @cfg {Boolean} jumbotron is it a jumbotron element
1611  * @cfg {String} html content of element
1612  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1613  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1614  * @cfg {String} header content of header (for panel)
1615  * @cfg {String} footer content of footer (for panel)
1616  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1617  * @cfg {String} tag (header|aside|section) type of HTML tag.
1618  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1619  * @cfg {String} fa font awesome icon
1620  * @cfg {String} icon (info-sign|check|...) glyphicon name
1621  * @cfg {Boolean} hidden (true|false) hide the element
1622  * @cfg {Boolean} expandable (true|false) default false
1623  * @cfg {Boolean} expanded (true|false) default true
1624  * @cfg {String} rheader contet on the right of header
1625  * @cfg {Boolean} clickable (true|false) default false
1626
1627  *     
1628  * @constructor
1629  * Create a new Container
1630  * @param {Object} config The config object
1631  */
1632
1633 Roo.bootstrap.Container = function(config){
1634     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1635     
1636     this.addEvents({
1637         // raw events
1638          /**
1639          * @event expand
1640          * After the panel has been expand
1641          * 
1642          * @param {Roo.bootstrap.Container} this
1643          */
1644         "expand" : true,
1645         /**
1646          * @event collapse
1647          * After the panel has been collapsed
1648          * 
1649          * @param {Roo.bootstrap.Container} this
1650          */
1651         "collapse" : true,
1652         /**
1653          * @event click
1654          * When a element is chick
1655          * @param {Roo.bootstrap.Container} this
1656          * @param {Roo.EventObject} e
1657          */
1658         "click" : true
1659     });
1660 };
1661
1662 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1663     
1664     jumbotron : false,
1665     well: '',
1666     panel : '',
1667     header: '',
1668     footer : '',
1669     sticky: '',
1670     tag : false,
1671     alert : false,
1672     fa: false,
1673     icon : false,
1674     expandable : false,
1675     rheader : '',
1676     expanded : true,
1677     clickable: false,
1678   
1679      
1680     getChildContainer : function() {
1681         
1682         if(!this.el){
1683             return false;
1684         }
1685         
1686         if (this.panel.length) {
1687             return this.el.select('.panel-body',true).first();
1688         }
1689         
1690         return this.el;
1691     },
1692     
1693     
1694     getAutoCreate : function(){
1695         
1696         var cfg = {
1697             tag : this.tag || 'div',
1698             html : '',
1699             cls : ''
1700         };
1701         if (this.jumbotron) {
1702             cfg.cls = 'jumbotron';
1703         }
1704         
1705         
1706         
1707         // - this is applied by the parent..
1708         //if (this.cls) {
1709         //    cfg.cls = this.cls + '';
1710         //}
1711         
1712         if (this.sticky.length) {
1713             
1714             var bd = Roo.get(document.body);
1715             if (!bd.hasClass('bootstrap-sticky')) {
1716                 bd.addClass('bootstrap-sticky');
1717                 Roo.select('html',true).setStyle('height', '100%');
1718             }
1719              
1720             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1721         }
1722         
1723         
1724         if (this.well.length) {
1725             switch (this.well) {
1726                 case 'lg':
1727                 case 'sm':
1728                     cfg.cls +=' well well-' +this.well;
1729                     break;
1730                 default:
1731                     cfg.cls +=' well';
1732                     break;
1733             }
1734         }
1735         
1736         if (this.hidden) {
1737             cfg.cls += ' hidden';
1738         }
1739         
1740         
1741         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1742             cfg.cls +=' alert alert-' + this.alert;
1743         }
1744         
1745         var body = cfg;
1746         
1747         if (this.panel.length) {
1748             cfg.cls += ' panel panel-' + this.panel;
1749             cfg.cn = [];
1750             if (this.header.length) {
1751                 
1752                 var h = [];
1753                 
1754                 if(this.expandable){
1755                     
1756                     cfg.cls = cfg.cls + ' expandable';
1757                     
1758                     h.push({
1759                         tag: 'i',
1760                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1761                     });
1762                     
1763                 }
1764                 
1765                 h.push(
1766                     {
1767                         tag: 'span',
1768                         cls : 'panel-title',
1769                         html : (this.expandable ? '&nbsp;' : '') + this.header
1770                     },
1771                     {
1772                         tag: 'span',
1773                         cls: 'panel-header-right',
1774                         html: this.rheader
1775                     }
1776                 );
1777                 
1778                 cfg.cn.push({
1779                     cls : 'panel-heading',
1780                     style : this.expandable ? 'cursor: pointer' : '',
1781                     cn : h
1782                 });
1783                 
1784             }
1785             
1786             body = false;
1787             cfg.cn.push({
1788                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1789                 html : this.html
1790             });
1791             
1792             
1793             if (this.footer.length) {
1794                 cfg.cn.push({
1795                     cls : 'panel-footer',
1796                     html : this.footer
1797                     
1798                 });
1799             }
1800             
1801         }
1802         
1803         if (body) {
1804             body.html = this.html || cfg.html;
1805             // prefix with the icons..
1806             if (this.fa) {
1807                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1808             }
1809             if (this.icon) {
1810                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1811             }
1812             
1813             
1814         }
1815         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1816             cfg.cls =  'container';
1817         }
1818         
1819         return cfg;
1820     },
1821     
1822     initEvents: function() 
1823     {
1824         if(this.expandable){
1825             var headerEl = this.headerEl();
1826         
1827             if(headerEl){
1828                 headerEl.on('click', this.onToggleClick, this);
1829             }
1830         }
1831         
1832         if(this.clickable){
1833             this.el.on('click', this.onClick, this);
1834         }
1835         
1836     },
1837     
1838     onToggleClick : function()
1839     {
1840         var headerEl = this.headerEl();
1841         
1842         if(!headerEl){
1843             return;
1844         }
1845         
1846         if(this.expanded){
1847             this.collapse();
1848             return;
1849         }
1850         
1851         this.expand();
1852     },
1853     
1854     expand : function()
1855     {
1856         if(this.fireEvent('expand', this)) {
1857             
1858             this.expanded = true;
1859             
1860             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1861             
1862             this.el.select('.panel-body',true).first().removeClass('hide');
1863             
1864             var toggleEl = this.toggleEl();
1865
1866             if(!toggleEl){
1867                 return;
1868             }
1869
1870             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1871         }
1872         
1873     },
1874     
1875     collapse : function()
1876     {
1877         if(this.fireEvent('collapse', this)) {
1878             
1879             this.expanded = false;
1880             
1881             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1882             this.el.select('.panel-body',true).first().addClass('hide');
1883         
1884             var toggleEl = this.toggleEl();
1885
1886             if(!toggleEl){
1887                 return;
1888             }
1889
1890             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1891         }
1892     },
1893     
1894     toggleEl : function()
1895     {
1896         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1897             return;
1898         }
1899         
1900         return this.el.select('.panel-heading .fa',true).first();
1901     },
1902     
1903     headerEl : function()
1904     {
1905         if(!this.el || !this.panel.length || !this.header.length){
1906             return;
1907         }
1908         
1909         return this.el.select('.panel-heading',true).first()
1910     },
1911     
1912     bodyEl : function()
1913     {
1914         if(!this.el || !this.panel.length){
1915             return;
1916         }
1917         
1918         return this.el.select('.panel-body',true).first()
1919     },
1920     
1921     titleEl : function()
1922     {
1923         if(!this.el || !this.panel.length || !this.header.length){
1924             return;
1925         }
1926         
1927         return this.el.select('.panel-title',true).first();
1928     },
1929     
1930     setTitle : function(v)
1931     {
1932         var titleEl = this.titleEl();
1933         
1934         if(!titleEl){
1935             return;
1936         }
1937         
1938         titleEl.dom.innerHTML = v;
1939     },
1940     
1941     getTitle : function()
1942     {
1943         
1944         var titleEl = this.titleEl();
1945         
1946         if(!titleEl){
1947             return '';
1948         }
1949         
1950         return titleEl.dom.innerHTML;
1951     },
1952     
1953     setRightTitle : function(v)
1954     {
1955         var t = this.el.select('.panel-header-right',true).first();
1956         
1957         if(!t){
1958             return;
1959         }
1960         
1961         t.dom.innerHTML = v;
1962     },
1963     
1964     onClick : function(e)
1965     {
1966         e.preventDefault();
1967         
1968         this.fireEvent('click', this, e);
1969     }
1970 });
1971
1972  /**
1973  * @class Roo.bootstrap.Card
1974  * @extends Roo.bootstrap.Component
1975  * @children Roo.bootstrap.Component
1976  * @licence LGPL
1977  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1978  *
1979  *
1980  * possible... may not be implemented..
1981  * @cfg {String} header_image  src url of image.
1982  * @cfg {String|Object} header
1983  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1984  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1985  * 
1986  * @cfg {String} title
1987  * @cfg {String} subtitle
1988  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1989  * @cfg {String} footer
1990  
1991  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1992  * 
1993  * @cfg {String} margin (0|1|2|3|4|5|auto)
1994  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1995  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2000  *
2001  * @cfg {String} padding (0|1|2|3|4|5)
2002  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2003  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2004  * @cfg {String} padding_left (0|1|2|3|4|5)
2005  * @cfg {String} padding_right (0|1|2|3|4|5)
2006  * @cfg {String} padding_x (0|1|2|3|4|5)
2007  * @cfg {String} padding_y (0|1|2|3|4|5)
2008  *
2009  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2010  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2011  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  
2015  * @config {Boolean} dragable  if this card can be dragged.
2016  * @config {String} drag_group  group for drag
2017  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2018  * @config {String} drop_group  group for drag
2019  * 
2020  * @config {Boolean} collapsable can the body be collapsed.
2021  * @config {Boolean} collapsed is the body collapsed when rendered...
2022  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2023  * @config {Boolean} rotated is the body rotated when rendered...
2024  * 
2025  * @constructor
2026  * Create a new Container
2027  * @param {Object} config The config object
2028  */
2029
2030 Roo.bootstrap.Card = function(config){
2031     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2032     
2033     this.addEvents({
2034          // raw events
2035         /**
2036          * @event drop
2037          * When a element a card is dropped
2038          * @param {Roo.bootstrap.Card} this
2039          *
2040          * 
2041          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2042          * @param {String} position 'above' or 'below'
2043          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2044         
2045          */
2046         'drop' : true,
2047          /**
2048          * @event rotate
2049          * When a element a card is rotate
2050          * @param {Roo.bootstrap.Card} this
2051          * @param {Roo.Element} n the node being dropped?
2052          * @param {Boolean} rotate status
2053          */
2054         'rotate' : true,
2055         /**
2056          * @event cardover
2057          * When a card element is dragged over ready to drop (return false to block dropable)
2058          * @param {Roo.bootstrap.Card} this
2059          * @param {Object} data from dragdrop 
2060          */
2061          'cardover' : true
2062          
2063     });
2064 };
2065
2066
2067 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2068     
2069     
2070     weight : '',
2071     
2072     margin: '', /// may be better in component?
2073     margin_top: '', 
2074     margin_bottom: '', 
2075     margin_left: '',
2076     margin_right: '',
2077     margin_x: '',
2078     margin_y: '',
2079     
2080     padding : '',
2081     padding_top: '', 
2082     padding_bottom: '', 
2083     padding_left: '',
2084     padding_right: '',
2085     padding_x: '',
2086     padding_y: '',
2087     
2088     display: '', 
2089     display_xs: '', 
2090     display_sm: '', 
2091     display_lg: '',
2092     display_xl: '',
2093  
2094     header_image  : '',
2095     header : '',
2096     header_size : 0,
2097     title : '',
2098     subtitle : '',
2099     html : '',
2100     footer: '',
2101
2102     collapsable : false,
2103     collapsed : false,
2104     rotateable : false,
2105     rotated : false,
2106     
2107     dragable : false,
2108     drag_group : false,
2109     dropable : false,
2110     drop_group : false,
2111     childContainer : false,
2112     dropEl : false, /// the dom placeholde element that indicates drop location.
2113     containerEl: false, // body container
2114     bodyEl: false, // card-body
2115     headerContainerEl : false, //
2116     headerEl : false,
2117     header_imageEl : false,
2118     
2119     
2120     layoutCls : function()
2121     {
2122         var cls = '';
2123         var t = this;
2124         Roo.log(this.margin_bottom.length);
2125         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2126             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2127             
2128             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2130             }
2131             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2133             }
2134         });
2135         
2136         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2137             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2138                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2139             }
2140         });
2141         
2142         // more generic support?
2143         if (this.hidden) {
2144             cls += ' d-none';
2145         }
2146         
2147         return cls;
2148     },
2149  
2150        // Roo.log("Call onRender: " + this.xtype);
2151         /*  We are looking at something like this.
2152 <div class="card">
2153     <img src="..." class="card-img-top" alt="...">
2154     <div class="card-body">
2155         <h5 class="card-title">Card title</h5>
2156          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2157
2158         >> this bit is really the body...
2159         <div> << we will ad dthis in hopefully it will not break shit.
2160         
2161         ** card text does not actually have any styling...
2162         
2163             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2164         
2165         </div> <<
2166           <a href="#" class="card-link">Card link</a>
2167           
2168     </div>
2169     <div class="card-footer">
2170         <small class="text-muted">Last updated 3 mins ago</small>
2171     </div>
2172 </div>
2173          */
2174     getAutoCreate : function(){
2175         
2176         var cfg = {
2177             tag : 'div',
2178             cls : 'card',
2179             cn : [ ]
2180         };
2181         
2182         if (this.weight.length && this.weight != 'light') {
2183             cfg.cls += ' text-white';
2184         } else {
2185             cfg.cls += ' text-dark'; // need as it's nested..
2186         }
2187         if (this.weight.length) {
2188             cfg.cls += ' bg-' + this.weight;
2189         }
2190         
2191         cfg.cls += ' ' + this.layoutCls(); 
2192         
2193         var hdr = false;
2194         var hdr_ctr = false;
2195         if (this.header.length) {
2196             hdr = {
2197                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2198                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199                 cn : []
2200             };
2201             cfg.cn.push(hdr);
2202             hdr_ctr = hdr;
2203         } else {
2204             hdr = {
2205                 tag : 'div',
2206                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2207                 cn : []
2208             };
2209             cfg.cn.push(hdr);
2210             hdr_ctr = hdr;
2211         }
2212         if (this.collapsable) {
2213             hdr_ctr = {
2214             tag : 'a',
2215             cls : 'd-block user-select-none',
2216             cn: [
2217                     {
2218                         tag: 'i',
2219                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2220                     }
2221                    
2222                 ]
2223             };
2224             hdr.cn.push(hdr_ctr);
2225         }
2226         
2227         hdr_ctr.cn.push(        {
2228             tag: 'span',
2229             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2230             html : this.header
2231         });
2232         
2233         
2234         if (this.header_image.length) {
2235             cfg.cn.push({
2236                 tag : 'img',
2237                 cls : 'card-img-top',
2238                 src: this.header_image // escape?
2239             });
2240         } else {
2241             cfg.cn.push({
2242                     tag : 'div',
2243                     cls : 'card-img-top d-none' 
2244                 });
2245         }
2246             
2247         var body = {
2248             tag : 'div',
2249             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2250             cn : []
2251         };
2252         var obody = body;
2253         if (this.collapsable || this.rotateable) {
2254             obody = {
2255                 tag: 'div',
2256                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2257                 cn : [  body ]
2258             };
2259         }
2260         
2261         cfg.cn.push(obody);
2262         
2263         if (this.title.length) {
2264             body.cn.push({
2265                 tag : 'div',
2266                 cls : 'card-title',
2267                 src: this.title // escape?
2268             });
2269         }  
2270         
2271         if (this.subtitle.length) {
2272             body.cn.push({
2273                 tag : 'div',
2274                 cls : 'card-title',
2275                 src: this.subtitle // escape?
2276             });
2277         }
2278         
2279         body.cn.push({
2280             tag : 'div',
2281             cls : 'roo-card-body-ctr'
2282         });
2283         
2284         if (this.html.length) {
2285             body.cn.push({
2286                 tag: 'div',
2287                 html : this.html
2288             });
2289         }
2290         // fixme ? handle objects?
2291         
2292         if (this.footer.length) {
2293            
2294             cfg.cn.push({
2295                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2296                 html : this.footer
2297             });
2298             
2299         } else {
2300             cfg.cn.push({cls : 'card-footer d-none'});
2301         }
2302         
2303         // footer...
2304         
2305         return cfg;
2306     },
2307     
2308     
2309     getCardHeader : function()
2310     {
2311         var  ret = this.el.select('.card-header',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardFooter : function()
2319     {
2320         var  ret = this.el.select('.card-footer',true).first();
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324         
2325         return ret;
2326     },
2327     getCardImageTop : function()
2328     {
2329         var  ret = this.header_imageEl;
2330         if (ret.hasClass('d-none')) {
2331             ret.removeClass('d-none');
2332         }
2333             
2334         return ret;
2335     },
2336     
2337     getChildContainer : function()
2338     {
2339         
2340         if(!this.el){
2341             return false;
2342         }
2343         return this.el.select('.roo-card-body-ctr',true).first();    
2344     },
2345     
2346     initEvents: function() 
2347     {
2348         this.bodyEl = this.el.select('.card-body',true).first(); 
2349         this.containerEl = this.getChildContainer();
2350         if(this.dragable){
2351             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2352                     containerScroll: true,
2353                     ddGroup: this.drag_group || 'default_card_drag_group'
2354             });
2355             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2356         }
2357         if (this.dropable) {
2358             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2359                 containerScroll: true,
2360                 ddGroup: this.drop_group || 'default_card_drag_group'
2361             });
2362             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2363             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2364             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2365             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2366             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2367         }
2368         
2369         if (this.collapsable) {
2370             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2371         }
2372         if (this.rotateable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2374         }
2375         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2376          
2377         this.footerEl = this.el.select('.card-footer',true).first();
2378         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2379         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2380         this.headerEl = this.el.select('.card-header',true).first();
2381         
2382         if (this.rotated) {
2383             this.el.addClass('roo-card-rotated');
2384             this.fireEvent('rotate', this, true);
2385         }
2386         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2387         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2388         
2389     },
2390     getDragData : function(e)
2391     {
2392         var target = this.getEl();
2393         if (target) {
2394             //this.handleSelection(e);
2395             
2396             var dragData = {
2397                 source: this,
2398                 copy: false,
2399                 nodes: this.getEl(),
2400                 records: []
2401             };
2402             
2403             
2404             dragData.ddel = target.dom ;    // the div element
2405             Roo.log(target.getWidth( ));
2406             dragData.ddel.style.width = target.getWidth() + 'px';
2407             
2408             return dragData;
2409         }
2410         return false;
2411     },
2412     /**
2413     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2414     *    whole Element becomes the target, and this causes the drop gesture to append.
2415     *
2416     *    Returns an object:
2417     *     {
2418            
2419            position : 'below' or 'above'
2420            card  : relateive to card OBJECT (or true for no cards listed)
2421            items_n : relative to nth item in list
2422            card_n : relative to  nth card in list
2423     }
2424     *
2425     *    
2426     */
2427     getTargetFromEvent : function(e, dragged_card_el)
2428     {
2429         var target = e.getTarget();
2430         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2431             target = target.parentNode;
2432         }
2433         
2434         var ret = {
2435             position: '',
2436             cards : [],
2437             card_n : -1,
2438             items_n : -1,
2439             card : false 
2440         };
2441         
2442         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2443         // see if target is one of the 'cards'...
2444         
2445         
2446         //Roo.log(this.items.length);
2447         var pos = false;
2448         
2449         var last_card_n = 0;
2450         var cards_len  = 0;
2451         for (var i = 0;i< this.items.length;i++) {
2452             
2453             if (!this.items[i].el.hasClass('card')) {
2454                  continue;
2455             }
2456             pos = this.getDropPoint(e, this.items[i].el.dom);
2457             
2458             cards_len = ret.cards.length;
2459             //Roo.log(this.items[i].el.dom.id);
2460             ret.cards.push(this.items[i]);
2461             last_card_n  = i;
2462             if (ret.card_n < 0 && pos == 'above') {
2463                 ret.position = cards_len > 0 ? 'below' : pos;
2464                 ret.items_n = i > 0 ? i - 1 : 0;
2465                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2466                 ret.card = ret.cards[ret.card_n];
2467             }
2468         }
2469         if (!ret.cards.length) {
2470             ret.card = true;
2471             ret.position = 'below';
2472             ret.items_n;
2473             return ret;
2474         }
2475         // could not find a card.. stick it at the end..
2476         if (ret.card_n < 0) {
2477             ret.card_n = last_card_n;
2478             ret.card = ret.cards[last_card_n];
2479             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2480             ret.position = 'below';
2481         }
2482         
2483         if (this.items[ret.items_n].el == dragged_card_el) {
2484             return false;
2485         }
2486         
2487         if (ret.position == 'below') {
2488             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2489             
2490             if (card_after  && card_after.el == dragged_card_el) {
2491                 return false;
2492             }
2493             return ret;
2494         }
2495         
2496         // its's after ..
2497         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2498         
2499         if (card_before  && card_before.el == dragged_card_el) {
2500             return false;
2501         }
2502         
2503         return ret;
2504     },
2505     
2506     onNodeEnter : function(n, dd, e, data){
2507         return false;
2508     },
2509     onNodeOver : function(n, dd, e, data)
2510     {
2511        
2512         var target_info = this.getTargetFromEvent(e,data.source.el);
2513         if (target_info === false) {
2514             this.dropPlaceHolder('hide');
2515             return false;
2516         }
2517         Roo.log(['getTargetFromEvent', target_info ]);
2518         
2519         
2520         if (this.fireEvent('cardover', this, [ data ]) === false) {
2521             return false;
2522         }
2523         
2524         this.dropPlaceHolder('show', target_info,data);
2525         
2526         return false; 
2527     },
2528     onNodeOut : function(n, dd, e, data){
2529         this.dropPlaceHolder('hide');
2530      
2531     },
2532     onNodeDrop : function(n, dd, e, data)
2533     {
2534         
2535         // call drop - return false if
2536         
2537         // this could actually fail - if the Network drops..
2538         // we will ignore this at present..- client should probably reload
2539         // the whole set of cards if stuff like that fails.
2540         
2541         
2542         var info = this.getTargetFromEvent(e,data.source.el);
2543         if (info === false) {
2544             return false;
2545         }
2546         this.dropPlaceHolder('hide');
2547   
2548           
2549     
2550         this.acceptCard(data.source, info.position, info.card, info.items_n);
2551         return true;
2552          
2553     },
2554     firstChildCard : function()
2555     {
2556         for (var i = 0;i< this.items.length;i++) {
2557             
2558             if (!this.items[i].el.hasClass('card')) {
2559                  continue;
2560             }
2561             return this.items[i];
2562         }
2563         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2564     },
2565     /**
2566      * accept card
2567      *
2568      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2569      */
2570     acceptCard : function(move_card,  position, next_to_card )
2571     {
2572         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2573             return false;
2574         }
2575         
2576         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2577         
2578         move_card.parent().removeCard(move_card);
2579         
2580         
2581         var dom = move_card.el.dom;
2582         dom.style.width = ''; // clear with - which is set by drag.
2583         
2584         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2585             var cardel = next_to_card.el.dom;
2586             
2587             if (position == 'above' ) {
2588                 cardel.parentNode.insertBefore(dom, cardel);
2589             } else if (cardel.nextSibling) {
2590                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2591             } else {
2592                 cardel.parentNode.append(dom);
2593             }
2594         } else {
2595             // card container???
2596             this.containerEl.dom.append(dom);
2597         }
2598         
2599         //FIXME HANDLE card = true 
2600         
2601         // add this to the correct place in items.
2602         
2603         // remove Card from items.
2604         
2605        
2606         if (this.items.length) {
2607             var nitems = [];
2608             //Roo.log([info.items_n, info.position, this.items.length]);
2609             for (var i =0; i < this.items.length; i++) {
2610                 if (i == to_items_n && position == 'above') {
2611                     nitems.push(move_card);
2612                 }
2613                 nitems.push(this.items[i]);
2614                 if (i == to_items_n && position == 'below') {
2615                     nitems.push(move_card);
2616                 }
2617             }
2618             this.items = nitems;
2619             Roo.log(this.items);
2620         } else {
2621             this.items.push(move_card);
2622         }
2623         
2624         move_card.parentId = this.id;
2625         
2626         return true;
2627         
2628         
2629     },
2630     removeCard : function(c)
2631     {
2632         this.items = this.items.filter(function(e) { return e != c });
2633  
2634         var dom = c.el.dom;
2635         dom.parentNode.removeChild(dom);
2636         dom.style.width = ''; // clear with - which is set by drag.
2637         c.parentId = false;
2638         
2639     },
2640     
2641     /**    Decide whether to drop above or below a View node. */
2642     getDropPoint : function(e, n, dd)
2643     {
2644         if (dd) {
2645              return false;
2646         }
2647         if (n == this.containerEl.dom) {
2648             return "above";
2649         }
2650         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2651         var c = t + (b - t) / 2;
2652         var y = Roo.lib.Event.getPageY(e);
2653         if(y <= c) {
2654             return "above";
2655         }else{
2656             return "below";
2657         }
2658     },
2659     onToggleCollapse : function(e)
2660         {
2661         if (this.collapsed) {
2662             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2663             this.collapsableEl.addClass('show');
2664             this.collapsed = false;
2665             return;
2666         }
2667         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2668         this.collapsableEl.removeClass('show');
2669         this.collapsed = true;
2670         
2671     
2672     },
2673     
2674     onToggleRotate : function(e)
2675     {
2676         this.collapsableEl.removeClass('show');
2677         this.footerEl.removeClass('d-none');
2678         this.el.removeClass('roo-card-rotated');
2679         this.el.removeClass('d-none');
2680         if (this.rotated) {
2681             
2682             this.collapsableEl.addClass('show');
2683             this.rotated = false;
2684             this.fireEvent('rotate', this, this.rotated);
2685             return;
2686         }
2687         this.el.addClass('roo-card-rotated');
2688         this.footerEl.addClass('d-none');
2689         this.el.select('.roo-collapsable').removeClass('show');
2690         
2691         this.rotated = true;
2692         this.fireEvent('rotate', this, this.rotated);
2693     
2694     },
2695     
2696     dropPlaceHolder: function (action, info, data)
2697     {
2698         if (this.dropEl === false) {
2699             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2700             cls : 'd-none'
2701             },true);
2702         }
2703         this.dropEl.removeClass(['d-none', 'd-block']);        
2704         if (action == 'hide') {
2705             
2706             this.dropEl.addClass('d-none');
2707             return;
2708         }
2709         // FIXME - info.card == true!!!
2710         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2711         
2712         if (info.card !== true) {
2713             var cardel = info.card.el.dom;
2714             
2715             if (info.position == 'above') {
2716                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2717             } else if (cardel.nextSibling) {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2719             } else {
2720                 cardel.parentNode.append(this.dropEl.dom);
2721             }
2722         } else {
2723             // card container???
2724             this.containerEl.dom.append(this.dropEl.dom);
2725         }
2726         
2727         this.dropEl.addClass('d-block roo-card-dropzone');
2728         
2729         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2730         
2731         
2732     
2733     
2734     
2735     },
2736     setHeaderText: function(html)
2737     {
2738         this.header = html;
2739         if (this.headerContainerEl) {
2740             this.headerContainerEl.dom.innerHTML = html;
2741         }
2742     },
2743     onHeaderImageLoad : function(ev, he)
2744     {
2745         if (!this.header_image_fit_square) {
2746             return;
2747         }
2748         
2749         var hw = he.naturalHeight / he.naturalWidth;
2750         // wide image = < 0
2751         // tall image = > 1
2752         //var w = he.dom.naturalWidth;
2753         var ww = he.width;
2754         he.style.left =  0;
2755         he.style.position =  'relative';
2756         if (hw > 1) {
2757             var nw = (ww * (1/hw));
2758             Roo.get(he).setSize( ww * (1/hw),  ww);
2759             he.style.left =  ((ww - nw)/ 2) + 'px';
2760             he.style.position =  'relative';
2761         }
2762
2763     }
2764
2765     
2766 });
2767
2768 /*
2769  * - LGPL
2770  *
2771  * Card header - holder for the card header elements.
2772  * 
2773  */
2774
2775 /**
2776  * @class Roo.bootstrap.CardHeader
2777  * @extends Roo.bootstrap.Element
2778  * @parent Roo.bootstrap.Card
2779  * @children Roo.bootstrap.Component
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  * @parent Roo.bootstrap.Card
2814  * @children Roo.bootstrap.Component
2815  * Bootstrap CardFooter class
2816  * 
2817  * @constructor
2818  * Create a new Card Footer - that you can embed children into
2819  * @param {Object} config The config object
2820  */
2821
2822 Roo.bootstrap.CardFooter = function(config){
2823     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2824 };
2825
2826 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2827     
2828     
2829     container_method : 'getCardFooter' 
2830     
2831      
2832     
2833     
2834    
2835 });
2836
2837  
2838
2839  /*
2840  * - LGPL
2841  *
2842  * Card header - holder for the card header elements.
2843  * 
2844  */
2845
2846 /**
2847  * @class Roo.bootstrap.CardImageTop
2848  * @extends Roo.bootstrap.Element
2849  * @parent Roo.bootstrap.Card
2850  * @children Roo.bootstrap.Component
2851  * Bootstrap CardImageTop class
2852  * 
2853  * @constructor
2854  * Create a new Card Image Top container
2855  * @param {Object} config The config object
2856  */
2857
2858 Roo.bootstrap.CardImageTop = function(config){
2859     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2860 };
2861
2862 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2863     
2864    
2865     container_method : 'getCardImageTop' 
2866     
2867      
2868     
2869    
2870 });
2871
2872  
2873
2874  
2875 /*
2876 * Licence: LGPL
2877 */
2878
2879 /**
2880  * @class Roo.bootstrap.ButtonUploader
2881  * @extends Roo.bootstrap.Button
2882  * Bootstrap Button Uploader class - it's a button which when you add files to it
2883  *
2884  * 
2885  * @cfg {Number} errorTimeout default 3000
2886  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2887  * @cfg {Array}  html The button text.
2888  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2889  *
2890  * @constructor
2891  * Create a new CardUploader
2892  * @param {Object} config The config object
2893  */
2894
2895 Roo.bootstrap.ButtonUploader = function(config){
2896     
2897  
2898     
2899     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2900     
2901      
2902      this.addEvents({
2903          // raw events
2904         /**
2905          * @event beforeselect
2906          * When button is pressed, before show upload files dialog is shown
2907          * @param {Roo.bootstrap.UploaderButton} this
2908          *
2909          */
2910         'beforeselect' : true,
2911          /**
2912          * @event fired when files have been selected, 
2913          * When a the download link is clicked
2914          * @param {Roo.bootstrap.UploaderButton} this
2915          * @param {Array} Array of files that have been uploaded
2916          */
2917         'uploaded' : true
2918         
2919     });
2920 };
2921  
2922 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2923     
2924      
2925     errorTimeout : 3000,
2926      
2927     images : false,
2928    
2929     fileCollection : false,
2930     allowBlank : true,
2931     
2932     multiple : true,
2933     
2934     getAutoCreate : function()
2935     {
2936         var im = {
2937             tag: 'input',
2938             type : 'file',
2939             cls : 'd-none  roo-card-upload-selector' 
2940           
2941         };
2942         if (this.multiple) {
2943             im.multiple = 'multiple';
2944         }
2945         
2946         return  {
2947             cls :'div' ,
2948             cn : [
2949                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2950                 im
2951
2952             ]
2953         };
2954            
2955          
2956     },
2957      
2958    
2959     initEvents : function()
2960     {
2961         
2962         Roo.bootstrap.Button.prototype.initEvents.call(this);
2963         
2964         
2965         
2966         
2967         
2968         this.urlAPI = (window.createObjectURL && window) || 
2969                                 (window.URL && URL.revokeObjectURL && URL) || 
2970                                 (window.webkitURL && webkitURL);
2971                         
2972          
2973          
2974          
2975         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * Bootstrap Header class
3384  * @cfg {String} html content of header
3385  * @cfg {Number} level (1|2|3|4|5|6) default 1
3386  * 
3387  * @constructor
3388  * Create a new Header
3389  * @param {Object} config The config object
3390  */
3391
3392
3393 Roo.bootstrap.Header  = function(config){
3394     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3395 };
3396
3397 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3398     
3399     //href : false,
3400     html : false,
3401     level : 1,
3402     
3403     
3404     
3405     getAutoCreate : function(){
3406         
3407         
3408         
3409         var cfg = {
3410             tag: 'h' + (1 *this.level),
3411             html: this.html || ''
3412         } ;
3413         
3414         return cfg;
3415     }
3416    
3417 });
3418
3419  
3420
3421  /*
3422  * Based on:
3423  * Ext JS Library 1.1.1
3424  * Copyright(c) 2006-2007, Ext JS, LLC.
3425  *
3426  * Originally Released Under LGPL - original licence link has changed is not relivant.
3427  *
3428  * Fork - LGPL
3429  * <script type="text/javascript">
3430  */
3431  
3432 /**
3433  * @class Roo.bootstrap.MenuMgr
3434  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3435  * @singleton
3436  */
3437 Roo.bootstrap.MenuMgr = function(){
3438    var menus, active, groups = {}, attached = false, lastShow = new Date();
3439
3440    // private - called when first menu is created
3441    function init(){
3442        menus = {};
3443        active = new Roo.util.MixedCollection();
3444        Roo.get(document).addKeyListener(27, function(){
3445            if(active.length > 0){
3446                hideAll();
3447            }
3448        });
3449    }
3450
3451    // private
3452    function hideAll(){
3453        if(active && active.length > 0){
3454            var c = active.clone();
3455            c.each(function(m){
3456                m.hide();
3457            });
3458        }
3459    }
3460
3461    // private
3462    function onHide(m){
3463        active.remove(m);
3464        if(active.length < 1){
3465            Roo.get(document).un("mouseup", onMouseDown);
3466             
3467            attached = false;
3468        }
3469    }
3470
3471    // private
3472    function onShow(m){
3473        var last = active.last();
3474        lastShow = new Date();
3475        active.add(m);
3476        if(!attached){
3477           Roo.get(document).on("mouseup", onMouseDown);
3478            
3479            attached = true;
3480        }
3481        if(m.parentMenu){
3482           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3483           m.parentMenu.activeChild = m;
3484        }else if(last && last.isVisible()){
3485           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3486        }
3487    }
3488
3489    // private
3490    function onBeforeHide(m){
3491        if(m.activeChild){
3492            m.activeChild.hide();
3493        }
3494        if(m.autoHideTimer){
3495            clearTimeout(m.autoHideTimer);
3496            delete m.autoHideTimer;
3497        }
3498    }
3499
3500    // private
3501    function onBeforeShow(m){
3502        var pm = m.parentMenu;
3503        if(!pm && !m.allowOtherMenus){
3504            hideAll();
3505        }else if(pm && pm.activeChild && active != m){
3506            pm.activeChild.hide();
3507        }
3508    }
3509
3510    // private this should really trigger on mouseup..
3511    function onMouseDown(e){
3512         Roo.log("on Mouse Up");
3513         
3514         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3515             Roo.log("MenuManager hideAll");
3516             hideAll();
3517             e.stopEvent();
3518         }
3519         
3520         
3521    }
3522
3523    // private
3524    function onBeforeCheck(mi, state){
3525        if(state){
3526            var g = groups[mi.group];
3527            for(var i = 0, l = g.length; i < l; i++){
3528                if(g[i] != mi){
3529                    g[i].setChecked(false);
3530                }
3531            }
3532        }
3533    }
3534
3535    return {
3536
3537        /**
3538         * Hides all menus that are currently visible
3539         */
3540        hideAll : function(){
3541             hideAll();  
3542        },
3543
3544        // private
3545        register : function(menu){
3546            if(!menus){
3547                init();
3548            }
3549            menus[menu.id] = menu;
3550            menu.on("beforehide", onBeforeHide);
3551            menu.on("hide", onHide);
3552            menu.on("beforeshow", onBeforeShow);
3553            menu.on("show", onShow);
3554            var g = menu.group;
3555            if(g && menu.events["checkchange"]){
3556                if(!groups[g]){
3557                    groups[g] = [];
3558                }
3559                groups[g].push(menu);
3560                menu.on("checkchange", onCheck);
3561            }
3562        },
3563
3564         /**
3565          * Returns a {@link Roo.menu.Menu} object
3566          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3567          * be used to generate and return a new Menu instance.
3568          */
3569        get : function(menu){
3570            if(typeof menu == "string"){ // menu id
3571                return menus[menu];
3572            }else if(menu.events){  // menu instance
3573                return menu;
3574            }
3575            /*else if(typeof menu.length == 'number'){ // array of menu items?
3576                return new Roo.bootstrap.Menu({items:menu});
3577            }else{ // otherwise, must be a config
3578                return new Roo.bootstrap.Menu(menu);
3579            }
3580            */
3581            return false;
3582        },
3583
3584        // private
3585        unregister : function(menu){
3586            delete menus[menu.id];
3587            menu.un("beforehide", onBeforeHide);
3588            menu.un("hide", onHide);
3589            menu.un("beforeshow", onBeforeShow);
3590            menu.un("show", onShow);
3591            var g = menu.group;
3592            if(g && menu.events["checkchange"]){
3593                groups[g].remove(menu);
3594                menu.un("checkchange", onCheck);
3595            }
3596        },
3597
3598        // private
3599        registerCheckable : function(menuItem){
3600            var g = menuItem.group;
3601            if(g){
3602                if(!groups[g]){
3603                    groups[g] = [];
3604                }
3605                groups[g].push(menuItem);
3606                menuItem.on("beforecheckchange", onBeforeCheck);
3607            }
3608        },
3609
3610        // private
3611        unregisterCheckable : function(menuItem){
3612            var g = menuItem.group;
3613            if(g){
3614                groups[g].remove(menuItem);
3615                menuItem.un("beforecheckchange", onBeforeCheck);
3616            }
3617        }
3618    };
3619 }();/*
3620  * - LGPL
3621  *
3622  * menu
3623  * 
3624  */
3625
3626 /**
3627  * @class Roo.bootstrap.Menu
3628  * @extends Roo.bootstrap.Component
3629  * @children Roo.bootstrap.MenuItem
3630  * Bootstrap Menu class - container for MenuItems
3631  * 
3632  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3633  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3634  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3635  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3636   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3637   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3638  
3639  * @constructor
3640  * Create a new Menu
3641  * @param {Object} config The config object
3642  */
3643
3644
3645 Roo.bootstrap.Menu = function(config){
3646     
3647     if (config.type == 'treeview') {
3648         // normally menu's are drawn attached to the document to handle layering etc..
3649         // however treeview (used by the docs menu is drawn into the parent element)
3650         this.container_method = 'getChildContainer'; 
3651     }
3652     
3653     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3654     if (this.registerMenu && this.type != 'treeview')  {
3655         Roo.bootstrap.MenuMgr.register(this);
3656     }
3657     
3658     
3659     this.addEvents({
3660         /**
3661          * @event beforeshow
3662          * Fires before this menu is displayed (return false to block)
3663          * @param {Roo.menu.Menu} this
3664          */
3665         beforeshow : true,
3666         /**
3667          * @event beforehide
3668          * Fires before this menu is hidden (return false to block)
3669          * @param {Roo.menu.Menu} this
3670          */
3671         beforehide : true,
3672         /**
3673          * @event show
3674          * Fires after this menu is displayed
3675          * @param {Roo.menu.Menu} this
3676          */
3677         show : true,
3678         /**
3679          * @event hide
3680          * Fires after this menu is hidden
3681          * @param {Roo.menu.Menu} this
3682          */
3683         hide : true,
3684         /**
3685          * @event click
3686          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          * @param {Roo.EventObject} e
3690          */
3691         click : true,
3692         /**
3693          * @event mouseover
3694          * Fires when the mouse is hovering over this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseover : true,
3700         /**
3701          * @event mouseout
3702          * Fires when the mouse exits this menu
3703          * @param {Roo.menu.Menu} this
3704          * @param {Roo.EventObject} e
3705          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3706          */
3707         mouseout : true,
3708         /**
3709          * @event itemclick
3710          * Fires when a menu item contained in this menu is clicked
3711          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3712          * @param {Roo.EventObject} e
3713          */
3714         itemclick: true
3715     });
3716     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3717 };
3718
3719 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3720     
3721    /// html : false,
3722    
3723     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3724     type: false,
3725     /**
3726      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3727      */
3728     registerMenu : true,
3729     
3730     menuItems :false, // stores the menu items..
3731     
3732     hidden:true,
3733         
3734     parentMenu : false,
3735     
3736     stopEvent : true,
3737     
3738     isLink : false,
3739     
3740     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3741     
3742     hideTrigger : false,
3743     
3744     align : 'tl-bl?',
3745     
3746     
3747     getChildContainer : function() {
3748         return this.el;  
3749     },
3750     
3751     getAutoCreate : function(){
3752          
3753         //if (['right'].indexOf(this.align)!==-1) {
3754         //    cfg.cn[1].cls += ' pull-right'
3755         //}
3756          
3757         var cfg = {
3758             tag : 'ul',
3759             cls : 'dropdown-menu shadow' ,
3760             style : 'z-index:1000'
3761             
3762         };
3763         
3764         if (this.type === 'submenu') {
3765             cfg.cls = 'submenu active';
3766         }
3767         if (this.type === 'treeview') {
3768             cfg.cls = 'treeview-menu';
3769         }
3770         
3771         return cfg;
3772     },
3773     initEvents : function() {
3774         
3775        // Roo.log("ADD event");
3776        // Roo.log(this.triggerEl.dom);
3777         if (this.triggerEl) {
3778             
3779             this.triggerEl.on('click', this.onTriggerClick, this);
3780             
3781             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3782             
3783             if (!this.hideTrigger) {
3784                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3785                     // dropdown toggle on the 'a' in BS4?
3786                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3787                 } else {
3788                     this.triggerEl.addClass('dropdown-toggle');
3789                 }
3790             }
3791         }
3792         
3793         if (Roo.isTouch) {
3794             this.el.on('touchstart'  , this.onTouch, this);
3795         }
3796         this.el.on('click' , this.onClick, this);
3797
3798         this.el.on("mouseover", this.onMouseOver, this);
3799         this.el.on("mouseout", this.onMouseOut, this);
3800         
3801     },
3802     
3803     findTargetItem : function(e)
3804     {
3805         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3806         if(!t){
3807             return false;
3808         }
3809         //Roo.log(t);         Roo.log(t.id);
3810         if(t && t.id){
3811             //Roo.log(this.menuitems);
3812             return this.menuitems.get(t.id);
3813             
3814             //return this.items.get(t.menuItemId);
3815         }
3816         
3817         return false;
3818     },
3819     
3820     onTouch : function(e) 
3821     {
3822         Roo.log("menu.onTouch");
3823         //e.stopEvent(); this make the user popdown broken
3824         this.onClick(e);
3825     },
3826     
3827     onClick : function(e)
3828     {
3829         Roo.log("menu.onClick");
3830         
3831         var t = this.findTargetItem(e);
3832         if(!t || t.isContainer){
3833             return;
3834         }
3835         Roo.log(e);
3836         /*
3837         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3838             if(t == this.activeItem && t.shouldDeactivate(e)){
3839                 this.activeItem.deactivate();
3840                 delete this.activeItem;
3841                 return;
3842             }
3843             if(t.canActivate){
3844                 this.setActiveItem(t, true);
3845             }
3846             return;
3847             
3848             
3849         }
3850         */
3851        
3852         Roo.log('pass click event');
3853         
3854         t.onClick(e);
3855         
3856         this.fireEvent("click", this, t, e);
3857         
3858         var _this = this;
3859         
3860         if(!t.href.length || t.href == '#'){
3861             (function() { _this.hide(); }).defer(100);
3862         }
3863         
3864     },
3865     
3866     onMouseOver : function(e){
3867         var t  = this.findTargetItem(e);
3868         //Roo.log(t);
3869         //if(t){
3870         //    if(t.canActivate && !t.disabled){
3871         //        this.setActiveItem(t, true);
3872         //    }
3873         //}
3874         
3875         this.fireEvent("mouseover", this, e, t);
3876     },
3877     isVisible : function(){
3878         return !this.hidden;
3879     },
3880     onMouseOut : function(e){
3881         var t  = this.findTargetItem(e);
3882         
3883         //if(t ){
3884         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3885         //        this.activeItem.deactivate();
3886         //        delete this.activeItem;
3887         //    }
3888         //}
3889         this.fireEvent("mouseout", this, e, t);
3890     },
3891     
3892     
3893     /**
3894      * Displays this menu relative to another element
3895      * @param {String/HTMLElement/Roo.Element} element The element to align to
3896      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3897      * the element (defaults to this.defaultAlign)
3898      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3899      */
3900     show : function(el, pos, parentMenu)
3901     {
3902         if (false === this.fireEvent("beforeshow", this)) {
3903             Roo.log("show canceled");
3904             return;
3905         }
3906         this.parentMenu = parentMenu;
3907         if(!this.el){
3908             this.render();
3909         }
3910         this.el.addClass('show'); // show otherwise we do not know how big we are..
3911          
3912         var xy = this.el.getAlignToXY(el, pos);
3913         
3914         // bl-tl << left align  below
3915         // tl-bl << left align 
3916         
3917         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3918             // if it goes to far to the right.. -> align left.
3919             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3920         }
3921         if(xy[0] < 0){
3922             // was left align - go right?
3923             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3924         }
3925         
3926         // goes down the bottom
3927         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3928            xy[1]  < 0 ){
3929             var a = this.align.replace('?', '').split('-');
3930             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3931             
3932         }
3933         
3934         this.showAt(  xy , parentMenu, false);
3935     },
3936      /**
3937      * Displays this menu at a specific xy position
3938      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3939      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3940      */
3941     showAt : function(xy, parentMenu, /* private: */_e){
3942         this.parentMenu = parentMenu;
3943         if(!this.el){
3944             this.render();
3945         }
3946         if(_e !== false){
3947             this.fireEvent("beforeshow", this);
3948             //xy = this.el.adjustForConstraints(xy);
3949         }
3950         
3951         //this.el.show();
3952         this.hideMenuItems();
3953         this.hidden = false;
3954         if (this.triggerEl) {
3955             this.triggerEl.addClass('open');
3956         }
3957         
3958         this.el.addClass('show');
3959         
3960         
3961         
3962         // reassign x when hitting right
3963         
3964         // reassign y when hitting bottom
3965         
3966         // but the list may align on trigger left or trigger top... should it be a properity?
3967         
3968         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3969             this.el.setXY(xy);
3970         }
3971         
3972         this.focus();
3973         this.fireEvent("show", this);
3974     },
3975     
3976     focus : function(){
3977         return;
3978         if(!this.hidden){
3979             this.doFocus.defer(50, this);
3980         }
3981     },
3982
3983     doFocus : function(){
3984         if(!this.hidden){
3985             this.focusEl.focus();
3986         }
3987     },
3988
3989     /**
3990      * Hides this menu and optionally all parent menus
3991      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3992      */
3993     hide : function(deep)
3994     {
3995         if (false === this.fireEvent("beforehide", this)) {
3996             Roo.log("hide canceled");
3997             return;
3998         }
3999         this.hideMenuItems();
4000         if(this.el && this.isVisible()){
4001            
4002             if(this.activeItem){
4003                 this.activeItem.deactivate();
4004                 this.activeItem = null;
4005             }
4006             if (this.triggerEl) {
4007                 this.triggerEl.removeClass('open');
4008             }
4009             
4010             this.el.removeClass('show');
4011             this.hidden = true;
4012             this.fireEvent("hide", this);
4013         }
4014         if(deep === true && this.parentMenu){
4015             this.parentMenu.hide(true);
4016         }
4017     },
4018     
4019     onTriggerClick : function(e)
4020     {
4021         Roo.log('trigger click');
4022         
4023         var target = e.getTarget();
4024         
4025         Roo.log(target.nodeName.toLowerCase());
4026         
4027         if(target.nodeName.toLowerCase() === 'i'){
4028             e.preventDefault();
4029         }
4030         
4031     },
4032     
4033     onTriggerPress  : function(e)
4034     {
4035         Roo.log('trigger press');
4036         //Roo.log(e.getTarget());
4037        // Roo.log(this.triggerEl.dom);
4038        
4039         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4040         var pel = Roo.get(e.getTarget());
4041         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4042             Roo.log('is treeview or dropdown?');
4043             return;
4044         }
4045         
4046         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4047             return;
4048         }
4049         
4050         if (this.isVisible()) {
4051             Roo.log('hide');
4052             this.hide();
4053         } else {
4054             Roo.log('show');
4055             
4056             this.show(this.triggerEl, this.align, false);
4057         }
4058         
4059         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4060             e.stopEvent();
4061         }
4062         
4063     },
4064        
4065     
4066     hideMenuItems : function()
4067     {
4068         Roo.log("hide Menu Items");
4069         if (!this.el) { 
4070             return;
4071         }
4072         
4073         this.el.select('.open',true).each(function(aa) {
4074             
4075             aa.removeClass('open');
4076          
4077         });
4078     },
4079     addxtypeChild : function (tree, cntr) {
4080         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4081           
4082         this.menuitems.add(comp);
4083         return comp;
4084
4085     },
4086     getEl : function()
4087     {
4088         Roo.log(this.el);
4089         return this.el;
4090     },
4091     
4092     clear : function()
4093     {
4094         this.getEl().dom.innerHTML = '';
4095         this.menuitems.clear();
4096     }
4097 });
4098
4099  
4100  /*
4101  * - LGPL
4102  *
4103  * menu item
4104  * 
4105  */
4106
4107
4108 /**
4109  * @class Roo.bootstrap.MenuItem
4110  * @extends Roo.bootstrap.Component
4111  * Bootstrap MenuItem class
4112  * @cfg {String} html the menu label
4113  * @cfg {String} href the link
4114  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4115  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4116  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4117  * @cfg {String} fa favicon to show on left of menu item.
4118  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4119  * 
4120  * 
4121  * @constructor
4122  * Create a new MenuItem
4123  * @param {Object} config The config object
4124  */
4125
4126
4127 Roo.bootstrap.MenuItem = function(config){
4128     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4129     this.addEvents({
4130         // raw events
4131         /**
4132          * @event click
4133          * The raw click event for the entire grid.
4134          * @param {Roo.bootstrap.MenuItem} this
4135          * @param {Roo.EventObject} e
4136          */
4137         "click" : true
4138     });
4139 };
4140
4141 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4142     
4143     href : false,
4144     html : false,
4145     preventDefault: false,
4146     isContainer : false,
4147     active : false,
4148     fa: false,
4149     
4150     getAutoCreate : function(){
4151         
4152         if(this.isContainer){
4153             return {
4154                 tag: 'li',
4155                 cls: 'dropdown-menu-item '
4156             };
4157         }
4158         var ctag = {
4159             tag: 'span',
4160             html: 'Link'
4161         };
4162         
4163         var anc = {
4164             tag : 'a',
4165             cls : 'dropdown-item',
4166             href : '#',
4167             cn : [  ]
4168         };
4169         
4170         if (this.fa !== false) {
4171             anc.cn.push({
4172                 tag : 'i',
4173                 cls : 'fa fa-' + this.fa
4174             });
4175         }
4176         
4177         anc.cn.push(ctag);
4178         
4179         
4180         var cfg= {
4181             tag: 'li',
4182             cls: 'dropdown-menu-item',
4183             cn: [ anc ]
4184         };
4185         if (this.parent().type == 'treeview') {
4186             cfg.cls = 'treeview-menu';
4187         }
4188         if (this.active) {
4189             cfg.cls += ' active';
4190         }
4191         
4192         
4193         
4194         anc.href = this.href || cfg.cn[0].href ;
4195         ctag.html = this.html || cfg.cn[0].html ;
4196         return cfg;
4197     },
4198     
4199     initEvents: function()
4200     {
4201         if (this.parent().type == 'treeview') {
4202             this.el.select('a').on('click', this.onClick, this);
4203         }
4204         
4205         if (this.menu) {
4206             this.menu.parentType = this.xtype;
4207             this.menu.triggerEl = this.el;
4208             this.menu = this.addxtype(Roo.apply({}, this.menu));
4209         }
4210         
4211     },
4212     onClick : function(e)
4213     {
4214         Roo.log('item on click ');
4215         
4216         if(this.preventDefault){
4217             e.preventDefault();
4218         }
4219         //this.parent().hideMenuItems();
4220         
4221         this.fireEvent('click', this, e);
4222     },
4223     getEl : function()
4224     {
4225         return this.el;
4226     } 
4227 });
4228
4229  
4230
4231  /*
4232  * - LGPL
4233  *
4234  * menu separator
4235  * 
4236  */
4237
4238
4239 /**
4240  * @class Roo.bootstrap.MenuSeparator
4241  * @extends Roo.bootstrap.Component
4242  * Bootstrap MenuSeparator class
4243  * 
4244  * @constructor
4245  * Create a new MenuItem
4246  * @param {Object} config The config object
4247  */
4248
4249
4250 Roo.bootstrap.MenuSeparator = function(config){
4251     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4252 };
4253
4254 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4255     
4256     getAutoCreate : function(){
4257         var cfg = {
4258             cls: 'divider',
4259             tag : 'li'
4260         };
4261         
4262         return cfg;
4263     }
4264    
4265 });
4266
4267  
4268
4269  
4270 /*
4271 * Licence: LGPL
4272 */
4273
4274 /**
4275  * @class Roo.bootstrap.Modal
4276  * @extends Roo.bootstrap.Component
4277  * @builder-top
4278  * @parent none
4279  * @children Roo.bootstrap.Component
4280  * Bootstrap Modal class
4281  * @cfg {String} title Title of dialog
4282  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4283  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4284  * @cfg {Boolean} specificTitle default false
4285  * @cfg {Array} buttons Array of buttons or standard button set..
4286  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4287  * @cfg {Boolean} animate default true
4288  * @cfg {Boolean} allow_close default true
4289  * @cfg {Boolean} fitwindow default false
4290  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4291  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4292  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4293  * @cfg {String} size (sm|lg|xl) default empty
4294  * @cfg {Number} max_width set the max width of modal
4295  * @cfg {Boolean} editableTitle can the title be edited
4296
4297  *
4298  *
4299  * @constructor
4300  * Create a new Modal Dialog
4301  * @param {Object} config The config object
4302  */
4303
4304 Roo.bootstrap.Modal = function(config){
4305     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4306     this.addEvents({
4307         // raw events
4308         /**
4309          * @event btnclick
4310          * The raw btnclick event for the button
4311          * @param {Roo.EventObject} e
4312          */
4313         "btnclick" : true,
4314         /**
4315          * @event resize
4316          * Fire when dialog resize
4317          * @param {Roo.bootstrap.Modal} this
4318          * @param {Roo.EventObject} e
4319          */
4320         "resize" : true,
4321         /**
4322          * @event titlechanged
4323          * Fire when the editable title has been changed
4324          * @param {Roo.bootstrap.Modal} this
4325          * @param {Roo.EventObject} value
4326          */
4327         "titlechanged" : true 
4328         
4329     });
4330     this.buttons = this.buttons || [];
4331
4332     if (this.tmpl) {
4333         this.tmpl = Roo.factory(this.tmpl);
4334     }
4335
4336 };
4337
4338 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4339
4340     title : 'test dialog',
4341
4342     buttons : false,
4343
4344     // set on load...
4345
4346     html: false,
4347
4348     tmp: false,
4349
4350     specificTitle: false,
4351
4352     buttonPosition: 'right',
4353
4354     allow_close : true,
4355
4356     animate : true,
4357
4358     fitwindow: false,
4359     
4360      // private
4361     dialogEl: false,
4362     bodyEl:  false,
4363     footerEl:  false,
4364     titleEl:  false,
4365     closeEl:  false,
4366
4367     size: '',
4368     
4369     max_width: 0,
4370     
4371     max_height: 0,
4372     
4373     fit_content: false,
4374     editableTitle  : false,
4375
4376     onRender : function(ct, position)
4377     {
4378         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4379
4380         if(!this.el){
4381             var cfg = Roo.apply({},  this.getAutoCreate());
4382             cfg.id = Roo.id();
4383             //if(!cfg.name){
4384             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4385             //}
4386             //if (!cfg.name.length) {
4387             //    delete cfg.name;
4388            // }
4389             if (this.cls) {
4390                 cfg.cls += ' ' + this.cls;
4391             }
4392             if (this.style) {
4393                 cfg.style = this.style;
4394             }
4395             this.el = Roo.get(document.body).createChild(cfg, position);
4396         }
4397         //var type = this.el.dom.type;
4398
4399
4400         if(this.tabIndex !== undefined){
4401             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4402         }
4403
4404         this.dialogEl = this.el.select('.modal-dialog',true).first();
4405         this.bodyEl = this.el.select('.modal-body',true).first();
4406         this.closeEl = this.el.select('.modal-header .close', true).first();
4407         this.headerEl = this.el.select('.modal-header',true).first();
4408         this.titleEl = this.el.select('.modal-title',true).first();
4409         this.footerEl = this.el.select('.modal-footer',true).first();
4410
4411         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4412         
4413         //this.el.addClass("x-dlg-modal");
4414
4415         if (this.buttons.length) {
4416             Roo.each(this.buttons, function(bb) {
4417                 var b = Roo.apply({}, bb);
4418                 b.xns = b.xns || Roo.bootstrap;
4419                 b.xtype = b.xtype || 'Button';
4420                 if (typeof(b.listeners) == 'undefined') {
4421                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4422                 }
4423
4424                 var btn = Roo.factory(b);
4425
4426                 btn.render(this.getButtonContainer());
4427
4428             },this);
4429         }
4430         // render the children.
4431         var nitems = [];
4432
4433         if(typeof(this.items) != 'undefined'){
4434             var items = this.items;
4435             delete this.items;
4436
4437             for(var i =0;i < items.length;i++) {
4438                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4439             }
4440         }
4441
4442         this.items = nitems;
4443
4444         // where are these used - they used to be body/close/footer
4445
4446
4447         this.initEvents();
4448         //this.el.addClass([this.fieldClass, this.cls]);
4449
4450     },
4451
4452     getAutoCreate : function()
4453     {
4454         // we will default to modal-body-overflow - might need to remove or make optional later.
4455         var bdy = {
4456                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4457                 html : this.html || ''
4458         };
4459
4460         var title = {
4461             tag: 'h5',
4462             cls : 'modal-title',
4463             html : this.title
4464         };
4465
4466         if(this.specificTitle){ // WTF is this?
4467             title = this.title;
4468         }
4469
4470         var header = [];
4471         if (this.allow_close && Roo.bootstrap.version == 3) {
4472             header.push({
4473                 tag: 'button',
4474                 cls : 'close',
4475                 html : '&times'
4476             });
4477         }
4478
4479         header.push(title);
4480
4481         if (this.editableTitle) {
4482             header.push({
4483                 cls: 'form-control roo-editable-title d-none',
4484                 tag: 'input',
4485                 type: 'text'
4486             });
4487         }
4488         
4489         if (this.allow_close && Roo.bootstrap.version == 4) {
4490             header.push({
4491                 tag: 'button',
4492                 cls : 'close',
4493                 html : '&times'
4494             });
4495         }
4496         
4497         var size = '';
4498
4499         if(this.size.length){
4500             size = 'modal-' + this.size;
4501         }
4502         
4503         var footer = Roo.bootstrap.version == 3 ?
4504             {
4505                 cls : 'modal-footer',
4506                 cn : [
4507                     {
4508                         tag: 'div',
4509                         cls: 'btn-' + this.buttonPosition
4510                     }
4511                 ]
4512
4513             } :
4514             {  // BS4 uses mr-auto on left buttons....
4515                 cls : 'modal-footer'
4516             };
4517
4518             
4519
4520         
4521         
4522         var modal = {
4523             cls: "modal",
4524              cn : [
4525                 {
4526                     cls: "modal-dialog " + size,
4527                     cn : [
4528                         {
4529                             cls : "modal-content",
4530                             cn : [
4531                                 {
4532                                     cls : 'modal-header',
4533                                     cn : header
4534                                 },
4535                                 bdy,
4536                                 footer
4537                             ]
4538
4539                         }
4540                     ]
4541
4542                 }
4543             ]
4544         };
4545
4546         if(this.animate){
4547             modal.cls += ' fade';
4548         }
4549
4550         return modal;
4551
4552     },
4553     getChildContainer : function() {
4554
4555          return this.bodyEl;
4556
4557     },
4558     getButtonContainer : function() {
4559         
4560          return Roo.bootstrap.version == 4 ?
4561             this.el.select('.modal-footer',true).first()
4562             : this.el.select('.modal-footer div',true).first();
4563
4564     },
4565     initEvents : function()
4566     {
4567         if (this.allow_close) {
4568             this.closeEl.on('click', this.hide, this);
4569         }
4570         Roo.EventManager.onWindowResize(this.resize, this, true);
4571         if (this.editableTitle) {
4572             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4573             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4574             this.headerEditEl.on('keyup', function(e) {
4575                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4576                         this.toggleHeaderInput(false)
4577                     }
4578                 }, this);
4579             this.headerEditEl.on('blur', function(e) {
4580                 this.toggleHeaderInput(false)
4581             },this);
4582         }
4583
4584     },
4585   
4586
4587     resize : function()
4588     {
4589         this.maskEl.setSize(
4590             Roo.lib.Dom.getViewWidth(true),
4591             Roo.lib.Dom.getViewHeight(true)
4592         );
4593         
4594         if (this.fitwindow) {
4595             
4596            this.dialogEl.setStyle( { 'max-width' : '100%' });
4597             this.setSize(
4598                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4599                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4600             );
4601             return;
4602         }
4603         
4604         if(this.max_width !== 0) {
4605             
4606             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4607             
4608             if(this.height) {
4609                 this.setSize(w, this.height);
4610                 return;
4611             }
4612             
4613             if(this.max_height) {
4614                 this.setSize(w,Math.min(
4615                     this.max_height,
4616                     Roo.lib.Dom.getViewportHeight(true) - 60
4617                 ));
4618                 
4619                 return;
4620             }
4621             
4622             if(!this.fit_content) {
4623                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4624                 return;
4625             }
4626             
4627             this.setSize(w, Math.min(
4628                 60 +
4629                 this.headerEl.getHeight() + 
4630                 this.footerEl.getHeight() + 
4631                 this.getChildHeight(this.bodyEl.dom.childNodes),
4632                 Roo.lib.Dom.getViewportHeight(true) - 60)
4633             );
4634         }
4635         
4636     },
4637
4638     setSize : function(w,h)
4639     {
4640         if (!w && !h) {
4641             return;
4642         }
4643         
4644         this.resizeTo(w,h);
4645     },
4646
4647     show : function() {
4648
4649         if (!this.rendered) {
4650             this.render();
4651         }
4652         this.toggleHeaderInput(false);
4653         //this.el.setStyle('display', 'block');
4654         this.el.removeClass('hideing');
4655         this.el.dom.style.display='block';
4656         
4657         Roo.get(document.body).addClass('modal-open');
4658  
4659         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4660             
4661             (function(){
4662                 this.el.addClass('show');
4663                 this.el.addClass('in');
4664             }).defer(50, this);
4665         }else{
4666             this.el.addClass('show');
4667             this.el.addClass('in');
4668         }
4669
4670         // not sure how we can show data in here..
4671         //if (this.tmpl) {
4672         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4673         //}
4674
4675         Roo.get(document.body).addClass("x-body-masked");
4676         
4677         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4678         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4679         this.maskEl.dom.style.display = 'block';
4680         this.maskEl.addClass('show');
4681         
4682         
4683         this.resize();
4684         
4685         this.fireEvent('show', this);
4686
4687         // set zindex here - otherwise it appears to be ignored...
4688         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4689
4690         (function () {
4691             this.items.forEach( function(e) {
4692                 e.layout ? e.layout() : false;
4693
4694             });
4695         }).defer(100,this);
4696
4697     },
4698     hide : function()
4699     {
4700         if(this.fireEvent("beforehide", this) !== false){
4701             
4702             this.maskEl.removeClass('show');
4703             
4704             this.maskEl.dom.style.display = '';
4705             Roo.get(document.body).removeClass("x-body-masked");
4706             this.el.removeClass('in');
4707             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4708
4709             if(this.animate){ // why
4710                 this.el.addClass('hideing');
4711                 this.el.removeClass('show');
4712                 (function(){
4713                     if (!this.el.hasClass('hideing')) {
4714                         return; // it's been shown again...
4715                     }
4716                     
4717                     this.el.dom.style.display='';
4718
4719                     Roo.get(document.body).removeClass('modal-open');
4720                     this.el.removeClass('hideing');
4721                 }).defer(150,this);
4722                 
4723             }else{
4724                 this.el.removeClass('show');
4725                 this.el.dom.style.display='';
4726                 Roo.get(document.body).removeClass('modal-open');
4727
4728             }
4729             this.fireEvent('hide', this);
4730         }
4731     },
4732     isVisible : function()
4733     {
4734         
4735         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4736         
4737     },
4738
4739     addButton : function(str, cb)
4740     {
4741
4742
4743         var b = Roo.apply({}, { html : str } );
4744         b.xns = b.xns || Roo.bootstrap;
4745         b.xtype = b.xtype || 'Button';
4746         if (typeof(b.listeners) == 'undefined') {
4747             b.listeners = { click : cb.createDelegate(this)  };
4748         }
4749
4750         var btn = Roo.factory(b);
4751
4752         btn.render(this.getButtonContainer());
4753
4754         return btn;
4755
4756     },
4757
4758     setDefaultButton : function(btn)
4759     {
4760         //this.el.select('.modal-footer').()
4761     },
4762
4763     resizeTo: function(w,h)
4764     {
4765         this.dialogEl.setWidth(w);
4766         
4767         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4768
4769         this.bodyEl.setHeight(h - diff);
4770         
4771         this.fireEvent('resize', this);
4772     },
4773     
4774     setContentSize  : function(w, h)
4775     {
4776
4777     },
4778     onButtonClick: function(btn,e)
4779     {
4780         //Roo.log([a,b,c]);
4781         this.fireEvent('btnclick', btn.name, e);
4782     },
4783      /**
4784      * Set the title of the Dialog
4785      * @param {String} str new Title
4786      */
4787     setTitle: function(str) {
4788         this.titleEl.dom.innerHTML = str;
4789         this.title = str;
4790     },
4791     /**
4792      * Set the body of the Dialog
4793      * @param {String} str new Title
4794      */
4795     setBody: function(str) {
4796         this.bodyEl.dom.innerHTML = str;
4797     },
4798     /**
4799      * Set the body of the Dialog using the template
4800      * @param {Obj} data - apply this data to the template and replace the body contents.
4801      */
4802     applyBody: function(obj)
4803     {
4804         if (!this.tmpl) {
4805             Roo.log("Error - using apply Body without a template");
4806             //code
4807         }
4808         this.tmpl.overwrite(this.bodyEl, obj);
4809     },
4810     
4811     getChildHeight : function(child_nodes)
4812     {
4813         if(
4814             !child_nodes ||
4815             child_nodes.length == 0
4816         ) {
4817             return 0;
4818         }
4819         
4820         var child_height = 0;
4821         
4822         for(var i = 0; i < child_nodes.length; i++) {
4823             
4824             /*
4825             * for modal with tabs...
4826             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4827                 
4828                 var layout_childs = child_nodes[i].childNodes;
4829                 
4830                 for(var j = 0; j < layout_childs.length; j++) {
4831                     
4832                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4833                         
4834                         var layout_body_childs = layout_childs[j].childNodes;
4835                         
4836                         for(var k = 0; k < layout_body_childs.length; k++) {
4837                             
4838                             if(layout_body_childs[k].classList.contains('navbar')) {
4839                                 child_height += layout_body_childs[k].offsetHeight;
4840                                 continue;
4841                             }
4842                             
4843                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4844                                 
4845                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4846                                 
4847                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4848                                     
4849                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4850                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4851                                         continue;
4852                                     }
4853                                     
4854                                 }
4855                                 
4856                             }
4857                             
4858                         }
4859                     }
4860                 }
4861                 continue;
4862             }
4863             */
4864             
4865             child_height += child_nodes[i].offsetHeight;
4866             // Roo.log(child_nodes[i].offsetHeight);
4867         }
4868         
4869         return child_height;
4870     },
4871     toggleHeaderInput : function(is_edit)
4872     {
4873         if (!this.editableTitle) {
4874             return; // not editable.
4875         }
4876         if (is_edit && this.is_header_editing) {
4877             return; // already editing..
4878         }
4879         if (is_edit) {
4880     
4881             this.headerEditEl.dom.value = this.title;
4882             this.headerEditEl.removeClass('d-none');
4883             this.headerEditEl.dom.focus();
4884             this.titleEl.addClass('d-none');
4885             
4886             this.is_header_editing = true;
4887             return
4888         }
4889         // flip back to not editing.
4890         this.title = this.headerEditEl.dom.value;
4891         this.headerEditEl.addClass('d-none');
4892         this.titleEl.removeClass('d-none');
4893         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4894         this.is_header_editing = false;
4895         this.fireEvent('titlechanged', this, this.title);
4896     
4897             
4898         
4899     }
4900
4901 });
4902
4903
4904 Roo.apply(Roo.bootstrap.Modal,  {
4905     /**
4906          * Button config that displays a single OK button
4907          * @type Object
4908          */
4909         OK :  [{
4910             name : 'ok',
4911             weight : 'primary',
4912             html : 'OK'
4913         }],
4914         /**
4915          * Button config that displays Yes and No buttons
4916          * @type Object
4917          */
4918         YESNO : [
4919             {
4920                 name  : 'no',
4921                 html : 'No'
4922             },
4923             {
4924                 name  :'yes',
4925                 weight : 'primary',
4926                 html : 'Yes'
4927             }
4928         ],
4929
4930         /**
4931          * Button config that displays OK and Cancel buttons
4932          * @type Object
4933          */
4934         OKCANCEL : [
4935             {
4936                name : 'cancel',
4937                 html : 'Cancel'
4938             },
4939             {
4940                 name : 'ok',
4941                 weight : 'primary',
4942                 html : 'OK'
4943             }
4944         ],
4945         /**
4946          * Button config that displays Yes, No and Cancel buttons
4947          * @type Object
4948          */
4949         YESNOCANCEL : [
4950             {
4951                 name : 'yes',
4952                 weight : 'primary',
4953                 html : 'Yes'
4954             },
4955             {
4956                 name : 'no',
4957                 html : 'No'
4958             },
4959             {
4960                 name : 'cancel',
4961                 html : 'Cancel'
4962             }
4963         ],
4964         
4965         zIndex : 10001
4966 });
4967
4968 /*
4969  * - LGPL
4970  *
4971  * messagebox - can be used as a replace
4972  * 
4973  */
4974 /**
4975  * @class Roo.MessageBox
4976  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4977  * Example usage:
4978  *<pre><code>
4979 // Basic alert:
4980 Roo.Msg.alert('Status', 'Changes saved successfully.');
4981
4982 // Prompt for user data:
4983 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4984     if (btn == 'ok'){
4985         // process text value...
4986     }
4987 });
4988
4989 // Show a dialog using config options:
4990 Roo.Msg.show({
4991    title:'Save Changes?',
4992    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4993    buttons: Roo.Msg.YESNOCANCEL,
4994    fn: processResult,
4995    animEl: 'elId'
4996 });
4997 </code></pre>
4998  * @singleton
4999  */
5000 Roo.bootstrap.MessageBox = function(){
5001     var dlg, opt, mask, waitTimer;
5002     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5003     var buttons, activeTextEl, bwidth;
5004
5005     
5006     // private
5007     var handleButton = function(button){
5008         dlg.hide();
5009         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5010     };
5011
5012     // private
5013     var handleHide = function(){
5014         if(opt && opt.cls){
5015             dlg.el.removeClass(opt.cls);
5016         }
5017         //if(waitTimer){
5018         //    Roo.TaskMgr.stop(waitTimer);
5019         //    waitTimer = null;
5020         //}
5021     };
5022
5023     // private
5024     var updateButtons = function(b){
5025         var width = 0;
5026         if(!b){
5027             buttons["ok"].hide();
5028             buttons["cancel"].hide();
5029             buttons["yes"].hide();
5030             buttons["no"].hide();
5031             dlg.footerEl.hide();
5032             
5033             return width;
5034         }
5035         dlg.footerEl.show();
5036         for(var k in buttons){
5037             if(typeof buttons[k] != "function"){
5038                 if(b[k]){
5039                     buttons[k].show();
5040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5041                     width += buttons[k].el.getWidth()+15;
5042                 }else{
5043                     buttons[k].hide();
5044                 }
5045             }
5046         }
5047         return width;
5048     };
5049
5050     // private
5051     var handleEsc = function(d, k, e){
5052         if(opt && opt.closable !== false){
5053             dlg.hide();
5054         }
5055         if(e){
5056             e.stopEvent();
5057         }
5058     };
5059
5060     return {
5061         /**
5062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5063          * @return {Roo.BasicDialog} The BasicDialog element
5064          */
5065         getDialog : function(){
5066            if(!dlg){
5067                 dlg = new Roo.bootstrap.Modal( {
5068                     //draggable: true,
5069                     //resizable:false,
5070                     //constraintoviewport:false,
5071                     //fixedcenter:true,
5072                     //collapsible : false,
5073                     //shim:true,
5074                     //modal: true,
5075                 //    width: 'auto',
5076                   //  height:100,
5077                     //buttonAlign:"center",
5078                     closeClick : function(){
5079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5080                             handleButton("no");
5081                         }else{
5082                             handleButton("cancel");
5083                         }
5084                     }
5085                 });
5086                 dlg.render();
5087                 dlg.on("hide", handleHide);
5088                 mask = dlg.mask;
5089                 //dlg.addKeyListener(27, handleEsc);
5090                 buttons = {};
5091                 this.buttons = buttons;
5092                 var bt = this.buttonText;
5093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5097                 //Roo.log(buttons);
5098                 bodyEl = dlg.bodyEl.createChild({
5099
5100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5101                         '<textarea class="roo-mb-textarea"></textarea>' +
5102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5103                 });
5104                 msgEl = bodyEl.dom.firstChild;
5105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5106                 textboxEl.enableDisplayMode();
5107                 textboxEl.addKeyListener([10,13], function(){
5108                     if(dlg.isVisible() && opt && opt.buttons){
5109                         if(opt.buttons.ok){
5110                             handleButton("ok");
5111                         }else if(opt.buttons.yes){
5112                             handleButton("yes");
5113                         }
5114                     }
5115                 });
5116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5117                 textareaEl.enableDisplayMode();
5118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5119                 progressEl.enableDisplayMode();
5120                 
5121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5122                 var pf = progressEl.dom.firstChild;
5123                 if (pf) {
5124                     pp = Roo.get(pf.firstChild);
5125                     pp.setHeight(pf.offsetHeight);
5126                 }
5127                 
5128             }
5129             return dlg;
5130         },
5131
5132         /**
5133          * Updates the message box body text
5134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5135          * the XHTML-compliant non-breaking space character '&amp;#160;')
5136          * @return {Roo.MessageBox} This message box
5137          */
5138         updateText : function(text)
5139         {
5140             if(!dlg.isVisible() && !opt.width){
5141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5143             }
5144             msgEl.innerHTML = text || '&#160;';
5145       
5146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5148             var w = Math.max(
5149                     Math.min(opt.width || cw , this.maxWidth), 
5150                     Math.max(opt.minWidth || this.minWidth, bwidth)
5151             );
5152             if(opt.prompt){
5153                 activeTextEl.setWidth(w);
5154             }
5155             if(dlg.isVisible()){
5156                 dlg.fixedcenter = false;
5157             }
5158             // to big, make it scroll. = But as usual stupid IE does not support
5159             // !important..
5160             
5161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5164             } else {
5165                 bodyEl.dom.style.height = '';
5166                 bodyEl.dom.style.overflowY = '';
5167             }
5168             if (cw > w) {
5169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5170             } else {
5171                 bodyEl.dom.style.overflowX = '';
5172             }
5173             
5174             dlg.setContentSize(w, bodyEl.getHeight());
5175             if(dlg.isVisible()){
5176                 dlg.fixedcenter = true;
5177             }
5178             return this;
5179         },
5180
5181         /**
5182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5186          * @return {Roo.MessageBox} This message box
5187          */
5188         updateProgress : function(value, text){
5189             if(text){
5190                 this.updateText(text);
5191             }
5192             
5193             if (pp) { // weird bug on my firefox - for some reason this is not defined
5194                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5195                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5196             }
5197             return this;
5198         },        
5199
5200         /**
5201          * Returns true if the message box is currently displayed
5202          * @return {Boolean} True if the message box is visible, else false
5203          */
5204         isVisible : function(){
5205             return dlg && dlg.isVisible();  
5206         },
5207
5208         /**
5209          * Hides the message box if it is displayed
5210          */
5211         hide : function(){
5212             if(this.isVisible()){
5213                 dlg.hide();
5214             }  
5215         },
5216
5217         /**
5218          * Displays a new message box, or reinitializes an existing message box, based on the config options
5219          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5220          * The following config object properties are supported:
5221          * <pre>
5222 Property    Type             Description
5223 ----------  ---------------  ------------------------------------------------------------------------------------
5224 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5225                                    closes (defaults to undefined)
5226 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5227                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5228 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5229                                    progress and wait dialogs will ignore this property and always hide the
5230                                    close button as they can only be closed programmatically.
5231 cls               String           A custom CSS class to apply to the message box element
5232 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5233                                    displayed (defaults to 75)
5234 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5235                                    function will be btn (the name of the button that was clicked, if applicable,
5236                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5237                                    Progress and wait dialogs will ignore this option since they do not respond to
5238                                    user actions and can only be closed programmatically, so any required function
5239                                    should be called by the same code after it closes the dialog.
5240 icon              String           A CSS class that provides a background image to be used as an icon for
5241                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5242 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5243 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5244 modal             Boolean          False to allow user interaction with the page while the message box is
5245                                    displayed (defaults to true)
5246 msg               String           A string that will replace the existing message box body text (defaults
5247                                    to the XHTML-compliant non-breaking space character '&#160;')
5248 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5249 progress          Boolean          True to display a progress bar (defaults to false)
5250 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5251 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5252 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5253 title             String           The title text
5254 value             String           The string value to set into the active textbox element if displayed
5255 wait              Boolean          True to display a progress bar (defaults to false)
5256 width             Number           The width of the dialog in pixels
5257 </pre>
5258          *
5259          * Example usage:
5260          * <pre><code>
5261 Roo.Msg.show({
5262    title: 'Address',
5263    msg: 'Please enter your address:',
5264    width: 300,
5265    buttons: Roo.MessageBox.OKCANCEL,
5266    multiline: true,
5267    fn: saveAddress,
5268    animEl: 'addAddressBtn'
5269 });
5270 </code></pre>
5271          * @param {Object} config Configuration options
5272          * @return {Roo.MessageBox} This message box
5273          */
5274         show : function(options)
5275         {
5276             
5277             // this causes nightmares if you show one dialog after another
5278             // especially on callbacks..
5279              
5280             if(this.isVisible()){
5281                 
5282                 this.hide();
5283                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5284                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5285                 Roo.log("New Dialog Message:" +  options.msg )
5286                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5287                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5288                 
5289             }
5290             var d = this.getDialog();
5291             opt = options;
5292             d.setTitle(opt.title || "&#160;");
5293             d.closeEl.setDisplayed(opt.closable !== false);
5294             activeTextEl = textboxEl;
5295             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5296             if(opt.prompt){
5297                 if(opt.multiline){
5298                     textboxEl.hide();
5299                     textareaEl.show();
5300                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5301                         opt.multiline : this.defaultTextHeight);
5302                     activeTextEl = textareaEl;
5303                 }else{
5304                     textboxEl.show();
5305                     textareaEl.hide();
5306                 }
5307             }else{
5308                 textboxEl.hide();
5309                 textareaEl.hide();
5310             }
5311             progressEl.setDisplayed(opt.progress === true);
5312             if (opt.progress) {
5313                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5314             }
5315             this.updateProgress(0);
5316             activeTextEl.dom.value = opt.value || "";
5317             if(opt.prompt){
5318                 dlg.setDefaultButton(activeTextEl);
5319             }else{
5320                 var bs = opt.buttons;
5321                 var db = null;
5322                 if(bs && bs.ok){
5323                     db = buttons["ok"];
5324                 }else if(bs && bs.yes){
5325                     db = buttons["yes"];
5326                 }
5327                 dlg.setDefaultButton(db);
5328             }
5329             bwidth = updateButtons(opt.buttons);
5330             this.updateText(opt.msg);
5331             if(opt.cls){
5332                 d.el.addClass(opt.cls);
5333             }
5334             d.proxyDrag = opt.proxyDrag === true;
5335             d.modal = opt.modal !== false;
5336             d.mask = opt.modal !== false ? mask : false;
5337             if(!d.isVisible()){
5338                 // force it to the end of the z-index stack so it gets a cursor in FF
5339                 document.body.appendChild(dlg.el.dom);
5340                 d.animateTarget = null;
5341                 d.show(options.animEl);
5342             }
5343             return this;
5344         },
5345
5346         /**
5347          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5348          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5349          * and closing the message box when the process is complete.
5350          * @param {String} title The title bar text
5351          * @param {String} msg The message box body text
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         progress : function(title, msg){
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: false,
5359                 progress:true,
5360                 closable:false,
5361                 minWidth: this.minProgressWidth,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5369          * If a callback function is passed it will be called after the user clicks the button, and the
5370          * id of the button that was clicked will be passed as the only parameter to the callback
5371          * (could also be the top-right close button).
5372          * @param {String} title The title bar text
5373          * @param {String} msg The message box body text
5374          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5375          * @param {Object} scope (optional) The scope of the callback function
5376          * @return {Roo.MessageBox} This message box
5377          */
5378         alert : function(title, msg, fn, scope)
5379         {
5380             this.show({
5381                 title : title,
5382                 msg : msg,
5383                 buttons: this.OK,
5384                 fn: fn,
5385                 closable : false,
5386                 scope : scope,
5387                 modal : true
5388             });
5389             return this;
5390         },
5391
5392         /**
5393          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5394          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5395          * You are responsible for closing the message box when the process is complete.
5396          * @param {String} msg The message box body text
5397          * @param {String} title (optional) The title bar text
5398          * @return {Roo.MessageBox} This message box
5399          */
5400         wait : function(msg, title){
5401             this.show({
5402                 title : title,
5403                 msg : msg,
5404                 buttons: false,
5405                 closable:false,
5406                 progress:true,
5407                 modal:true,
5408                 width:300,
5409                 wait:true
5410             });
5411             waitTimer = Roo.TaskMgr.start({
5412                 run: function(i){
5413                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5414                 },
5415                 interval: 1000
5416             });
5417             return this;
5418         },
5419
5420         /**
5421          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5422          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5423          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @return {Roo.MessageBox} This message box
5429          */
5430         confirm : function(title, msg, fn, scope){
5431             this.show({
5432                 title : title,
5433                 msg : msg,
5434                 buttons: this.YESNO,
5435                 fn: fn,
5436                 scope : scope,
5437                 modal : true
5438             });
5439             return this;
5440         },
5441
5442         /**
5443          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5444          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5445          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5446          * (could also be the top-right close button) and the text that was entered will be passed as the two
5447          * parameters to the callback.
5448          * @param {String} title The title bar text
5449          * @param {String} msg The message box body text
5450          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5451          * @param {Object} scope (optional) The scope of the callback function
5452          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5453          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5454          * @return {Roo.MessageBox} This message box
5455          */
5456         prompt : function(title, msg, fn, scope, multiline){
5457             this.show({
5458                 title : title,
5459                 msg : msg,
5460                 buttons: this.OKCANCEL,
5461                 fn: fn,
5462                 minWidth:250,
5463                 scope : scope,
5464                 prompt:true,
5465                 multiline: multiline,
5466                 modal : true
5467             });
5468             return this;
5469         },
5470
5471         /**
5472          * Button config that displays a single OK button
5473          * @type Object
5474          */
5475         OK : {ok:true},
5476         /**
5477          * Button config that displays Yes and No buttons
5478          * @type Object
5479          */
5480         YESNO : {yes:true, no:true},
5481         /**
5482          * Button config that displays OK and Cancel buttons
5483          * @type Object
5484          */
5485         OKCANCEL : {ok:true, cancel:true},
5486         /**
5487          * Button config that displays Yes, No and Cancel buttons
5488          * @type Object
5489          */
5490         YESNOCANCEL : {yes:true, no:true, cancel:true},
5491
5492         /**
5493          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5494          * @type Number
5495          */
5496         defaultTextHeight : 75,
5497         /**
5498          * The maximum width in pixels of the message box (defaults to 600)
5499          * @type Number
5500          */
5501         maxWidth : 600,
5502         /**
5503          * The minimum width in pixels of the message box (defaults to 100)
5504          * @type Number
5505          */
5506         minWidth : 100,
5507         /**
5508          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5509          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5510          * @type Number
5511          */
5512         minProgressWidth : 250,
5513         /**
5514          * An object containing the default button text strings that can be overriden for localized language support.
5515          * Supported properties are: ok, cancel, yes and no.
5516          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5517          * @type Object
5518          */
5519         buttonText : {
5520             ok : "OK",
5521             cancel : "Cancel",
5522             yes : "Yes",
5523             no : "No"
5524         }
5525     };
5526 }();
5527
5528 /**
5529  * Shorthand for {@link Roo.MessageBox}
5530  */
5531 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5532 Roo.Msg = Roo.Msg || Roo.MessageBox;
5533 /*
5534  * - LGPL
5535  *
5536  * navbar
5537  * 
5538  */
5539
5540 /**
5541  * @class Roo.bootstrap.Navbar
5542  * @extends Roo.bootstrap.Component
5543  * Bootstrap Navbar class
5544
5545  * @constructor
5546  * Create a new Navbar
5547  * @param {Object} config The config object
5548  */
5549
5550
5551 Roo.bootstrap.Navbar = function(config){
5552     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5553     this.addEvents({
5554         // raw events
5555         /**
5556          * @event beforetoggle
5557          * Fire before toggle the menu
5558          * @param {Roo.EventObject} e
5559          */
5560         "beforetoggle" : true
5561     });
5562 };
5563
5564 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5565     
5566     
5567    
5568     // private
5569     navItems : false,
5570     loadMask : false,
5571     
5572     
5573     getAutoCreate : function(){
5574         
5575         
5576         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5577         
5578     },
5579     
5580     initEvents :function ()
5581     {
5582         //Roo.log(this.el.select('.navbar-toggle',true));
5583         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5584         
5585         var mark = {
5586             tag: "div",
5587             cls:"x-dlg-mask"
5588         };
5589         
5590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5591         
5592         var size = this.el.getSize();
5593         this.maskEl.setSize(size.width, size.height);
5594         this.maskEl.enableDisplayMode("block");
5595         this.maskEl.hide();
5596         
5597         if(this.loadMask){
5598             this.maskEl.show();
5599         }
5600     },
5601     
5602     
5603     getChildContainer : function()
5604     {
5605         if (this.el && this.el.select('.collapse').getCount()) {
5606             return this.el.select('.collapse',true).first();
5607         }
5608         
5609         return this.el;
5610     },
5611     
5612     mask : function()
5613     {
5614         this.maskEl.show();
5615     },
5616     
5617     unmask : function()
5618     {
5619         this.maskEl.hide();
5620     },
5621     onToggle : function()
5622     {
5623         
5624         if(this.fireEvent('beforetoggle', this) === false){
5625             return;
5626         }
5627         var ce = this.el.select('.navbar-collapse',true).first();
5628       
5629         if (!ce.hasClass('show')) {
5630            this.expand();
5631         } else {
5632             this.collapse();
5633         }
5634         
5635         
5636     
5637     },
5638     /**
5639      * Expand the navbar pulldown 
5640      */
5641     expand : function ()
5642     {
5643        
5644         var ce = this.el.select('.navbar-collapse',true).first();
5645         if (ce.hasClass('collapsing')) {
5646             return;
5647         }
5648         ce.dom.style.height = '';
5649                // show it...
5650         ce.addClass('in'); // old...
5651         ce.removeClass('collapse');
5652         ce.addClass('show');
5653         var h = ce.getHeight();
5654         Roo.log(h);
5655         ce.removeClass('show');
5656         // at this point we should be able to see it..
5657         ce.addClass('collapsing');
5658         
5659         ce.setHeight(0); // resize it ...
5660         ce.on('transitionend', function() {
5661             //Roo.log('done transition');
5662             ce.removeClass('collapsing');
5663             ce.addClass('show');
5664             ce.removeClass('collapse');
5665
5666             ce.dom.style.height = '';
5667         }, this, { single: true} );
5668         ce.setHeight(h);
5669         ce.dom.scrollTop = 0;
5670     },
5671     /**
5672      * Collapse the navbar pulldown 
5673      */
5674     collapse : function()
5675     {
5676          var ce = this.el.select('.navbar-collapse',true).first();
5677        
5678         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5679             // it's collapsed or collapsing..
5680             return;
5681         }
5682         ce.removeClass('in'); // old...
5683         ce.setHeight(ce.getHeight());
5684         ce.removeClass('show');
5685         ce.addClass('collapsing');
5686         
5687         ce.on('transitionend', function() {
5688             ce.dom.style.height = '';
5689             ce.removeClass('collapsing');
5690             ce.addClass('collapse');
5691         }, this, { single: true} );
5692         ce.setHeight(0);
5693     }
5694     
5695     
5696     
5697 });
5698
5699
5700
5701  
5702
5703  /*
5704  * - LGPL
5705  *
5706  * navbar
5707  * 
5708  */
5709
5710 /**
5711  * @class Roo.bootstrap.NavSimplebar
5712  * @extends Roo.bootstrap.Navbar
5713  * Bootstrap Sidebar class
5714  *
5715  * @cfg {Boolean} inverse is inverted color
5716  * 
5717  * @cfg {String} type (nav | pills | tabs)
5718  * @cfg {Boolean} arrangement stacked | justified
5719  * @cfg {String} align (left | right) alignment
5720  * 
5721  * @cfg {Boolean} main (true|false) main nav bar? default false
5722  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5723  * 
5724  * @cfg {String} tag (header|footer|nav|div) default is nav 
5725
5726  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5727  * 
5728  * 
5729  * @constructor
5730  * Create a new Sidebar
5731  * @param {Object} config The config object
5732  */
5733
5734
5735 Roo.bootstrap.NavSimplebar = function(config){
5736     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5737 };
5738
5739 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5740     
5741     inverse: false,
5742     
5743     type: false,
5744     arrangement: '',
5745     align : false,
5746     
5747     weight : 'light',
5748     
5749     main : false,
5750     
5751     
5752     tag : false,
5753     
5754     
5755     getAutoCreate : function(){
5756         
5757         
5758         var cfg = {
5759             tag : this.tag || 'div',
5760             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5761         };
5762         if (['light','white'].indexOf(this.weight) > -1) {
5763             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5764         }
5765         cfg.cls += ' bg-' + this.weight;
5766         
5767         if (this.inverse) {
5768             cfg.cls += ' navbar-inverse';
5769             
5770         }
5771         
5772         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5773         
5774         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5775             return cfg;
5776         }
5777         
5778         
5779     
5780         
5781         cfg.cn = [
5782             {
5783                 cls: 'nav nav-' + this.xtype,
5784                 tag : 'ul'
5785             }
5786         ];
5787         
5788          
5789         this.type = this.type || 'nav';
5790         if (['tabs','pills'].indexOf(this.type) != -1) {
5791             cfg.cn[0].cls += ' nav-' + this.type
5792         
5793         
5794         } else {
5795             if (this.type!=='nav') {
5796                 Roo.log('nav type must be nav/tabs/pills')
5797             }
5798             cfg.cn[0].cls += ' navbar-nav'
5799         }
5800         
5801         
5802         
5803         
5804         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5805             cfg.cn[0].cls += ' nav-' + this.arrangement;
5806         }
5807         
5808         
5809         if (this.align === 'right') {
5810             cfg.cn[0].cls += ' navbar-right';
5811         }
5812         
5813         
5814         
5815         
5816         return cfg;
5817     
5818         
5819     }
5820     
5821     
5822     
5823 });
5824
5825
5826
5827  
5828
5829  
5830        /*
5831  * - LGPL
5832  *
5833  * navbar
5834  * navbar-fixed-top
5835  * navbar-expand-md  fixed-top 
5836  */
5837
5838 /**
5839  * @class Roo.bootstrap.NavHeaderbar
5840  * @extends Roo.bootstrap.NavSimplebar
5841  * Bootstrap Sidebar class
5842  *
5843  * @cfg {String} brand what is brand
5844  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5845  * @cfg {String} brand_href href of the brand
5846  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5847  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5848  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5849  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5850  * 
5851  * @constructor
5852  * Create a new Sidebar
5853  * @param {Object} config The config object
5854  */
5855
5856
5857 Roo.bootstrap.NavHeaderbar = function(config){
5858     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5859       
5860 };
5861
5862 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5863     
5864     position: '',
5865     brand: '',
5866     brand_href: false,
5867     srButton : true,
5868     autohide : false,
5869     desktopCenter : false,
5870    
5871     
5872     getAutoCreate : function(){
5873         
5874         var   cfg = {
5875             tag: this.nav || 'nav',
5876             cls: 'navbar navbar-expand-md',
5877             role: 'navigation',
5878             cn: []
5879         };
5880         
5881         var cn = cfg.cn;
5882         if (this.desktopCenter) {
5883             cn.push({cls : 'container', cn : []});
5884             cn = cn[0].cn;
5885         }
5886         
5887         if(this.srButton){
5888             var btn = {
5889                 tag: 'button',
5890                 type: 'button',
5891                 cls: 'navbar-toggle navbar-toggler',
5892                 'data-toggle': 'collapse',
5893                 cn: [
5894                     {
5895                         tag: 'span',
5896                         cls: 'sr-only',
5897                         html: 'Toggle navigation'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar navbar-toggler-icon'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     },
5907                     {
5908                         tag: 'span',
5909                         cls: 'icon-bar'
5910                     }
5911                 ]
5912             };
5913             
5914             cn.push( Roo.bootstrap.version == 4 ? btn : {
5915                 tag: 'div',
5916                 cls: 'navbar-header',
5917                 cn: [
5918                     btn
5919                 ]
5920             });
5921         }
5922         
5923         cn.push({
5924             tag: 'div',
5925             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5926             cn : []
5927         });
5928         
5929         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5930         
5931         if (['light','white'].indexOf(this.weight) > -1) {
5932             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5933         }
5934         cfg.cls += ' bg-' + this.weight;
5935         
5936         
5937         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5938             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5939             
5940             // tag can override this..
5941             
5942             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5943         }
5944         
5945         if (this.brand !== '') {
5946             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5947             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5948                 tag: 'a',
5949                 href: this.brand_href ? this.brand_href : '#',
5950                 cls: 'navbar-brand',
5951                 cn: [
5952                 this.brand
5953                 ]
5954             });
5955         }
5956         
5957         if(this.main){
5958             cfg.cls += ' main-nav';
5959         }
5960         
5961         
5962         return cfg;
5963
5964         
5965     },
5966     getHeaderChildContainer : function()
5967     {
5968         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5969             return this.el.select('.navbar-header',true).first();
5970         }
5971         
5972         return this.getChildContainer();
5973     },
5974     
5975     getChildContainer : function()
5976     {
5977          
5978         return this.el.select('.roo-navbar-collapse',true).first();
5979          
5980         
5981     },
5982     
5983     initEvents : function()
5984     {
5985         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5986         
5987         if (this.autohide) {
5988             
5989             var prevScroll = 0;
5990             var ft = this.el;
5991             
5992             Roo.get(document).on('scroll',function(e) {
5993                 var ns = Roo.get(document).getScroll().top;
5994                 var os = prevScroll;
5995                 prevScroll = ns;
5996                 
5997                 if(ns > os){
5998                     ft.removeClass('slideDown');
5999                     ft.addClass('slideUp');
6000                     return;
6001                 }
6002                 ft.removeClass('slideUp');
6003                 ft.addClass('slideDown');
6004                  
6005               
6006           },this);
6007         }
6008     }    
6009     
6010 });
6011
6012
6013
6014  
6015
6016  /*
6017  * - LGPL
6018  *
6019  * navbar
6020  * 
6021  */
6022
6023 /**
6024  * @class Roo.bootstrap.NavSidebar
6025  * @extends Roo.bootstrap.Navbar
6026  * Bootstrap Sidebar class
6027  * 
6028  * @constructor
6029  * Create a new Sidebar
6030  * @param {Object} config The config object
6031  */
6032
6033
6034 Roo.bootstrap.NavSidebar = function(config){
6035     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6036 };
6037
6038 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6039     
6040     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6041     
6042     getAutoCreate : function(){
6043         
6044         
6045         return  {
6046             tag: 'div',
6047             cls: 'sidebar sidebar-nav'
6048         };
6049     
6050         
6051     }
6052     
6053     
6054     
6055 });
6056
6057
6058
6059  
6060
6061  /*
6062  * - LGPL
6063  *
6064  * nav group
6065  * 
6066  */
6067
6068 /**
6069  * @class Roo.bootstrap.NavGroup
6070  * @extends Roo.bootstrap.Component
6071  * Bootstrap NavGroup class
6072  * @cfg {String} align (left|right)
6073  * @cfg {Boolean} inverse
6074  * @cfg {String} type (nav|pills|tab) default nav
6075  * @cfg {String} navId - reference Id for navbar.
6076  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6077  * 
6078  * @constructor
6079  * Create a new nav group
6080  * @param {Object} config The config object
6081  */
6082
6083 Roo.bootstrap.NavGroup = function(config){
6084     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6085     this.navItems = [];
6086    
6087     Roo.bootstrap.NavGroup.register(this);
6088      this.addEvents({
6089         /**
6090              * @event changed
6091              * Fires when the active item changes
6092              * @param {Roo.bootstrap.NavGroup} this
6093              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6094              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6095          */
6096         'changed': true
6097      });
6098     
6099 };
6100
6101 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6102     
6103     align: '',
6104     inverse: false,
6105     form: false,
6106     type: 'nav',
6107     navId : '',
6108     // private
6109     pilltype : true,
6110     
6111     navItems : false, 
6112     
6113     getAutoCreate : function()
6114     {
6115         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6116         
6117         cfg = {
6118             tag : 'ul',
6119             cls: 'nav' 
6120         };
6121         if (Roo.bootstrap.version == 4) {
6122             if (['tabs','pills'].indexOf(this.type) != -1) {
6123                 cfg.cls += ' nav-' + this.type; 
6124             } else {
6125                 // trying to remove so header bar can right align top?
6126                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6127                     // do not use on header bar... 
6128                     cfg.cls += ' navbar-nav';
6129                 }
6130             }
6131             
6132         } else {
6133             if (['tabs','pills'].indexOf(this.type) != -1) {
6134                 cfg.cls += ' nav-' + this.type
6135             } else {
6136                 if (this.type !== 'nav') {
6137                     Roo.log('nav type must be nav/tabs/pills')
6138                 }
6139                 cfg.cls += ' navbar-nav'
6140             }
6141         }
6142         
6143         if (this.parent() && this.parent().sidebar) {
6144             cfg = {
6145                 tag: 'ul',
6146                 cls: 'dashboard-menu sidebar-menu'
6147             };
6148             
6149             return cfg;
6150         }
6151         
6152         if (this.form === true) {
6153             cfg = {
6154                 tag: 'form',
6155                 cls: 'navbar-form form-inline'
6156             };
6157             //nav navbar-right ml-md-auto
6158             if (this.align === 'right') {
6159                 cfg.cls += ' navbar-right ml-md-auto';
6160             } else {
6161                 cfg.cls += ' navbar-left';
6162             }
6163         }
6164         
6165         if (this.align === 'right') {
6166             cfg.cls += ' navbar-right ml-md-auto';
6167         } else {
6168             cfg.cls += ' mr-auto';
6169         }
6170         
6171         if (this.inverse) {
6172             cfg.cls += ' navbar-inverse';
6173             
6174         }
6175         
6176         
6177         return cfg;
6178     },
6179     /**
6180     * sets the active Navigation item
6181     * @param {Roo.bootstrap.NavItem} the new current navitem
6182     */
6183     setActiveItem : function(item)
6184     {
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             if (v == item) {
6188                 return ;
6189             }
6190             if (v.isActive()) {
6191                 v.setActive(false, true);
6192                 prev = v;
6193                 
6194             }
6195             
6196         });
6197
6198         item.setActive(true, true);
6199         this.fireEvent('changed', this, item, prev);
6200         
6201         
6202     },
6203     /**
6204     * gets the active Navigation item
6205     * @return {Roo.bootstrap.NavItem} the current navitem
6206     */
6207     getActive : function()
6208     {
6209         
6210         var prev = false;
6211         Roo.each(this.navItems, function(v){
6212             
6213             if (v.isActive()) {
6214                 prev = v;
6215                 
6216             }
6217             
6218         });
6219         return prev;
6220     },
6221     
6222     indexOfNav : function()
6223     {
6224         
6225         var prev = false;
6226         Roo.each(this.navItems, function(v,i){
6227             
6228             if (v.isActive()) {
6229                 prev = i;
6230                 
6231             }
6232             
6233         });
6234         return prev;
6235     },
6236     /**
6237     * adds a Navigation item
6238     * @param {Roo.bootstrap.NavItem} the navitem to add
6239     */
6240     addItem : function(cfg)
6241     {
6242         if (this.form && Roo.bootstrap.version == 4) {
6243             cfg.tag = 'div';
6244         }
6245         var cn = new Roo.bootstrap.NavItem(cfg);
6246         this.register(cn);
6247         cn.parentId = this.id;
6248         cn.onRender(this.el, null);
6249         return cn;
6250     },
6251     /**
6252     * register a Navigation item
6253     * @param {Roo.bootstrap.NavItem} the navitem to add
6254     */
6255     register : function(item)
6256     {
6257         this.navItems.push( item);
6258         item.navId = this.navId;
6259     
6260     },
6261     
6262     /**
6263     * clear all the Navigation item
6264     */
6265    
6266     clearAll : function()
6267     {
6268         this.navItems = [];
6269         this.el.dom.innerHTML = '';
6270     },
6271     
6272     getNavItem: function(tabId)
6273     {
6274         var ret = false;
6275         Roo.each(this.navItems, function(e) {
6276             if (e.tabId == tabId) {
6277                ret =  e;
6278                return false;
6279             }
6280             return true;
6281             
6282         });
6283         return ret;
6284     },
6285     
6286     setActiveNext : function()
6287     {
6288         var i = this.indexOfNav(this.getActive());
6289         if (i > this.navItems.length) {
6290             return;
6291         }
6292         this.setActiveItem(this.navItems[i+1]);
6293     },
6294     setActivePrev : function()
6295     {
6296         var i = this.indexOfNav(this.getActive());
6297         if (i  < 1) {
6298             return;
6299         }
6300         this.setActiveItem(this.navItems[i-1]);
6301     },
6302     clearWasActive : function(except) {
6303         Roo.each(this.navItems, function(e) {
6304             if (e.tabId != except.tabId && e.was_active) {
6305                e.was_active = false;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311     },
6312     getWasActive : function ()
6313     {
6314         var r = false;
6315         Roo.each(this.navItems, function(e) {
6316             if (e.was_active) {
6317                r = e;
6318                return false;
6319             }
6320             return true;
6321             
6322         });
6323         return r;
6324     }
6325     
6326     
6327 });
6328
6329  
6330 Roo.apply(Roo.bootstrap.NavGroup, {
6331     
6332     groups: {},
6333      /**
6334     * register a Navigation Group
6335     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6336     */
6337     register : function(navgrp)
6338     {
6339         this.groups[navgrp.navId] = navgrp;
6340         
6341     },
6342     /**
6343     * fetch a Navigation Group based on the navigation ID
6344     * @param {string} the navgroup to add
6345     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6346     */
6347     get: function(navId) {
6348         if (typeof(this.groups[navId]) == 'undefined') {
6349             return false;
6350             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6351         }
6352         return this.groups[navId] ;
6353     }
6354     
6355     
6356     
6357 });
6358
6359  /*
6360  * - LGPL
6361  *
6362  * row
6363  * 
6364  */
6365
6366 /**
6367  * @class Roo.bootstrap.NavItem
6368  * @extends Roo.bootstrap.Component
6369  * Bootstrap Navbar.NavItem class
6370  * @cfg {String} href  link to
6371  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6372  * @cfg {Boolean} button_outline show and outlined button
6373  * @cfg {String} html content of button
6374  * @cfg {String} badge text inside badge
6375  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6376  * @cfg {String} glyphicon DEPRICATED - use fa
6377  * @cfg {String} icon DEPRICATED - use fa
6378  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6379  * @cfg {Boolean} active Is item active
6380  * @cfg {Boolean} disabled Is item disabled
6381  * @cfg {String} linkcls  Link Class
6382  * @cfg {Boolean} preventDefault (true | false) default false
6383  * @cfg {String} tabId the tab that this item activates.
6384  * @cfg {String} tagtype (a|span) render as a href or span?
6385  * @cfg {Boolean} animateRef (true|false) link to element default false  
6386  * @cfg {Roo.bootstrap.Menu} menu a Menu 
6387   
6388  * @constructor
6389  * Create a new Navbar Item
6390  * @param {Object} config The config object
6391  */
6392 Roo.bootstrap.NavItem = function(config){
6393     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6394     this.addEvents({
6395         // raw events
6396         /**
6397          * @event click
6398          * The raw click event for the entire grid.
6399          * @param {Roo.EventObject} e
6400          */
6401         "click" : true,
6402          /**
6403             * @event changed
6404             * Fires when the active item active state changes
6405             * @param {Roo.bootstrap.NavItem} this
6406             * @param {boolean} state the new state
6407              
6408          */
6409         'changed': true,
6410         /**
6411             * @event scrollto
6412             * Fires when scroll to element
6413             * @param {Roo.bootstrap.NavItem} this
6414             * @param {Object} options
6415             * @param {Roo.EventObject} e
6416              
6417          */
6418         'scrollto': true
6419     });
6420    
6421 };
6422
6423 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6424     
6425     href: false,
6426     html: '',
6427     badge: '',
6428     icon: false,
6429     fa : false,
6430     glyphicon: false,
6431     active: false,
6432     preventDefault : false,
6433     tabId : false,
6434     tagtype : 'a',
6435     tag: 'li',
6436     disabled : false,
6437     animateRef : false,
6438     was_active : false,
6439     button_weight : '',
6440     button_outline : false,
6441     linkcls : '',
6442     navLink: false,
6443     
6444     getAutoCreate : function(){
6445          
6446         var cfg = {
6447             tag: this.tag,
6448             cls: 'nav-item'
6449         };
6450         
6451         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6452         
6453         if (this.active) {
6454             cfg.cls +=  ' active' ;
6455         }
6456         if (this.disabled) {
6457             cfg.cls += ' disabled';
6458         }
6459         
6460         // BS4 only?
6461         if (this.button_weight.length) {
6462             cfg.tag = this.href ? 'a' : 'button';
6463             cfg.html = this.html || '';
6464             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6465             if (this.href) {
6466                 cfg.href = this.href;
6467             }
6468             if (this.fa) {
6469                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6470             } else {
6471                 cfg.cls += " nav-html";
6472             }
6473             
6474             // menu .. should add dropdown-menu class - so no need for carat..
6475             
6476             if (this.badge !== '') {
6477                  
6478                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6479             }
6480             return cfg;
6481         }
6482         
6483         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6484             cfg.cn = [
6485                 {
6486                     tag: this.tagtype,
6487                     href : this.href || "#",
6488                     html: this.html || '',
6489                     cls : ''
6490                 }
6491             ];
6492             if (this.tagtype == 'a') {
6493                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6494         
6495             }
6496             if (this.icon) {
6497                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6498             } else  if (this.fa) {
6499                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6500             } else if(this.glyphicon) {
6501                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6502             } else {
6503                 cfg.cn[0].cls += " nav-html";
6504             }
6505             
6506             if (this.menu) {
6507                 cfg.cn[0].html += " <span class='caret'></span>";
6508              
6509             }
6510             
6511             if (this.badge !== '') {
6512                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6513             }
6514         }
6515         
6516         
6517         
6518         return cfg;
6519     },
6520     onRender : function(ct, position)
6521     {
6522        // Roo.log("Call onRender: " + this.xtype);
6523         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6524             this.tag = 'div';
6525         }
6526         
6527         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6528         this.navLink = this.el.select('.nav-link',true).first();
6529         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6530         return ret;
6531     },
6532       
6533     
6534     initEvents: function() 
6535     {
6536         if (typeof (this.menu) != 'undefined') {
6537             this.menu.parentType = this.xtype;
6538             this.menu.triggerEl = this.el;
6539             this.menu = this.addxtype(Roo.apply({}, this.menu));
6540         }
6541         
6542         this.el.on('click', this.onClick, this);
6543         
6544         //if(this.tagtype == 'span'){
6545         //    this.el.select('span',true).on('click', this.onClick, this);
6546         //}
6547        
6548         // at this point parent should be available..
6549         this.parent().register(this);
6550     },
6551     
6552     onClick : function(e)
6553     {
6554         if (e.getTarget('.dropdown-menu-item')) {
6555             // did you click on a menu itemm.... - then don't trigger onclick..
6556             return;
6557         }
6558         
6559         if(
6560                 this.preventDefault || 
6561                 this.href == '#' 
6562         ){
6563             Roo.log("NavItem - prevent Default?");
6564             e.preventDefault();
6565         }
6566         
6567         if (this.disabled) {
6568             return;
6569         }
6570         
6571         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6572         if (tg && tg.transition) {
6573             Roo.log("waiting for the transitionend");
6574             return;
6575         }
6576         
6577         
6578         
6579         //Roo.log("fire event clicked");
6580         if(this.fireEvent('click', this, e) === false){
6581             return;
6582         };
6583         
6584         if(this.tagtype == 'span'){
6585             return;
6586         }
6587         
6588         //Roo.log(this.href);
6589         var ael = this.el.select('a',true).first();
6590         //Roo.log(ael);
6591         
6592         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6593             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6594             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6595                 return; // ignore... - it's a 'hash' to another page.
6596             }
6597             Roo.log("NavItem - prevent Default?");
6598             e.preventDefault();
6599             this.scrollToElement(e);
6600         }
6601         
6602         
6603         var p =  this.parent();
6604    
6605         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6606             if (typeof(p.setActiveItem) !== 'undefined') {
6607                 p.setActiveItem(this);
6608             }
6609         }
6610         
6611         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6612         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6613             // remove the collapsed menu expand...
6614             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6615         }
6616     },
6617     
6618     isActive: function () {
6619         return this.active
6620     },
6621     setActive : function(state, fire, is_was_active)
6622     {
6623         if (this.active && !state && this.navId) {
6624             this.was_active = true;
6625             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6626             if (nv) {
6627                 nv.clearWasActive(this);
6628             }
6629             
6630         }
6631         this.active = state;
6632         
6633         if (!state ) {
6634             this.el.removeClass('active');
6635             this.navLink ? this.navLink.removeClass('active') : false;
6636         } else if (!this.el.hasClass('active')) {
6637             
6638             this.el.addClass('active');
6639             if (Roo.bootstrap.version == 4 && this.navLink ) {
6640                 this.navLink.addClass('active');
6641             }
6642             
6643         }
6644         if (fire) {
6645             this.fireEvent('changed', this, state);
6646         }
6647         
6648         // show a panel if it's registered and related..
6649         
6650         if (!this.navId || !this.tabId || !state || is_was_active) {
6651             return;
6652         }
6653         
6654         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6655         if (!tg) {
6656             return;
6657         }
6658         var pan = tg.getPanelByName(this.tabId);
6659         if (!pan) {
6660             return;
6661         }
6662         // if we can not flip to new panel - go back to old nav highlight..
6663         if (false == tg.showPanel(pan)) {
6664             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6665             if (nv) {
6666                 var onav = nv.getWasActive();
6667                 if (onav) {
6668                     onav.setActive(true, false, true);
6669                 }
6670             }
6671             
6672         }
6673         
6674         
6675         
6676     },
6677      // this should not be here...
6678     setDisabled : function(state)
6679     {
6680         this.disabled = state;
6681         if (!state ) {
6682             this.el.removeClass('disabled');
6683         } else if (!this.el.hasClass('disabled')) {
6684             this.el.addClass('disabled');
6685         }
6686         
6687     },
6688     
6689     /**
6690      * Fetch the element to display the tooltip on.
6691      * @return {Roo.Element} defaults to this.el
6692      */
6693     tooltipEl : function()
6694     {
6695         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6696     },
6697     
6698     scrollToElement : function(e)
6699     {
6700         var c = document.body;
6701         
6702         /*
6703          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6704          */
6705         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6706             c = document.documentElement;
6707         }
6708         
6709         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6710         
6711         if(!target){
6712             return;
6713         }
6714
6715         var o = target.calcOffsetsTo(c);
6716         
6717         var options = {
6718             target : target,
6719             value : o[1]
6720         };
6721         
6722         this.fireEvent('scrollto', this, options, e);
6723         
6724         Roo.get(c).scrollTo('top', options.value, true);
6725         
6726         return;
6727     },
6728     /**
6729      * Set the HTML (text content) of the item
6730      * @param {string} html  content for the nav item
6731      */
6732     setHtml : function(html)
6733     {
6734         this.html = html;
6735         this.htmlEl.dom.innerHTML = html;
6736         
6737     } 
6738 });
6739  
6740
6741  /*
6742  * - LGPL
6743  *
6744  * sidebar item
6745  *
6746  *  li
6747  *    <span> icon </span>
6748  *    <span> text </span>
6749  *    <span>badge </span>
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.NavSidebarItem
6754  * @extends Roo.bootstrap.NavItem
6755  * Bootstrap Navbar.NavSidebarItem class
6756  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6757  * {Boolean} open is the menu open
6758  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6759  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6760  * {String} buttonSize (sm|md|lg)the extra classes for the button
6761  * {Boolean} showArrow show arrow next to the text (default true)
6762  * @constructor
6763  * Create a new Navbar Button
6764  * @param {Object} config The config object
6765  */
6766 Roo.bootstrap.NavSidebarItem = function(config){
6767     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6768     this.addEvents({
6769         // raw events
6770         /**
6771          * @event click
6772          * The raw click event for the entire grid.
6773          * @param {Roo.EventObject} e
6774          */
6775         "click" : true,
6776          /**
6777             * @event changed
6778             * Fires when the active item active state changes
6779             * @param {Roo.bootstrap.NavSidebarItem} this
6780             * @param {boolean} state the new state
6781              
6782          */
6783         'changed': true
6784     });
6785    
6786 };
6787
6788 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6789     
6790     badgeWeight : 'default',
6791     
6792     open: false,
6793     
6794     buttonView : false,
6795     
6796     buttonWeight : 'default',
6797     
6798     buttonSize : 'md',
6799     
6800     showArrow : true,
6801     
6802     getAutoCreate : function(){
6803         
6804         
6805         var a = {
6806                 tag: 'a',
6807                 href : this.href || '#',
6808                 cls: '',
6809                 html : '',
6810                 cn : []
6811         };
6812         
6813         if(this.buttonView){
6814             a = {
6815                 tag: 'button',
6816                 href : this.href || '#',
6817                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6818                 html : this.html,
6819                 cn : []
6820             };
6821         }
6822         
6823         var cfg = {
6824             tag: 'li',
6825             cls: '',
6826             cn: [ a ]
6827         };
6828         
6829         if (this.active) {
6830             cfg.cls += ' active';
6831         }
6832         
6833         if (this.disabled) {
6834             cfg.cls += ' disabled';
6835         }
6836         if (this.open) {
6837             cfg.cls += ' open x-open';
6838         }
6839         // left icon..
6840         if (this.glyphicon || this.icon) {
6841             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6842             a.cn.push({ tag : 'i', cls : c }) ;
6843         }
6844         
6845         if(!this.buttonView){
6846             var span = {
6847                 tag: 'span',
6848                 html : this.html || ''
6849             };
6850
6851             a.cn.push(span);
6852             
6853         }
6854         
6855         if (this.badge !== '') {
6856             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6857         }
6858         
6859         if (this.menu) {
6860             
6861             if(this.showArrow){
6862                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6863             }
6864             
6865             a.cls += ' dropdown-toggle treeview' ;
6866         }
6867         
6868         return cfg;
6869     },
6870     
6871     initEvents : function()
6872     { 
6873         if (typeof (this.menu) != 'undefined') {
6874             this.menu.parentType = this.xtype;
6875             this.menu.triggerEl = this.el;
6876             this.menu = this.addxtype(Roo.apply({}, this.menu));
6877         }
6878         
6879         this.el.on('click', this.onClick, this);
6880         
6881         if(this.badge !== ''){
6882             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6883         }
6884         
6885     },
6886     
6887     onClick : function(e)
6888     {
6889         if(this.disabled){
6890             e.preventDefault();
6891             return;
6892         }
6893         
6894         if(this.preventDefault){
6895             e.preventDefault();
6896         }
6897         
6898         this.fireEvent('click', this, e);
6899     },
6900     
6901     disable : function()
6902     {
6903         this.setDisabled(true);
6904     },
6905     
6906     enable : function()
6907     {
6908         this.setDisabled(false);
6909     },
6910     
6911     setDisabled : function(state)
6912     {
6913         if(this.disabled == state){
6914             return;
6915         }
6916         
6917         this.disabled = state;
6918         
6919         if (state) {
6920             this.el.addClass('disabled');
6921             return;
6922         }
6923         
6924         this.el.removeClass('disabled');
6925         
6926         return;
6927     },
6928     
6929     setActive : function(state)
6930     {
6931         if(this.active == state){
6932             return;
6933         }
6934         
6935         this.active = state;
6936         
6937         if (state) {
6938             this.el.addClass('active');
6939             return;
6940         }
6941         
6942         this.el.removeClass('active');
6943         
6944         return;
6945     },
6946     
6947     isActive: function () 
6948     {
6949         return this.active;
6950     },
6951     
6952     setBadge : function(str)
6953     {
6954         if(!this.badgeEl){
6955             return;
6956         }
6957         
6958         this.badgeEl.dom.innerHTML = str;
6959     }
6960     
6961    
6962      
6963  
6964 });
6965  
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Nav
6971  * 
6972  */
6973 Roo.namespace('Roo.bootstrap.breadcrumb');
6974
6975
6976 /**
6977  * @class Roo.bootstrap.breadcrumb.Nav
6978  * @extends Roo.bootstrap.Component
6979  * Bootstrap Breadcrumb Nav Class
6980  *  
6981  * @children Roo.bootstrap.breadcrumb.Item
6982  * 
6983  * @constructor
6984  * Create a new breadcrumb.Nav
6985  * @param {Object} config The config object
6986  */
6987
6988
6989 Roo.bootstrap.breadcrumb.Nav = function(config){
6990     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6991     
6992     
6993 };
6994
6995 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6996     
6997     getAutoCreate : function()
6998     {
6999
7000         var cfg = {
7001             tag: 'nav',
7002             cn : [
7003                 {
7004                     tag : 'ol',
7005                     cls : 'breadcrumb'
7006                 }
7007             ]
7008             
7009         };
7010           
7011         return cfg;
7012     },
7013     
7014     initEvents: function()
7015     {
7016         this.olEl = this.el.select('ol',true).first();    
7017     },
7018     getChildContainer : function()
7019     {
7020         return this.olEl;  
7021     }
7022     
7023 });
7024
7025  /*
7026  * - LGPL
7027  *
7028  *  Breadcrumb Item
7029  * 
7030  */
7031
7032
7033 /**
7034  * @class Roo.bootstrap.breadcrumb.Nav
7035  * @extends Roo.bootstrap.Component
7036  * Bootstrap Breadcrumb Nav Class
7037  *  
7038  * @children Roo.bootstrap.breadcrumb.Component
7039  * @cfg {String} html the content of the link.
7040  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7041  * @cfg {Boolean} active is it active
7042
7043  * 
7044  * @constructor
7045  * Create a new breadcrumb.Nav
7046  * @param {Object} config The config object
7047  */
7048
7049 Roo.bootstrap.breadcrumb.Item = function(config){
7050     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7051     this.addEvents({
7052         // img events
7053         /**
7054          * @event click
7055          * The img click event for the img.
7056          * @param {Roo.EventObject} e
7057          */
7058         "click" : true
7059     });
7060     
7061 };
7062
7063 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7064     
7065     href: false,
7066     html : '',
7067     
7068     getAutoCreate : function()
7069     {
7070
7071         var cfg = {
7072             tag: 'li',
7073             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7074         };
7075         if (this.href !== false) {
7076             cfg.cn = [{
7077                 tag : 'a',
7078                 href : this.href,
7079                 html : this.html
7080             }];
7081         } else {
7082             cfg.html = this.html;
7083         }
7084         
7085         return cfg;
7086     },
7087     
7088     initEvents: function()
7089     {
7090         if (this.href) {
7091             this.el.select('a', true).first().on('click',this.onClick, this)
7092         }
7093         
7094     },
7095     onClick : function(e)
7096     {
7097         e.preventDefault();
7098         this.fireEvent('click',this,  e);
7099     }
7100     
7101 });
7102
7103  /*
7104  * - LGPL
7105  *
7106  * row
7107  * 
7108  */
7109
7110 /**
7111  * @class Roo.bootstrap.Row
7112  * @extends Roo.bootstrap.Component
7113  * @children Roo.bootstrap.Component
7114  * Bootstrap Row class (contains columns...)
7115  * 
7116  * @constructor
7117  * Create a new Row
7118  * @param {Object} config The config object
7119  */
7120
7121 Roo.bootstrap.Row = function(config){
7122     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7123 };
7124
7125 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7126     
7127     getAutoCreate : function(){
7128        return {
7129             cls: 'row clearfix'
7130        };
7131     }
7132     
7133     
7134 });
7135
7136  
7137
7138  /*
7139  * - LGPL
7140  *
7141  * pagination
7142  * 
7143  */
7144
7145 /**
7146  * @class Roo.bootstrap.Pagination
7147  * @extends Roo.bootstrap.Component
7148  * Bootstrap Pagination class
7149  * @cfg {String} size xs | sm | md | lg
7150  * @cfg {Boolean} inverse false | true
7151  * 
7152  * @constructor
7153  * Create a new Pagination
7154  * @param {Object} config The config object
7155  */
7156
7157 Roo.bootstrap.Pagination = function(config){
7158     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7159 };
7160
7161 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7162     
7163     cls: false,
7164     size: false,
7165     inverse: false,
7166     
7167     getAutoCreate : function(){
7168         var cfg = {
7169             tag: 'ul',
7170                 cls: 'pagination'
7171         };
7172         if (this.inverse) {
7173             cfg.cls += ' inverse';
7174         }
7175         if (this.html) {
7176             cfg.html=this.html;
7177         }
7178         if (this.cls) {
7179             cfg.cls += " " + this.cls;
7180         }
7181         return cfg;
7182     }
7183    
7184 });
7185
7186  
7187
7188  /*
7189  * - LGPL
7190  *
7191  * Pagination item
7192  * 
7193  */
7194
7195
7196 /**
7197  * @class Roo.bootstrap.PaginationItem
7198  * @extends Roo.bootstrap.Component
7199  * Bootstrap PaginationItem class
7200  * @cfg {String} html text
7201  * @cfg {String} href the link
7202  * @cfg {Boolean} preventDefault (true | false) default true
7203  * @cfg {Boolean} active (true | false) default false
7204  * @cfg {Boolean} disabled default false
7205  * 
7206  * 
7207  * @constructor
7208  * Create a new PaginationItem
7209  * @param {Object} config The config object
7210  */
7211
7212
7213 Roo.bootstrap.PaginationItem = function(config){
7214     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7215     this.addEvents({
7216         // raw events
7217         /**
7218          * @event click
7219          * The raw click event for the entire grid.
7220          * @param {Roo.EventObject} e
7221          */
7222         "click" : true
7223     });
7224 };
7225
7226 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7227     
7228     href : false,
7229     html : false,
7230     preventDefault: true,
7231     active : false,
7232     cls : false,
7233     disabled: false,
7234     
7235     getAutoCreate : function(){
7236         var cfg= {
7237             tag: 'li',
7238             cn: [
7239                 {
7240                     tag : 'a',
7241                     href : this.href ? this.href : '#',
7242                     html : this.html ? this.html : ''
7243                 }
7244             ]
7245         };
7246         
7247         if(this.cls){
7248             cfg.cls = this.cls;
7249         }
7250         
7251         if(this.disabled){
7252             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7253         }
7254         
7255         if(this.active){
7256             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7257         }
7258         
7259         return cfg;
7260     },
7261     
7262     initEvents: function() {
7263         
7264         this.el.on('click', this.onClick, this);
7265         
7266     },
7267     onClick : function(e)
7268     {
7269         Roo.log('PaginationItem on click ');
7270         if(this.preventDefault){
7271             e.preventDefault();
7272         }
7273         
7274         if(this.disabled){
7275             return;
7276         }
7277         
7278         this.fireEvent('click', this, e);
7279     }
7280    
7281 });
7282
7283  
7284
7285  /*
7286  * - LGPL
7287  *
7288  * slider
7289  * 
7290  */
7291
7292
7293 /**
7294  * @class Roo.bootstrap.Slider
7295  * @extends Roo.bootstrap.Component
7296  * Bootstrap Slider class
7297  *    
7298  * @constructor
7299  * Create a new Slider
7300  * @param {Object} config The config object
7301  */
7302
7303 Roo.bootstrap.Slider = function(config){
7304     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7305 };
7306
7307 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7308     
7309     getAutoCreate : function(){
7310         
7311         var cfg = {
7312             tag: 'div',
7313             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7314             cn: [
7315                 {
7316                     tag: 'a',
7317                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7318                 }
7319             ]
7320         };
7321         
7322         return cfg;
7323     }
7324    
7325 });
7326
7327  /*
7328  * Based on:
7329  * Ext JS Library 1.1.1
7330  * Copyright(c) 2006-2007, Ext JS, LLC.
7331  *
7332  * Originally Released Under LGPL - original licence link has changed is not relivant.
7333  *
7334  * Fork - LGPL
7335  * <script type="text/javascript">
7336  */
7337  /**
7338  * @extends Roo.dd.DDProxy
7339  * @class Roo.grid.SplitDragZone
7340  * Support for Column Header resizing
7341  * @constructor
7342  * @param {Object} config
7343  */
7344 // private
7345 // This is a support class used internally by the Grid components
7346 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7347     this.grid = grid;
7348     this.view = grid.getView();
7349     this.proxy = this.view.resizeProxy;
7350     Roo.grid.SplitDragZone.superclass.constructor.call(
7351         this,
7352         hd, // ID
7353         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7354         {  // CONFIG
7355             dragElId : Roo.id(this.proxy.dom),
7356             resizeFrame:false
7357         }
7358     );
7359     
7360     this.setHandleElId(Roo.id(hd));
7361     if (hd2 !== false) {
7362         this.setOuterHandleElId(Roo.id(hd2));
7363     }
7364     
7365     this.scroll = false;
7366 };
7367 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7368     fly: Roo.Element.fly,
7369
7370     b4StartDrag : function(x, y){
7371         this.view.headersDisabled = true;
7372         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7373                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7374         );
7375         this.proxy.setHeight(h);
7376         
7377         // for old system colWidth really stored the actual width?
7378         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7379         // which in reality did not work.. - it worked only for fixed sizes
7380         // for resizable we need to use actual sizes.
7381         var w = this.cm.getColumnWidth(this.cellIndex);
7382         if (!this.view.mainWrap) {
7383             // bootstrap.
7384             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7385         }
7386         
7387         
7388         
7389         // this was w-this.grid.minColumnWidth;
7390         // doesnt really make sense? - w = thie curren width or the rendered one?
7391         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7392         this.resetConstraints();
7393         this.setXConstraint(minw, 1000);
7394         this.setYConstraint(0, 0);
7395         this.minX = x - minw;
7396         this.maxX = x + 1000;
7397         this.startPos = x;
7398         if (!this.view.mainWrap) { // this is Bootstrap code..
7399             this.getDragEl().style.display='block';
7400         }
7401         
7402         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7403     },
7404
7405
7406     handleMouseDown : function(e){
7407         ev = Roo.EventObject.setEvent(e);
7408         var t = this.fly(ev.getTarget());
7409         if(t.hasClass("x-grid-split")){
7410             this.cellIndex = this.view.getCellIndex(t.dom);
7411             this.split = t.dom;
7412             this.cm = this.grid.colModel;
7413             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7414                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7415             }
7416         }
7417     },
7418
7419     endDrag : function(e){
7420         this.view.headersDisabled = false;
7421         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7422         var diff = endX - this.startPos;
7423         // 
7424         var w = this.cm.getColumnWidth(this.cellIndex);
7425         if (!this.view.mainWrap) {
7426             w = 0;
7427         }
7428         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7429     },
7430
7431     autoOffset : function(){
7432         this.setDelta(0,0);
7433     }
7434 });/*
7435  * Based on:
7436  * Ext JS Library 1.1.1
7437  * Copyright(c) 2006-2007, Ext JS, LLC.
7438  *
7439  * Originally Released Under LGPL - original licence link has changed is not relivant.
7440  *
7441  * Fork - LGPL
7442  * <script type="text/javascript">
7443  */
7444
7445 /**
7446  * @class Roo.grid.AbstractSelectionModel
7447  * @extends Roo.util.Observable
7448  * @abstract
7449  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7450  * implemented by descendant classes.  This class should not be directly instantiated.
7451  * @constructor
7452  */
7453 Roo.grid.AbstractSelectionModel = function(){
7454     this.locked = false;
7455     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7456 };
7457
7458 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7459     /** @ignore Called by the grid automatically. Do not call directly. */
7460     init : function(grid){
7461         this.grid = grid;
7462         this.initEvents();
7463     },
7464
7465     /**
7466      * Locks the selections.
7467      */
7468     lock : function(){
7469         this.locked = true;
7470     },
7471
7472     /**
7473      * Unlocks the selections.
7474      */
7475     unlock : function(){
7476         this.locked = false;
7477     },
7478
7479     /**
7480      * Returns true if the selections are locked.
7481      * @return {Boolean}
7482      */
7483     isLocked : function(){
7484         return this.locked;
7485     }
7486 });/*
7487  * Based on:
7488  * Ext JS Library 1.1.1
7489  * Copyright(c) 2006-2007, Ext JS, LLC.
7490  *
7491  * Originally Released Under LGPL - original licence link has changed is not relivant.
7492  *
7493  * Fork - LGPL
7494  * <script type="text/javascript">
7495  */
7496 /**
7497  * @extends Roo.grid.AbstractSelectionModel
7498  * @class Roo.grid.RowSelectionModel
7499  * The default SelectionModel used by {@link Roo.grid.Grid}.
7500  * It supports multiple selections and keyboard selection/navigation. 
7501  * @constructor
7502  * @param {Object} config
7503  */
7504 Roo.grid.RowSelectionModel = function(config){
7505     Roo.apply(this, config);
7506     this.selections = new Roo.util.MixedCollection(false, function(o){
7507         return o.id;
7508     });
7509
7510     this.last = false;
7511     this.lastActive = false;
7512
7513     this.addEvents({
7514         /**
7515         * @event selectionchange
7516         * Fires when the selection changes
7517         * @param {SelectionModel} this
7518         */
7519        "selectionchange" : true,
7520        /**
7521         * @event afterselectionchange
7522         * Fires after the selection changes (eg. by key press or clicking)
7523         * @param {SelectionModel} this
7524         */
7525        "afterselectionchange" : true,
7526        /**
7527         * @event beforerowselect
7528         * Fires when a row is selected being selected, return false to cancel.
7529         * @param {SelectionModel} this
7530         * @param {Number} rowIndex The selected index
7531         * @param {Boolean} keepExisting False if other selections will be cleared
7532         */
7533        "beforerowselect" : true,
7534        /**
7535         * @event rowselect
7536         * Fires when a row is selected.
7537         * @param {SelectionModel} this
7538         * @param {Number} rowIndex The selected index
7539         * @param {Roo.data.Record} r The record
7540         */
7541        "rowselect" : true,
7542        /**
7543         * @event rowdeselect
7544         * Fires when a row is deselected.
7545         * @param {SelectionModel} this
7546         * @param {Number} rowIndex The selected index
7547         */
7548         "rowdeselect" : true
7549     });
7550     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7551     this.locked = false;
7552 };
7553
7554 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7555     /**
7556      * @cfg {Boolean} singleSelect
7557      * True to allow selection of only one row at a time (defaults to false)
7558      */
7559     singleSelect : false,
7560
7561     // private
7562     initEvents : function(){
7563
7564         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7565             this.grid.on("mousedown", this.handleMouseDown, this);
7566         }else{ // allow click to work like normal
7567             this.grid.on("rowclick", this.handleDragableRowClick, this);
7568         }
7569         // bootstrap does not have a view..
7570         var view = this.grid.view ? this.grid.view : this.grid;
7571         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7572             "up" : function(e){
7573                 if(!e.shiftKey){
7574                     this.selectPrevious(e.shiftKey);
7575                 }else if(this.last !== false && this.lastActive !== false){
7576                     var last = this.last;
7577                     this.selectRange(this.last,  this.lastActive-1);
7578                     view.focusRow(this.lastActive);
7579                     if(last !== false){
7580                         this.last = last;
7581                     }
7582                 }else{
7583                     this.selectFirstRow();
7584                 }
7585                 this.fireEvent("afterselectionchange", this);
7586             },
7587             "down" : function(e){
7588                 if(!e.shiftKey){
7589                     this.selectNext(e.shiftKey);
7590                 }else if(this.last !== false && this.lastActive !== false){
7591                     var last = this.last;
7592                     this.selectRange(this.last,  this.lastActive+1);
7593                     view.focusRow(this.lastActive);
7594                     if(last !== false){
7595                         this.last = last;
7596                     }
7597                 }else{
7598                     this.selectFirstRow();
7599                 }
7600                 this.fireEvent("afterselectionchange", this);
7601             },
7602             scope: this
7603         });
7604
7605          
7606         view.on("refresh", this.onRefresh, this);
7607         view.on("rowupdated", this.onRowUpdated, this);
7608         view.on("rowremoved", this.onRemove, this);
7609     },
7610
7611     // private
7612     onRefresh : function(){
7613         var ds = this.grid.ds, i, v = this.grid.view;
7614         var s = this.selections;
7615         s.each(function(r){
7616             if((i = ds.indexOfId(r.id)) != -1){
7617                 v.onRowSelect(i);
7618                 s.add(ds.getAt(i)); // updating the selection relate data
7619             }else{
7620                 s.remove(r);
7621             }
7622         });
7623     },
7624
7625     // private
7626     onRemove : function(v, index, r){
7627         this.selections.remove(r);
7628     },
7629
7630     // private
7631     onRowUpdated : function(v, index, r){
7632         if(this.isSelected(r)){
7633             v.onRowSelect(index);
7634         }
7635     },
7636
7637     /**
7638      * Select records.
7639      * @param {Array} records The records to select
7640      * @param {Boolean} keepExisting (optional) True to keep existing selections
7641      */
7642     selectRecords : function(records, keepExisting){
7643         if(!keepExisting){
7644             this.clearSelections();
7645         }
7646         var ds = this.grid.ds;
7647         for(var i = 0, len = records.length; i < len; i++){
7648             this.selectRow(ds.indexOf(records[i]), true);
7649         }
7650     },
7651
7652     /**
7653      * Gets the number of selected rows.
7654      * @return {Number}
7655      */
7656     getCount : function(){
7657         return this.selections.length;
7658     },
7659
7660     /**
7661      * Selects the first row in the grid.
7662      */
7663     selectFirstRow : function(){
7664         this.selectRow(0);
7665     },
7666
7667     /**
7668      * Select the last row.
7669      * @param {Boolean} keepExisting (optional) True to keep existing selections
7670      */
7671     selectLastRow : function(keepExisting){
7672         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7673     },
7674
7675     /**
7676      * Selects the row immediately following the last selected row.
7677      * @param {Boolean} keepExisting (optional) True to keep existing selections
7678      */
7679     selectNext : function(keepExisting){
7680         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7681             this.selectRow(this.last+1, keepExisting);
7682             var view = this.grid.view ? this.grid.view : this.grid;
7683             view.focusRow(this.last);
7684         }
7685     },
7686
7687     /**
7688      * Selects the row that precedes the last selected row.
7689      * @param {Boolean} keepExisting (optional) True to keep existing selections
7690      */
7691     selectPrevious : function(keepExisting){
7692         if(this.last){
7693             this.selectRow(this.last-1, keepExisting);
7694             var view = this.grid.view ? this.grid.view : this.grid;
7695             view.focusRow(this.last);
7696         }
7697     },
7698
7699     /**
7700      * Returns the selected records
7701      * @return {Array} Array of selected records
7702      */
7703     getSelections : function(){
7704         return [].concat(this.selections.items);
7705     },
7706
7707     /**
7708      * Returns the first selected record.
7709      * @return {Record}
7710      */
7711     getSelected : function(){
7712         return this.selections.itemAt(0);
7713     },
7714
7715
7716     /**
7717      * Clears all selections.
7718      */
7719     clearSelections : function(fast){
7720         if(this.locked) {
7721             return;
7722         }
7723         if(fast !== true){
7724             var ds = this.grid.ds;
7725             var s = this.selections;
7726             s.each(function(r){
7727                 this.deselectRow(ds.indexOfId(r.id));
7728             }, this);
7729             s.clear();
7730         }else{
7731             this.selections.clear();
7732         }
7733         this.last = false;
7734     },
7735
7736
7737     /**
7738      * Selects all rows.
7739      */
7740     selectAll : function(){
7741         if(this.locked) {
7742             return;
7743         }
7744         this.selections.clear();
7745         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7746             this.selectRow(i, true);
7747         }
7748     },
7749
7750     /**
7751      * Returns True if there is a selection.
7752      * @return {Boolean}
7753      */
7754     hasSelection : function(){
7755         return this.selections.length > 0;
7756     },
7757
7758     /**
7759      * Returns True if the specified row is selected.
7760      * @param {Number/Record} record The record or index of the record to check
7761      * @return {Boolean}
7762      */
7763     isSelected : function(index){
7764         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7765         return (r && this.selections.key(r.id) ? true : false);
7766     },
7767
7768     /**
7769      * Returns True if the specified record id is selected.
7770      * @param {String} id The id of record to check
7771      * @return {Boolean}
7772      */
7773     isIdSelected : function(id){
7774         return (this.selections.key(id) ? true : false);
7775     },
7776
7777     // private
7778     handleMouseDown : function(e, t)
7779     {
7780         var view = this.grid.view ? this.grid.view : this.grid;
7781         var rowIndex;
7782         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7783             return;
7784         };
7785         if(e.shiftKey && this.last !== false){
7786             var last = this.last;
7787             this.selectRange(last, rowIndex, e.ctrlKey);
7788             this.last = last; // reset the last
7789             view.focusRow(rowIndex);
7790         }else{
7791             var isSelected = this.isSelected(rowIndex);
7792             if(e.button !== 0 && isSelected){
7793                 view.focusRow(rowIndex);
7794             }else if(e.ctrlKey && isSelected){
7795                 this.deselectRow(rowIndex);
7796             }else if(!isSelected){
7797                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7798                 view.focusRow(rowIndex);
7799             }
7800         }
7801         this.fireEvent("afterselectionchange", this);
7802     },
7803     // private
7804     handleDragableRowClick :  function(grid, rowIndex, e) 
7805     {
7806         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7807             this.selectRow(rowIndex, false);
7808             var view = this.grid.view ? this.grid.view : this.grid;
7809             view.focusRow(rowIndex);
7810              this.fireEvent("afterselectionchange", this);
7811         }
7812     },
7813     
7814     /**
7815      * Selects multiple rows.
7816      * @param {Array} rows Array of the indexes of the row to select
7817      * @param {Boolean} keepExisting (optional) True to keep existing selections
7818      */
7819     selectRows : function(rows, keepExisting){
7820         if(!keepExisting){
7821             this.clearSelections();
7822         }
7823         for(var i = 0, len = rows.length; i < len; i++){
7824             this.selectRow(rows[i], true);
7825         }
7826     },
7827
7828     /**
7829      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7830      * @param {Number} startRow The index of the first row in the range
7831      * @param {Number} endRow The index of the last row in the range
7832      * @param {Boolean} keepExisting (optional) True to retain existing selections
7833      */
7834     selectRange : function(startRow, endRow, keepExisting){
7835         if(this.locked) {
7836             return;
7837         }
7838         if(!keepExisting){
7839             this.clearSelections();
7840         }
7841         if(startRow <= endRow){
7842             for(var i = startRow; i <= endRow; i++){
7843                 this.selectRow(i, true);
7844             }
7845         }else{
7846             for(var i = startRow; i >= endRow; i--){
7847                 this.selectRow(i, true);
7848             }
7849         }
7850     },
7851
7852     /**
7853      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7854      * @param {Number} startRow The index of the first row in the range
7855      * @param {Number} endRow The index of the last row in the range
7856      */
7857     deselectRange : function(startRow, endRow, preventViewNotify){
7858         if(this.locked) {
7859             return;
7860         }
7861         for(var i = startRow; i <= endRow; i++){
7862             this.deselectRow(i, preventViewNotify);
7863         }
7864     },
7865
7866     /**
7867      * Selects a row.
7868      * @param {Number} row The index of the row to select
7869      * @param {Boolean} keepExisting (optional) True to keep existing selections
7870      */
7871     selectRow : function(index, keepExisting, preventViewNotify){
7872         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7873             return;
7874         }
7875         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7876             if(!keepExisting || this.singleSelect){
7877                 this.clearSelections();
7878             }
7879             var r = this.grid.ds.getAt(index);
7880             this.selections.add(r);
7881             this.last = this.lastActive = index;
7882             if(!preventViewNotify){
7883                 var view = this.grid.view ? this.grid.view : this.grid;
7884                 view.onRowSelect(index);
7885             }
7886             this.fireEvent("rowselect", this, index, r);
7887             this.fireEvent("selectionchange", this);
7888         }
7889     },
7890
7891     /**
7892      * Deselects a row.
7893      * @param {Number} row The index of the row to deselect
7894      */
7895     deselectRow : function(index, preventViewNotify){
7896         if(this.locked) {
7897             return;
7898         }
7899         if(this.last == index){
7900             this.last = false;
7901         }
7902         if(this.lastActive == index){
7903             this.lastActive = false;
7904         }
7905         var r = this.grid.ds.getAt(index);
7906         this.selections.remove(r);
7907         if(!preventViewNotify){
7908             var view = this.grid.view ? this.grid.view : this.grid;
7909             view.onRowDeselect(index);
7910         }
7911         this.fireEvent("rowdeselect", this, index);
7912         this.fireEvent("selectionchange", this);
7913     },
7914
7915     // private
7916     restoreLast : function(){
7917         if(this._last){
7918             this.last = this._last;
7919         }
7920     },
7921
7922     // private
7923     acceptsNav : function(row, col, cm){
7924         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7925     },
7926
7927     // private
7928     onEditorKey : function(field, e){
7929         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7930         if(k == e.TAB){
7931             e.stopEvent();
7932             ed.completeEdit();
7933             if(e.shiftKey){
7934                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7935             }else{
7936                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7937             }
7938         }else if(k == e.ENTER && !e.ctrlKey){
7939             e.stopEvent();
7940             ed.completeEdit();
7941             if(e.shiftKey){
7942                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7943             }else{
7944                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7945             }
7946         }else if(k == e.ESC){
7947             ed.cancelEdit();
7948         }
7949         if(newCell){
7950             g.startEditing(newCell[0], newCell[1]);
7951         }
7952     }
7953 });/*
7954  * Based on:
7955  * Ext JS Library 1.1.1
7956  * Copyright(c) 2006-2007, Ext JS, LLC.
7957  *
7958  * Originally Released Under LGPL - original licence link has changed is not relivant.
7959  *
7960  * Fork - LGPL
7961  * <script type="text/javascript">
7962  */
7963  
7964
7965 /**
7966  * @class Roo.grid.ColumnModel
7967  * @extends Roo.util.Observable
7968  * This is the default implementation of a ColumnModel used by the Grid. It defines
7969  * the columns in the grid.
7970  * <br>Usage:<br>
7971  <pre><code>
7972  var colModel = new Roo.grid.ColumnModel([
7973         {header: "Ticker", width: 60, sortable: true, locked: true},
7974         {header: "Company Name", width: 150, sortable: true},
7975         {header: "Market Cap.", width: 100, sortable: true},
7976         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7977         {header: "Employees", width: 100, sortable: true, resizable: false}
7978  ]);
7979  </code></pre>
7980  * <p>
7981  
7982  * The config options listed for this class are options which may appear in each
7983  * individual column definition.
7984  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7985  * @constructor
7986  * @param {Object} config An Array of column config objects. See this class's
7987  * config objects for details.
7988 */
7989 Roo.grid.ColumnModel = function(config){
7990         /**
7991      * The config passed into the constructor
7992      */
7993     this.config = []; //config;
7994     this.lookup = {};
7995
7996     // if no id, create one
7997     // if the column does not have a dataIndex mapping,
7998     // map it to the order it is in the config
7999     for(var i = 0, len = config.length; i < len; i++){
8000         this.addColumn(config[i]);
8001         
8002     }
8003
8004     /**
8005      * The width of columns which have no width specified (defaults to 100)
8006      * @type Number
8007      */
8008     this.defaultWidth = 100;
8009
8010     /**
8011      * Default sortable of columns which have no sortable specified (defaults to false)
8012      * @type Boolean
8013      */
8014     this.defaultSortable = false;
8015
8016     this.addEvents({
8017         /**
8018              * @event widthchange
8019              * Fires when the width of a column changes.
8020              * @param {ColumnModel} this
8021              * @param {Number} columnIndex The column index
8022              * @param {Number} newWidth The new width
8023              */
8024             "widthchange": true,
8025         /**
8026              * @event headerchange
8027              * Fires when the text of a header changes.
8028              * @param {ColumnModel} this
8029              * @param {Number} columnIndex The column index
8030              * @param {Number} newText The new header text
8031              */
8032             "headerchange": true,
8033         /**
8034              * @event hiddenchange
8035              * Fires when a column is hidden or "unhidden".
8036              * @param {ColumnModel} this
8037              * @param {Number} columnIndex The column index
8038              * @param {Boolean} hidden true if hidden, false otherwise
8039              */
8040             "hiddenchange": true,
8041             /**
8042          * @event columnmoved
8043          * Fires when a column is moved.
8044          * @param {ColumnModel} this
8045          * @param {Number} oldIndex
8046          * @param {Number} newIndex
8047          */
8048         "columnmoved" : true,
8049         /**
8050          * @event columlockchange
8051          * Fires when a column's locked state is changed
8052          * @param {ColumnModel} this
8053          * @param {Number} colIndex
8054          * @param {Boolean} locked true if locked
8055          */
8056         "columnlockchange" : true
8057     });
8058     Roo.grid.ColumnModel.superclass.constructor.call(this);
8059 };
8060 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8061     /**
8062      * @cfg {String} header The header text to display in the Grid view.
8063      */
8064         /**
8065      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8066      */
8067         /**
8068      * @cfg {String} smHeader Header at Bootsrap Small width
8069      */
8070         /**
8071      * @cfg {String} mdHeader Header at Bootsrap Medium width
8072      */
8073         /**
8074      * @cfg {String} lgHeader Header at Bootsrap Large width
8075      */
8076         /**
8077      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8078      */
8079     /**
8080      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8081      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8082      * specified, the column's index is used as an index into the Record's data Array.
8083      */
8084     /**
8085      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8086      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8087      */
8088     /**
8089      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8090      * Defaults to the value of the {@link #defaultSortable} property.
8091      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8092      */
8093     /**
8094      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8095      */
8096     /**
8097      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8098      */
8099     /**
8100      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8101      */
8102     /**
8103      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8104      */
8105     /**
8106      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8107      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8108      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8109      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8110      */
8111        /**
8112      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8113      */
8114     /**
8115      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8116      */
8117     /**
8118      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8119      */
8120     /**
8121      * @cfg {String} cursor (Optional)
8122      */
8123     /**
8124      * @cfg {String} tooltip (Optional)
8125      */
8126     /**
8127      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8128      */
8129     /**
8130      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8131      */
8132     /**
8133      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8134      */
8135     /**
8136      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8137      */
8138         /**
8139      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8140      */
8141     /**
8142      * Returns the id of the column at the specified index.
8143      * @param {Number} index The column index
8144      * @return {String} the id
8145      */
8146     getColumnId : function(index){
8147         return this.config[index].id;
8148     },
8149
8150     /**
8151      * Returns the column for a specified id.
8152      * @param {String} id The column id
8153      * @return {Object} the column
8154      */
8155     getColumnById : function(id){
8156         return this.lookup[id];
8157     },
8158
8159     
8160     /**
8161      * Returns the column Object for a specified dataIndex.
8162      * @param {String} dataIndex The column dataIndex
8163      * @return {Object|Boolean} the column or false if not found
8164      */
8165     getColumnByDataIndex: function(dataIndex){
8166         var index = this.findColumnIndex(dataIndex);
8167         return index > -1 ? this.config[index] : false;
8168     },
8169     
8170     /**
8171      * Returns the index for a specified column id.
8172      * @param {String} id The column id
8173      * @return {Number} the index, or -1 if not found
8174      */
8175     getIndexById : function(id){
8176         for(var i = 0, len = this.config.length; i < len; i++){
8177             if(this.config[i].id == id){
8178                 return i;
8179             }
8180         }
8181         return -1;
8182     },
8183     
8184     /**
8185      * Returns the index for a specified column dataIndex.
8186      * @param {String} dataIndex The column dataIndex
8187      * @return {Number} the index, or -1 if not found
8188      */
8189     
8190     findColumnIndex : function(dataIndex){
8191         for(var i = 0, len = this.config.length; i < len; i++){
8192             if(this.config[i].dataIndex == dataIndex){
8193                 return i;
8194             }
8195         }
8196         return -1;
8197     },
8198     
8199     
8200     moveColumn : function(oldIndex, newIndex){
8201         var c = this.config[oldIndex];
8202         this.config.splice(oldIndex, 1);
8203         this.config.splice(newIndex, 0, c);
8204         this.dataMap = null;
8205         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8206     },
8207
8208     isLocked : function(colIndex){
8209         return this.config[colIndex].locked === true;
8210     },
8211
8212     setLocked : function(colIndex, value, suppressEvent){
8213         if(this.isLocked(colIndex) == value){
8214             return;
8215         }
8216         this.config[colIndex].locked = value;
8217         if(!suppressEvent){
8218             this.fireEvent("columnlockchange", this, colIndex, value);
8219         }
8220     },
8221
8222     getTotalLockedWidth : function(){
8223         var totalWidth = 0;
8224         for(var i = 0; i < this.config.length; i++){
8225             if(this.isLocked(i) && !this.isHidden(i)){
8226                 this.totalWidth += this.getColumnWidth(i);
8227             }
8228         }
8229         return totalWidth;
8230     },
8231
8232     getLockedCount : function(){
8233         for(var i = 0, len = this.config.length; i < len; i++){
8234             if(!this.isLocked(i)){
8235                 return i;
8236             }
8237         }
8238         
8239         return this.config.length;
8240     },
8241
8242     /**
8243      * Returns the number of columns.
8244      * @return {Number}
8245      */
8246     getColumnCount : function(visibleOnly){
8247         if(visibleOnly === true){
8248             var c = 0;
8249             for(var i = 0, len = this.config.length; i < len; i++){
8250                 if(!this.isHidden(i)){
8251                     c++;
8252                 }
8253             }
8254             return c;
8255         }
8256         return this.config.length;
8257     },
8258
8259     /**
8260      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8261      * @param {Function} fn
8262      * @param {Object} scope (optional)
8263      * @return {Array} result
8264      */
8265     getColumnsBy : function(fn, scope){
8266         var r = [];
8267         for(var i = 0, len = this.config.length; i < len; i++){
8268             var c = this.config[i];
8269             if(fn.call(scope||this, c, i) === true){
8270                 r[r.length] = c;
8271             }
8272         }
8273         return r;
8274     },
8275
8276     /**
8277      * Returns true if the specified column is sortable.
8278      * @param {Number} col The column index
8279      * @return {Boolean}
8280      */
8281     isSortable : function(col){
8282         if(typeof this.config[col].sortable == "undefined"){
8283             return this.defaultSortable;
8284         }
8285         return this.config[col].sortable;
8286     },
8287
8288     /**
8289      * Returns the rendering (formatting) function defined for the column.
8290      * @param {Number} col The column index.
8291      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8292      */
8293     getRenderer : function(col){
8294         if(!this.config[col].renderer){
8295             return Roo.grid.ColumnModel.defaultRenderer;
8296         }
8297         return this.config[col].renderer;
8298     },
8299
8300     /**
8301      * Sets the rendering (formatting) function for a column.
8302      * @param {Number} col The column index
8303      * @param {Function} fn The function to use to process the cell's raw data
8304      * to return HTML markup for the grid view. The render function is called with
8305      * the following parameters:<ul>
8306      * <li>Data value.</li>
8307      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8308      * <li>css A CSS style string to apply to the table cell.</li>
8309      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8310      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8311      * <li>Row index</li>
8312      * <li>Column index</li>
8313      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8314      */
8315     setRenderer : function(col, fn){
8316         this.config[col].renderer = fn;
8317     },
8318
8319     /**
8320      * Returns the width for the specified column.
8321      * @param {Number} col The column index
8322      * @param (optional) {String} gridSize bootstrap width size.
8323      * @return {Number}
8324      */
8325     getColumnWidth : function(col, gridSize)
8326         {
8327                 var cfg = this.config[col];
8328                 
8329                 if (typeof(gridSize) == 'undefined') {
8330                         return cfg.width * 1 || this.defaultWidth;
8331                 }
8332                 if (gridSize === false) { // if we set it..
8333                         return cfg.width || false;
8334                 }
8335                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8336                 
8337                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8338                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8339                                 continue;
8340                         }
8341                         return cfg[ sizes[i] ];
8342                 }
8343                 return 1;
8344                 
8345     },
8346
8347     /**
8348      * Sets the width for a column.
8349      * @param {Number} col The column index
8350      * @param {Number} width The new width
8351      */
8352     setColumnWidth : function(col, width, suppressEvent){
8353         this.config[col].width = width;
8354         this.totalWidth = null;
8355         if(!suppressEvent){
8356              this.fireEvent("widthchange", this, col, width);
8357         }
8358     },
8359
8360     /**
8361      * Returns the total width of all columns.
8362      * @param {Boolean} includeHidden True to include hidden column widths
8363      * @return {Number}
8364      */
8365     getTotalWidth : function(includeHidden){
8366         if(!this.totalWidth){
8367             this.totalWidth = 0;
8368             for(var i = 0, len = this.config.length; i < len; i++){
8369                 if(includeHidden || !this.isHidden(i)){
8370                     this.totalWidth += this.getColumnWidth(i);
8371                 }
8372             }
8373         }
8374         return this.totalWidth;
8375     },
8376
8377     /**
8378      * Returns the header for the specified column.
8379      * @param {Number} col The column index
8380      * @return {String}
8381      */
8382     getColumnHeader : function(col){
8383         return this.config[col].header;
8384     },
8385
8386     /**
8387      * Sets the header for a column.
8388      * @param {Number} col The column index
8389      * @param {String} header The new header
8390      */
8391     setColumnHeader : function(col, header){
8392         this.config[col].header = header;
8393         this.fireEvent("headerchange", this, col, header);
8394     },
8395
8396     /**
8397      * Returns the tooltip for the specified column.
8398      * @param {Number} col The column index
8399      * @return {String}
8400      */
8401     getColumnTooltip : function(col){
8402             return this.config[col].tooltip;
8403     },
8404     /**
8405      * Sets the tooltip for a column.
8406      * @param {Number} col The column index
8407      * @param {String} tooltip The new tooltip
8408      */
8409     setColumnTooltip : function(col, tooltip){
8410             this.config[col].tooltip = tooltip;
8411     },
8412
8413     /**
8414      * Returns the dataIndex for the specified column.
8415      * @param {Number} col The column index
8416      * @return {Number}
8417      */
8418     getDataIndex : function(col){
8419         return this.config[col].dataIndex;
8420     },
8421
8422     /**
8423      * Sets the dataIndex for a column.
8424      * @param {Number} col The column index
8425      * @param {Number} dataIndex The new dataIndex
8426      */
8427     setDataIndex : function(col, dataIndex){
8428         this.config[col].dataIndex = dataIndex;
8429     },
8430
8431     
8432     
8433     /**
8434      * Returns true if the cell is editable.
8435      * @param {Number} colIndex The column index
8436      * @param {Number} rowIndex The row index - this is nto actually used..?
8437      * @return {Boolean}
8438      */
8439     isCellEditable : function(colIndex, rowIndex){
8440         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8441     },
8442
8443     /**
8444      * Returns the editor defined for the cell/column.
8445      * return false or null to disable editing.
8446      * @param {Number} colIndex The column index
8447      * @param {Number} rowIndex The row index
8448      * @return {Object}
8449      */
8450     getCellEditor : function(colIndex, rowIndex){
8451         return this.config[colIndex].editor;
8452     },
8453
8454     /**
8455      * Sets if a column is editable.
8456      * @param {Number} col The column index
8457      * @param {Boolean} editable True if the column is editable
8458      */
8459     setEditable : function(col, editable){
8460         this.config[col].editable = editable;
8461     },
8462
8463
8464     /**
8465      * Returns true if the column is hidden.
8466      * @param {Number} colIndex The column index
8467      * @return {Boolean}
8468      */
8469     isHidden : function(colIndex){
8470         return this.config[colIndex].hidden;
8471     },
8472
8473
8474     /**
8475      * Returns true if the column width cannot be changed
8476      */
8477     isFixed : function(colIndex){
8478         return this.config[colIndex].fixed;
8479     },
8480
8481     /**
8482      * Returns true if the column can be resized
8483      * @return {Boolean}
8484      */
8485     isResizable : function(colIndex){
8486         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8487     },
8488     /**
8489      * Sets if a column is hidden.
8490      * @param {Number} colIndex The column index
8491      * @param {Boolean} hidden True if the column is hidden
8492      */
8493     setHidden : function(colIndex, hidden){
8494         this.config[colIndex].hidden = hidden;
8495         this.totalWidth = null;
8496         this.fireEvent("hiddenchange", this, colIndex, hidden);
8497     },
8498
8499     /**
8500      * Sets the editor for a column.
8501      * @param {Number} col The column index
8502      * @param {Object} editor The editor object
8503      */
8504     setEditor : function(col, editor){
8505         this.config[col].editor = editor;
8506     },
8507     /**
8508      * Add a column (experimental...) - defaults to adding to the end..
8509      * @param {Object} config 
8510     */
8511     addColumn : function(c)
8512     {
8513     
8514         var i = this.config.length;
8515         this.config[i] = c;
8516         
8517         if(typeof c.dataIndex == "undefined"){
8518             c.dataIndex = i;
8519         }
8520         if(typeof c.renderer == "string"){
8521             c.renderer = Roo.util.Format[c.renderer];
8522         }
8523         if(typeof c.id == "undefined"){
8524             c.id = Roo.id();
8525         }
8526         if(c.editor && c.editor.xtype){
8527             c.editor  = Roo.factory(c.editor, Roo.grid);
8528         }
8529         if(c.editor && c.editor.isFormField){
8530             c.editor = new Roo.grid.GridEditor(c.editor);
8531         }
8532         this.lookup[c.id] = c;
8533     }
8534     
8535 });
8536
8537 Roo.grid.ColumnModel.defaultRenderer = function(value)
8538 {
8539     if(typeof value == "object") {
8540         return value;
8541     }
8542         if(typeof value == "string" && value.length < 1){
8543             return "&#160;";
8544         }
8545     
8546         return String.format("{0}", value);
8547 };
8548
8549 // Alias for backwards compatibility
8550 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8551 /*
8552  * Based on:
8553  * Ext JS Library 1.1.1
8554  * Copyright(c) 2006-2007, Ext JS, LLC.
8555  *
8556  * Originally Released Under LGPL - original licence link has changed is not relivant.
8557  *
8558  * Fork - LGPL
8559  * <script type="text/javascript">
8560  */
8561  
8562 /**
8563  * @class Roo.LoadMask
8564  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8565  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8566  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8567  * element's UpdateManager load indicator and will be destroyed after the initial load.
8568  * @constructor
8569  * Create a new LoadMask
8570  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8571  * @param {Object} config The config object
8572  */
8573 Roo.LoadMask = function(el, config){
8574     this.el = Roo.get(el);
8575     Roo.apply(this, config);
8576     if(this.store){
8577         this.store.on('beforeload', this.onBeforeLoad, this);
8578         this.store.on('load', this.onLoad, this);
8579         this.store.on('loadexception', this.onLoadException, this);
8580         this.removeMask = false;
8581     }else{
8582         var um = this.el.getUpdateManager();
8583         um.showLoadIndicator = false; // disable the default indicator
8584         um.on('beforeupdate', this.onBeforeLoad, this);
8585         um.on('update', this.onLoad, this);
8586         um.on('failure', this.onLoad, this);
8587         this.removeMask = true;
8588     }
8589 };
8590
8591 Roo.LoadMask.prototype = {
8592     /**
8593      * @cfg {Boolean} removeMask
8594      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8595      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8596      */
8597     removeMask : false,
8598     /**
8599      * @cfg {String} msg
8600      * The text to display in a centered loading message box (defaults to 'Loading...')
8601      */
8602     msg : 'Loading...',
8603     /**
8604      * @cfg {String} msgCls
8605      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8606      */
8607     msgCls : 'x-mask-loading',
8608
8609     /**
8610      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8611      * @type Boolean
8612      */
8613     disabled: false,
8614
8615     /**
8616      * Disables the mask to prevent it from being displayed
8617      */
8618     disable : function(){
8619        this.disabled = true;
8620     },
8621
8622     /**
8623      * Enables the mask so that it can be displayed
8624      */
8625     enable : function(){
8626         this.disabled = false;
8627     },
8628     
8629     onLoadException : function()
8630     {
8631         Roo.log(arguments);
8632         
8633         if (typeof(arguments[3]) != 'undefined') {
8634             Roo.MessageBox.alert("Error loading",arguments[3]);
8635         } 
8636         /*
8637         try {
8638             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8639                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8640             }   
8641         } catch(e) {
8642             
8643         }
8644         */
8645     
8646         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8647     },
8648     // private
8649     onLoad : function()
8650     {
8651         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8652     },
8653
8654     // private
8655     onBeforeLoad : function(){
8656         if(!this.disabled){
8657             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8658         }
8659     },
8660
8661     // private
8662     destroy : function(){
8663         if(this.store){
8664             this.store.un('beforeload', this.onBeforeLoad, this);
8665             this.store.un('load', this.onLoad, this);
8666             this.store.un('loadexception', this.onLoadException, this);
8667         }else{
8668             var um = this.el.getUpdateManager();
8669             um.un('beforeupdate', this.onBeforeLoad, this);
8670             um.un('update', this.onLoad, this);
8671             um.un('failure', this.onLoad, this);
8672         }
8673     }
8674 };/**
8675  * @class Roo.bootstrap.Table
8676  * @licence LGBL
8677  * @extends Roo.bootstrap.Component
8678  * @children Roo.bootstrap.TableBody
8679  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8680  * Similar to Roo.grid.Grid
8681  * <pre><code>
8682  var table = Roo.factory({
8683     xtype : 'Table',
8684     xns : Roo.bootstrap,
8685     autoSizeColumns: true,
8686     
8687     
8688     store : {
8689         xtype : 'Store',
8690         xns : Roo.data,
8691         remoteSort : true,
8692         sortInfo : { direction : 'ASC', field: 'name' },
8693         proxy : {
8694            xtype : 'HttpProxy',
8695            xns : Roo.data,
8696            method : 'GET',
8697            url : 'https://example.com/some.data.url.json'
8698         },
8699         reader : {
8700            xtype : 'JsonReader',
8701            xns : Roo.data,
8702            fields : [ 'id', 'name', whatever' ],
8703            id : 'id',
8704            root : 'data'
8705         }
8706     },
8707     cm : [
8708         {
8709             xtype : 'ColumnModel',
8710             xns : Roo.grid,
8711             align : 'center',
8712             cursor : 'pointer',
8713             dataIndex : 'is_in_group',
8714             header : "Name",
8715             sortable : true,
8716             renderer : function(v, x , r) {  
8717             
8718                 return String.format("{0}", v)
8719             }
8720             width : 3
8721         } // more columns..
8722     ],
8723     selModel : {
8724         xtype : 'RowSelectionModel',
8725         xns : Roo.bootstrap.Table
8726         // you can add listeners to catch selection change here....
8727     }
8728      
8729
8730  });
8731  // set any options
8732  grid.render(Roo.get("some-div"));
8733 </code></pre>
8734
8735 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8736
8737
8738
8739  *
8740  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8741  * @cfg {Roo.data.Store} store The data store to use
8742  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
8743  * 
8744  * @cfg {String} cls table class
8745  *
8746  * 
8747  * @cfg {boolean} striped Should the rows be alternative striped
8748  * @cfg {boolean} bordered Add borders to the table
8749  * @cfg {boolean} hover Add hover highlighting
8750  * @cfg {boolean} condensed Format condensed
8751  * @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,
8752  *                also adds table-responsive (see bootstrap docs for details)
8753  * @cfg {Boolean} loadMask (true|false) default false
8754  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8755  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8756  * @cfg {Boolean} rowSelection (true|false) default false
8757  * @cfg {Boolean} cellSelection (true|false) default false
8758  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8759  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8760  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8761  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8762  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8763  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8764  * 
8765  * @constructor
8766  * Create a new Table
8767  * @param {Object} config The config object
8768  */
8769
8770 Roo.bootstrap.Table = function(config)
8771 {
8772     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8773      
8774     // BC...
8775     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8776     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8777     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8778     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8779     
8780     this.view = this; // compat with grid.
8781     
8782     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8783     if (this.sm) {
8784         this.sm.grid = this;
8785         this.selModel = Roo.factory(this.sm, Roo.grid);
8786         this.sm = this.selModel;
8787         this.sm.xmodule = this.xmodule || false;
8788     }
8789     
8790     if (this.cm && typeof(this.cm.config) == 'undefined') {
8791         this.colModel = new Roo.grid.ColumnModel(this.cm);
8792         this.cm = this.colModel;
8793         this.cm.xmodule = this.xmodule || false;
8794     }
8795     if (this.store) {
8796         this.store= Roo.factory(this.store, Roo.data);
8797         this.ds = this.store;
8798         this.ds.xmodule = this.xmodule || false;
8799          
8800     }
8801     if (this.footer && this.store) {
8802         this.footer.dataSource = this.ds;
8803         this.footer = Roo.factory(this.footer);
8804     }
8805     
8806     /** @private */
8807     this.addEvents({
8808         /**
8809          * @event cellclick
8810          * Fires when a cell is clicked
8811          * @param {Roo.bootstrap.Table} this
8812          * @param {Roo.Element} el
8813          * @param {Number} rowIndex
8814          * @param {Number} columnIndex
8815          * @param {Roo.EventObject} e
8816          */
8817         "cellclick" : true,
8818         /**
8819          * @event celldblclick
8820          * Fires when a cell is double clicked
8821          * @param {Roo.bootstrap.Table} this
8822          * @param {Roo.Element} el
8823          * @param {Number} rowIndex
8824          * @param {Number} columnIndex
8825          * @param {Roo.EventObject} e
8826          */
8827         "celldblclick" : true,
8828         /**
8829          * @event rowclick
8830          * Fires when a row is clicked
8831          * @param {Roo.bootstrap.Table} this
8832          * @param {Roo.Element} el
8833          * @param {Number} rowIndex
8834          * @param {Roo.EventObject} e
8835          */
8836         "rowclick" : true,
8837         /**
8838          * @event rowdblclick
8839          * Fires when a row is double clicked
8840          * @param {Roo.bootstrap.Table} this
8841          * @param {Roo.Element} el
8842          * @param {Number} rowIndex
8843          * @param {Roo.EventObject} e
8844          */
8845         "rowdblclick" : true,
8846         /**
8847          * @event mouseover
8848          * Fires when a mouseover occur
8849          * @param {Roo.bootstrap.Table} this
8850          * @param {Roo.Element} el
8851          * @param {Number} rowIndex
8852          * @param {Number} columnIndex
8853          * @param {Roo.EventObject} e
8854          */
8855         "mouseover" : true,
8856         /**
8857          * @event mouseout
8858          * Fires when a mouseout occur
8859          * @param {Roo.bootstrap.Table} this
8860          * @param {Roo.Element} el
8861          * @param {Number} rowIndex
8862          * @param {Number} columnIndex
8863          * @param {Roo.EventObject} e
8864          */
8865         "mouseout" : true,
8866         /**
8867          * @event rowclass
8868          * Fires when a row is rendered, so you can change add a style to it.
8869          * @param {Roo.bootstrap.Table} this
8870          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8871          */
8872         'rowclass' : true,
8873           /**
8874          * @event rowsrendered
8875          * Fires when all the  rows have been rendered
8876          * @param {Roo.bootstrap.Table} this
8877          */
8878         'rowsrendered' : true,
8879         /**
8880          * @event contextmenu
8881          * The raw contextmenu event for the entire grid.
8882          * @param {Roo.EventObject} e
8883          */
8884         "contextmenu" : true,
8885         /**
8886          * @event rowcontextmenu
8887          * Fires when a row is right clicked
8888          * @param {Roo.bootstrap.Table} this
8889          * @param {Number} rowIndex
8890          * @param {Roo.EventObject} e
8891          */
8892         "rowcontextmenu" : true,
8893         /**
8894          * @event cellcontextmenu
8895          * Fires when a cell is right clicked
8896          * @param {Roo.bootstrap.Table} this
8897          * @param {Number} rowIndex
8898          * @param {Number} cellIndex
8899          * @param {Roo.EventObject} e
8900          */
8901          "cellcontextmenu" : true,
8902          /**
8903          * @event headercontextmenu
8904          * Fires when a header is right clicked
8905          * @param {Roo.bootstrap.Table} this
8906          * @param {Number} columnIndex
8907          * @param {Roo.EventObject} e
8908          */
8909         "headercontextmenu" : true,
8910         /**
8911          * @event mousedown
8912          * The raw mousedown event for the entire grid.
8913          * @param {Roo.EventObject} e
8914          */
8915         "mousedown" : true
8916         
8917     });
8918 };
8919
8920 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8921     
8922     cls: false,
8923     
8924     striped : false,
8925     scrollBody : false,
8926     bordered: false,
8927     hover:  false,
8928     condensed : false,
8929     responsive : false,
8930     sm : false,
8931     cm : false,
8932     store : false,
8933     loadMask : false,
8934     footerShow : true,
8935     headerShow : true,
8936     enableColumnResize: true,
8937   
8938     rowSelection : false,
8939     cellSelection : false,
8940     layout : false,
8941
8942     minColumnWidth : 50,
8943     
8944     // Roo.Element - the tbody
8945     bodyEl: false,  // <tbody> Roo.Element - thead element    
8946     headEl: false,  // <thead> Roo.Element - thead element
8947     resizeProxy : false, // proxy element for dragging?
8948
8949
8950     
8951     container: false, // used by gridpanel...
8952     
8953     lazyLoad : false,
8954     
8955     CSS : Roo.util.CSS,
8956     
8957     auto_hide_footer : false,
8958     
8959     view: false, // actually points to this..
8960     
8961     getAutoCreate : function()
8962     {
8963         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8964         
8965         cfg = {
8966             tag: 'table',
8967             cls : 'table', 
8968             cn : []
8969         };
8970         // this get's auto added by panel.Grid
8971         if (this.scrollBody) {
8972             cfg.cls += ' table-body-fixed';
8973         }    
8974         if (this.striped) {
8975             cfg.cls += ' table-striped';
8976         }
8977         
8978         if (this.hover) {
8979             cfg.cls += ' table-hover';
8980         }
8981         if (this.bordered) {
8982             cfg.cls += ' table-bordered';
8983         }
8984         if (this.condensed) {
8985             cfg.cls += ' table-condensed';
8986         }
8987         
8988         if (this.responsive) {
8989             cfg.cls += ' table-responsive';
8990         }
8991         
8992         if (this.cls) {
8993             cfg.cls+=  ' ' +this.cls;
8994         }
8995         
8996         
8997         
8998         if (this.layout) {
8999             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9000         }
9001         
9002         if(this.store || this.cm){
9003             if(this.headerShow){
9004                 cfg.cn.push(this.renderHeader());
9005             }
9006             
9007             cfg.cn.push(this.renderBody());
9008             
9009             if(this.footerShow){
9010                 cfg.cn.push(this.renderFooter());
9011             }
9012             // where does this come from?
9013             //cfg.cls+=  ' TableGrid';
9014         }
9015         
9016         return { cn : [ cfg ] };
9017     },
9018     
9019     initEvents : function()
9020     {   
9021         if(!this.store || !this.cm){
9022             return;
9023         }
9024         if (this.selModel) {
9025             this.selModel.initEvents();
9026         }
9027         
9028         
9029         //Roo.log('initEvents with ds!!!!');
9030         
9031         this.bodyEl = this.el.select('tbody', true).first();
9032         this.headEl = this.el.select('thead', true).first();
9033         this.mainFoot = this.el.select('tfoot', true).first();
9034         
9035         
9036         
9037         
9038         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9039             e.on('click', this.sort, this);
9040         }, this);
9041         
9042         
9043         // why is this done????? = it breaks dialogs??
9044         //this.parent().el.setStyle('position', 'relative');
9045         
9046         
9047         if (this.footer) {
9048             this.footer.parentId = this.id;
9049             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9050             
9051             if(this.lazyLoad){
9052                 this.el.select('tfoot tr td').first().addClass('hide');
9053             }
9054         } 
9055         
9056         if(this.loadMask) {
9057             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9058         }
9059         
9060         this.store.on('load', this.onLoad, this);
9061         this.store.on('beforeload', this.onBeforeLoad, this);
9062         this.store.on('update', this.onUpdate, this);
9063         this.store.on('add', this.onAdd, this);
9064         this.store.on("clear", this.clear, this);
9065         
9066         this.el.on("contextmenu", this.onContextMenu, this);
9067         
9068         
9069         this.cm.on("headerchange", this.onHeaderChange, this);
9070         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9071
9072  //?? does bodyEl get replaced on render?
9073         this.bodyEl.on("click", this.onClick, this);
9074         this.bodyEl.on("dblclick", this.onDblClick, this);        
9075         this.bodyEl.on('scroll', this.onBodyScroll, this);
9076
9077         // guessing mainbody will work - this relays usually caught by selmodel at present.
9078         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9079   
9080   
9081         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9082         
9083   
9084         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9085             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9086         }
9087         
9088         this.initCSS();
9089     },
9090     // Compatibility with grid - we implement all the view features at present.
9091     getView : function()
9092     {
9093         return this;
9094     },
9095     
9096     initCSS : function()
9097     {
9098         
9099         
9100         var cm = this.cm, styles = [];
9101         this.CSS.removeStyleSheet(this.id + '-cssrules');
9102         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9103         // we can honour xs/sm/md/xl  as widths...
9104         // we first have to decide what widht we are currently at...
9105         var sz = Roo.getGridSize();
9106         
9107         var total = 0;
9108         var last = -1;
9109         var cols = []; // visable cols.
9110         var total_abs = 0;
9111         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9112             var w = cm.getColumnWidth(i, false);
9113             if(cm.isHidden(i)){
9114                 cols.push( { rel : false, abs : 0 });
9115                 continue;
9116             }
9117             if (w !== false) {
9118                 cols.push( { rel : false, abs : w });
9119                 total_abs += w;
9120                 last = i; // not really..
9121                 continue;
9122             }
9123             var w = cm.getColumnWidth(i, sz);
9124             if (w > 0) {
9125                 last = i
9126             }
9127             total += w;
9128             cols.push( { rel : w, abs : false });
9129         }
9130         
9131         var avail = this.bodyEl.dom.clientWidth - total_abs;
9132         
9133         var unitWidth = Math.floor(avail / total);
9134         var rem = avail - (unitWidth * total);
9135         
9136         var hidden, width, pos = 0 , splithide , left;
9137         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9138             
9139             hidden = 'display:none;';
9140             left = '';
9141             width  = 'width:0px;';
9142             splithide = '';
9143             if(!cm.isHidden(i)){
9144                 hidden = '';
9145                 
9146                 
9147                 // we can honour xs/sm/md/xl ?
9148                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9149                 if (w===0) {
9150                     hidden = 'display:none;';
9151                 }
9152                 // width should return a small number...
9153                 if (i == last) {
9154                     w+=rem; // add the remaining with..
9155                 }
9156                 pos += w;
9157                 left = "left:" + (pos -4) + "px;";
9158                 width = "width:" + w+ "px;";
9159                 
9160             }
9161             if (this.responsive) {
9162                 width = '';
9163                 left = '';
9164                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9165                 splithide = 'display: none;';
9166             }
9167             
9168             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9169             if (this.headEl) {
9170                 if (i == last) {
9171                     splithide = 'display:none;';
9172                 }
9173                 
9174                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9175                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9176                 );
9177             }
9178             
9179         }
9180         //Roo.log(styles.join(''));
9181         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9182         
9183     },
9184     
9185     
9186     
9187     onContextMenu : function(e, t)
9188     {
9189         this.processEvent("contextmenu", e);
9190     },
9191     
9192     processEvent : function(name, e)
9193     {
9194         if (name != 'touchstart' ) {
9195             this.fireEvent(name, e);    
9196         }
9197         
9198         var t = e.getTarget();
9199         
9200         var cell = Roo.get(t);
9201         
9202         if(!cell){
9203             return;
9204         }
9205         
9206         if(cell.findParent('tfoot', false, true)){
9207             return;
9208         }
9209         
9210         if(cell.findParent('thead', false, true)){
9211             
9212             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9213                 cell = Roo.get(t).findParent('th', false, true);
9214                 if (!cell) {
9215                     Roo.log("failed to find th in thead?");
9216                     Roo.log(e.getTarget());
9217                     return;
9218                 }
9219             }
9220             
9221             var cellIndex = cell.dom.cellIndex;
9222             
9223             var ename = name == 'touchstart' ? 'click' : name;
9224             this.fireEvent("header" + ename, this, cellIndex, e);
9225             
9226             return;
9227         }
9228         
9229         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9230             cell = Roo.get(t).findParent('td', false, true);
9231             if (!cell) {
9232                 Roo.log("failed to find th in tbody?");
9233                 Roo.log(e.getTarget());
9234                 return;
9235             }
9236         }
9237         
9238         var row = cell.findParent('tr', false, true);
9239         var cellIndex = cell.dom.cellIndex;
9240         var rowIndex = row.dom.rowIndex - 1;
9241         
9242         if(row !== false){
9243             
9244             this.fireEvent("row" + name, this, rowIndex, e);
9245             
9246             if(cell !== false){
9247             
9248                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9249             }
9250         }
9251         
9252     },
9253     
9254     onMouseover : function(e, el)
9255     {
9256         var cell = Roo.get(el);
9257         
9258         if(!cell){
9259             return;
9260         }
9261         
9262         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9263             cell = cell.findParent('td', false, true);
9264         }
9265         
9266         var row = cell.findParent('tr', false, true);
9267         var cellIndex = cell.dom.cellIndex;
9268         var rowIndex = row.dom.rowIndex - 1; // start from 0
9269         
9270         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9271         
9272     },
9273     
9274     onMouseout : function(e, el)
9275     {
9276         var cell = Roo.get(el);
9277         
9278         if(!cell){
9279             return;
9280         }
9281         
9282         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9283             cell = cell.findParent('td', false, true);
9284         }
9285         
9286         var row = cell.findParent('tr', false, true);
9287         var cellIndex = cell.dom.cellIndex;
9288         var rowIndex = row.dom.rowIndex - 1; // start from 0
9289         
9290         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9291         
9292     },
9293     
9294     onClick : function(e, el)
9295     {
9296         var cell = Roo.get(el);
9297         
9298         if(!cell || (!this.cellSelection && !this.rowSelection)){
9299             return;
9300         }
9301         
9302         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9303             cell = cell.findParent('td', false, true);
9304         }
9305         
9306         if(!cell || typeof(cell) == 'undefined'){
9307             return;
9308         }
9309         
9310         var row = cell.findParent('tr', false, true);
9311         
9312         if(!row || typeof(row) == 'undefined'){
9313             return;
9314         }
9315         
9316         var cellIndex = cell.dom.cellIndex;
9317         var rowIndex = this.getRowIndex(row);
9318         
9319         // why??? - should these not be based on SelectionModel?
9320         //if(this.cellSelection){
9321             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9322         //}
9323         
9324         //if(this.rowSelection){
9325             this.fireEvent('rowclick', this, row, rowIndex, e);
9326         //}
9327          
9328     },
9329         
9330     onDblClick : function(e,el)
9331     {
9332         var cell = Roo.get(el);
9333         
9334         if(!cell || (!this.cellSelection && !this.rowSelection)){
9335             return;
9336         }
9337         
9338         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9339             cell = cell.findParent('td', false, true);
9340         }
9341         
9342         if(!cell || typeof(cell) == 'undefined'){
9343             return;
9344         }
9345         
9346         var row = cell.findParent('tr', false, true);
9347         
9348         if(!row || typeof(row) == 'undefined'){
9349             return;
9350         }
9351         
9352         var cellIndex = cell.dom.cellIndex;
9353         var rowIndex = this.getRowIndex(row);
9354         
9355         if(this.cellSelection){
9356             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9357         }
9358         
9359         if(this.rowSelection){
9360             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9361         }
9362     },
9363     findRowIndex : function(el)
9364     {
9365         var cell = Roo.get(el);
9366         if(!cell) {
9367             return false;
9368         }
9369         var row = cell.findParent('tr', false, true);
9370         
9371         if(!row || typeof(row) == 'undefined'){
9372             return false;
9373         }
9374         return this.getRowIndex(row);
9375     },
9376     sort : function(e,el)
9377     {
9378         var col = Roo.get(el);
9379         
9380         if(!col.hasClass('sortable')){
9381             return;
9382         }
9383         
9384         var sort = col.attr('sort');
9385         var dir = 'ASC';
9386         
9387         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9388             dir = 'DESC';
9389         }
9390         
9391         this.store.sortInfo = {field : sort, direction : dir};
9392         
9393         if (this.footer) {
9394             Roo.log("calling footer first");
9395             this.footer.onClick('first');
9396         } else {
9397         
9398             this.store.load({ params : { start : 0 } });
9399         }
9400     },
9401     
9402     renderHeader : function()
9403     {
9404         var header = {
9405             tag: 'thead',
9406             cn : []
9407         };
9408         
9409         var cm = this.cm;
9410         this.totalWidth = 0;
9411         
9412         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9413             
9414             var config = cm.config[i];
9415             
9416             var c = {
9417                 tag: 'th',
9418                 cls : 'x-hcol-' + i,
9419                 style : '',
9420                 
9421                 html: cm.getColumnHeader(i)
9422             };
9423             
9424             var tooltip = cm.getColumnTooltip(i);
9425             if (tooltip) {
9426                 c.tooltip = tooltip;
9427             }
9428             
9429             
9430             var hh = '';
9431             
9432             if(typeof(config.sortable) != 'undefined' && config.sortable){
9433                 c.cls += ' sortable';
9434                 c.html = '<i class="fa"></i>' + c.html;
9435             }
9436             
9437             // could use BS4 hidden-..-down 
9438             
9439             if(typeof(config.lgHeader) != 'undefined'){
9440                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9441             }
9442             
9443             if(typeof(config.mdHeader) != 'undefined'){
9444                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9445             }
9446             
9447             if(typeof(config.smHeader) != 'undefined'){
9448                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9449             }
9450             
9451             if(typeof(config.xsHeader) != 'undefined'){
9452                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9453             }
9454             
9455             if(hh.length){
9456                 c.html = hh;
9457             }
9458             
9459             if(typeof(config.tooltip) != 'undefined'){
9460                 c.tooltip = config.tooltip;
9461             }
9462             
9463             if(typeof(config.colspan) != 'undefined'){
9464                 c.colspan = config.colspan;
9465             }
9466             
9467             // hidden is handled by CSS now
9468             
9469             if(typeof(config.dataIndex) != 'undefined'){
9470                 c.sort = config.dataIndex;
9471             }
9472             
9473            
9474             
9475             if(typeof(config.align) != 'undefined' && config.align.length){
9476                 c.style += ' text-align:' + config.align + ';';
9477             }
9478             
9479             /* width is done in CSS
9480              *if(typeof(config.width) != 'undefined'){
9481                 c.style += ' width:' + config.width + 'px;';
9482                 this.totalWidth += config.width;
9483             } else {
9484                 this.totalWidth += 100; // assume minimum of 100 per column?
9485             }
9486             */
9487             
9488             if(typeof(config.cls) != 'undefined'){
9489                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9490             }
9491             // this is the bit that doesnt reall work at all...
9492             
9493             if (this.responsive) {
9494                  
9495             
9496                 ['xs','sm','md','lg'].map(function(size){
9497                     
9498                     if(typeof(config[size]) == 'undefined'){
9499                         return;
9500                     }
9501                      
9502                     if (!config[size]) { // 0 = hidden
9503                         // BS 4 '0' is treated as hide that column and below.
9504                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9505                         return;
9506                     }
9507                     
9508                     c.cls += ' col-' + size + '-' + config[size] + (
9509                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9510                     );
9511                     
9512                     
9513                 });
9514             }
9515             // at the end?
9516             
9517             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9518             
9519             
9520             
9521             
9522             header.cn.push(c)
9523         }
9524         
9525         return header;
9526     },
9527     
9528     renderBody : function()
9529     {
9530         var body = {
9531             tag: 'tbody',
9532             cn : [
9533                 {
9534                     tag: 'tr',
9535                     cn : [
9536                         {
9537                             tag : 'td',
9538                             colspan :  this.cm.getColumnCount()
9539                         }
9540                     ]
9541                 }
9542             ]
9543         };
9544         
9545         return body;
9546     },
9547     
9548     renderFooter : function()
9549     {
9550         var footer = {
9551             tag: 'tfoot',
9552             cn : [
9553                 {
9554                     tag: 'tr',
9555                     cn : [
9556                         {
9557                             tag : 'td',
9558                             colspan :  this.cm.getColumnCount()
9559                         }
9560                     ]
9561                 }
9562             ]
9563         };
9564         
9565         return footer;
9566     },
9567     
9568     
9569     
9570     onLoad : function()
9571     {
9572 //        Roo.log('ds onload');
9573         this.clear();
9574         
9575         var _this = this;
9576         var cm = this.cm;
9577         var ds = this.store;
9578         
9579         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9580             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9581             if (_this.store.sortInfo) {
9582                     
9583                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9584                     e.select('i', true).addClass(['fa-arrow-up']);
9585                 }
9586                 
9587                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9588                     e.select('i', true).addClass(['fa-arrow-down']);
9589                 }
9590             }
9591         });
9592         
9593         var tbody =  this.bodyEl;
9594               
9595         if(ds.getCount() > 0){
9596             ds.data.each(function(d,rowIndex){
9597                 var row =  this.renderRow(cm, ds, rowIndex);
9598                 
9599                 tbody.createChild(row);
9600                 
9601                 var _this = this;
9602                 
9603                 if(row.cellObjects.length){
9604                     Roo.each(row.cellObjects, function(r){
9605                         _this.renderCellObject(r);
9606                     })
9607                 }
9608                 
9609             }, this);
9610         }
9611         
9612         var tfoot = this.el.select('tfoot', true).first();
9613         
9614         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9615             
9616             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9617             
9618             var total = this.ds.getTotalCount();
9619             
9620             if(this.footer.pageSize < total){
9621                 this.mainFoot.show();
9622             }
9623         }
9624         
9625         Roo.each(this.el.select('tbody td', true).elements, function(e){
9626             e.on('mouseover', _this.onMouseover, _this);
9627         });
9628         
9629         Roo.each(this.el.select('tbody td', true).elements, function(e){
9630             e.on('mouseout', _this.onMouseout, _this);
9631         });
9632         this.fireEvent('rowsrendered', this);
9633         
9634         this.autoSize();
9635         
9636         this.initCSS(); /// resize cols
9637
9638         
9639     },
9640     
9641     
9642     onUpdate : function(ds,record)
9643     {
9644         this.refreshRow(record);
9645         this.autoSize();
9646     },
9647     
9648     onRemove : function(ds, record, index, isUpdate){
9649         if(isUpdate !== true){
9650             this.fireEvent("beforerowremoved", this, index, record);
9651         }
9652         var bt = this.bodyEl.dom;
9653         
9654         var rows = this.el.select('tbody > tr', true).elements;
9655         
9656         if(typeof(rows[index]) != 'undefined'){
9657             bt.removeChild(rows[index].dom);
9658         }
9659         
9660 //        if(bt.rows[index]){
9661 //            bt.removeChild(bt.rows[index]);
9662 //        }
9663         
9664         if(isUpdate !== true){
9665             //this.stripeRows(index);
9666             //this.syncRowHeights(index, index);
9667             //this.layout();
9668             this.fireEvent("rowremoved", this, index, record);
9669         }
9670     },
9671     
9672     onAdd : function(ds, records, rowIndex)
9673     {
9674         //Roo.log('on Add called');
9675         // - note this does not handle multiple adding very well..
9676         var bt = this.bodyEl.dom;
9677         for (var i =0 ; i < records.length;i++) {
9678             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9679             //Roo.log(records[i]);
9680             //Roo.log(this.store.getAt(rowIndex+i));
9681             this.insertRow(this.store, rowIndex + i, false);
9682             return;
9683         }
9684         
9685     },
9686     
9687     
9688     refreshRow : function(record){
9689         var ds = this.store, index;
9690         if(typeof record == 'number'){
9691             index = record;
9692             record = ds.getAt(index);
9693         }else{
9694             index = ds.indexOf(record);
9695             if (index < 0) {
9696                 return; // should not happen - but seems to 
9697             }
9698         }
9699         this.insertRow(ds, index, true);
9700         this.autoSize();
9701         this.onRemove(ds, record, index+1, true);
9702         this.autoSize();
9703         //this.syncRowHeights(index, index);
9704         //this.layout();
9705         this.fireEvent("rowupdated", this, index, record);
9706     },
9707     // private - called by RowSelection
9708     onRowSelect : function(rowIndex){
9709         var row = this.getRowDom(rowIndex);
9710         row.addClass(['bg-info','info']);
9711     },
9712     // private - called by RowSelection
9713     onRowDeselect : function(rowIndex)
9714     {
9715         if (rowIndex < 0) {
9716             return;
9717         }
9718         var row = this.getRowDom(rowIndex);
9719         row.removeClass(['bg-info','info']);
9720     },
9721       /**
9722      * Focuses the specified row.
9723      * @param {Number} row The row index
9724      */
9725     focusRow : function(row)
9726     {
9727         //Roo.log('GridView.focusRow');
9728         var x = this.bodyEl.dom.scrollLeft;
9729         this.focusCell(row, 0, false);
9730         this.bodyEl.dom.scrollLeft = x;
9731
9732     },
9733      /**
9734      * Focuses the specified cell.
9735      * @param {Number} row The row index
9736      * @param {Number} col The column index
9737      * @param {Boolean} hscroll false to disable horizontal scrolling
9738      */
9739     focusCell : function(row, col, hscroll)
9740     {
9741         //Roo.log('GridView.focusCell');
9742         var el = this.ensureVisible(row, col, hscroll);
9743         // not sure what focusEL achives = it's a <a> pos relative 
9744         //this.focusEl.alignTo(el, "tl-tl");
9745         //if(Roo.isGecko){
9746         //    this.focusEl.focus();
9747         //}else{
9748         //    this.focusEl.focus.defer(1, this.focusEl);
9749         //}
9750     },
9751     
9752      /**
9753      * Scrolls the specified cell into view
9754      * @param {Number} row The row index
9755      * @param {Number} col The column index
9756      * @param {Boolean} hscroll false to disable horizontal scrolling
9757      */
9758     ensureVisible : function(row, col, hscroll)
9759     {
9760         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9761         //return null; //disable for testing.
9762         if(typeof row != "number"){
9763             row = row.rowIndex;
9764         }
9765         if(row < 0 && row >= this.ds.getCount()){
9766             return  null;
9767         }
9768         col = (col !== undefined ? col : 0);
9769         var cm = this.cm;
9770         while(cm.isHidden(col)){
9771             col++;
9772         }
9773
9774         var el = this.getCellDom(row, col);
9775         if(!el){
9776             return null;
9777         }
9778         var c = this.bodyEl.dom;
9779
9780         var ctop = parseInt(el.offsetTop, 10);
9781         var cleft = parseInt(el.offsetLeft, 10);
9782         var cbot = ctop + el.offsetHeight;
9783         var cright = cleft + el.offsetWidth;
9784
9785         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9786         var ch = 0; //?? header is not withing the area?
9787         var stop = parseInt(c.scrollTop, 10);
9788         var sleft = parseInt(c.scrollLeft, 10);
9789         var sbot = stop + ch;
9790         var sright = sleft + c.clientWidth;
9791         /*
9792         Roo.log('GridView.ensureVisible:' +
9793                 ' ctop:' + ctop +
9794                 ' c.clientHeight:' + c.clientHeight +
9795                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9796                 ' stop:' + stop +
9797                 ' cbot:' + cbot +
9798                 ' sbot:' + sbot +
9799                 ' ch:' + ch  
9800                 );
9801         */
9802         if(ctop < stop){
9803             c.scrollTop = ctop;
9804             //Roo.log("set scrolltop to ctop DISABLE?");
9805         }else if(cbot > sbot){
9806             //Roo.log("set scrolltop to cbot-ch");
9807             c.scrollTop = cbot-ch;
9808         }
9809
9810         if(hscroll !== false){
9811             if(cleft < sleft){
9812                 c.scrollLeft = cleft;
9813             }else if(cright > sright){
9814                 c.scrollLeft = cright-c.clientWidth;
9815             }
9816         }
9817
9818         return el;
9819     },
9820     
9821     
9822     insertRow : function(dm, rowIndex, isUpdate){
9823         
9824         if(!isUpdate){
9825             this.fireEvent("beforerowsinserted", this, rowIndex);
9826         }
9827             //var s = this.getScrollState();
9828         var row = this.renderRow(this.cm, this.store, rowIndex);
9829         // insert before rowIndex..
9830         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9831         
9832         var _this = this;
9833                 
9834         if(row.cellObjects.length){
9835             Roo.each(row.cellObjects, function(r){
9836                 _this.renderCellObject(r);
9837             })
9838         }
9839             
9840         if(!isUpdate){
9841             this.fireEvent("rowsinserted", this, rowIndex);
9842             //this.syncRowHeights(firstRow, lastRow);
9843             //this.stripeRows(firstRow);
9844             //this.layout();
9845         }
9846         
9847     },
9848     
9849     
9850     getRowDom : function(rowIndex)
9851     {
9852         var rows = this.el.select('tbody > tr', true).elements;
9853         
9854         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9855         
9856     },
9857     getCellDom : function(rowIndex, colIndex)
9858     {
9859         var row = this.getRowDom(rowIndex);
9860         if (row === false) {
9861             return false;
9862         }
9863         var cols = row.select('td', true).elements;
9864         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9865         
9866     },
9867     
9868     // returns the object tree for a tr..
9869   
9870     
9871     renderRow : function(cm, ds, rowIndex) 
9872     {
9873         var d = ds.getAt(rowIndex);
9874         
9875         var row = {
9876             tag : 'tr',
9877             cls : 'x-row-' + rowIndex,
9878             cn : []
9879         };
9880             
9881         var cellObjects = [];
9882         
9883         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9884             var config = cm.config[i];
9885             
9886             var renderer = cm.getRenderer(i);
9887             var value = '';
9888             var id = false;
9889             
9890             if(typeof(renderer) !== 'undefined'){
9891                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9892             }
9893             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9894             // and are rendered into the cells after the row is rendered - using the id for the element.
9895             
9896             if(typeof(value) === 'object'){
9897                 id = Roo.id();
9898                 cellObjects.push({
9899                     container : id,
9900                     cfg : value 
9901                 })
9902             }
9903             
9904             var rowcfg = {
9905                 record: d,
9906                 rowIndex : rowIndex,
9907                 colIndex : i,
9908                 rowClass : ''
9909             };
9910
9911             this.fireEvent('rowclass', this, rowcfg);
9912             
9913             var td = {
9914                 tag: 'td',
9915                 // this might end up displaying HTML?
9916                 // this is too messy... - better to only do it on columsn you know are going to be too long
9917                 //tooltip : (typeof(value) === 'object') ? '' : value,
9918                 cls : rowcfg.rowClass + ' x-col-' + i,
9919                 style: '',
9920                 html: (typeof(value) === 'object') ? '' : value
9921             };
9922             
9923             if (id) {
9924                 td.id = id;
9925             }
9926             
9927             if(typeof(config.colspan) != 'undefined'){
9928                 td.colspan = config.colspan;
9929             }
9930             
9931             
9932             
9933             if(typeof(config.align) != 'undefined' && config.align.length){
9934                 td.style += ' text-align:' + config.align + ';';
9935             }
9936             if(typeof(config.valign) != 'undefined' && config.valign.length){
9937                 td.style += ' vertical-align:' + config.valign + ';';
9938             }
9939             /*
9940             if(typeof(config.width) != 'undefined'){
9941                 td.style += ' width:' +  config.width + 'px;';
9942             }
9943             */
9944             
9945             if(typeof(config.cursor) != 'undefined'){
9946                 td.style += ' cursor:' +  config.cursor + ';';
9947             }
9948             
9949             if(typeof(config.cls) != 'undefined'){
9950                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9951             }
9952             if (this.responsive) {
9953                 ['xs','sm','md','lg'].map(function(size){
9954                     
9955                     if(typeof(config[size]) == 'undefined'){
9956                         return;
9957                     }
9958                     
9959                     
9960                       
9961                     if (!config[size]) { // 0 = hidden
9962                         // BS 4 '0' is treated as hide that column and below.
9963                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9964                         return;
9965                     }
9966                     
9967                     td.cls += ' col-' + size + '-' + config[size] + (
9968                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9969                     );
9970                      
9971     
9972                 });
9973             }
9974             row.cn.push(td);
9975            
9976         }
9977         
9978         row.cellObjects = cellObjects;
9979         
9980         return row;
9981           
9982     },
9983     
9984     
9985     
9986     onBeforeLoad : function()
9987     {
9988         
9989     },
9990      /**
9991      * Remove all rows
9992      */
9993     clear : function()
9994     {
9995         this.el.select('tbody', true).first().dom.innerHTML = '';
9996     },
9997     /**
9998      * Show or hide a row.
9999      * @param {Number} rowIndex to show or hide
10000      * @param {Boolean} state hide
10001      */
10002     setRowVisibility : function(rowIndex, state)
10003     {
10004         var bt = this.bodyEl.dom;
10005         
10006         var rows = this.el.select('tbody > tr', true).elements;
10007         
10008         if(typeof(rows[rowIndex]) == 'undefined'){
10009             return;
10010         }
10011         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10012         
10013     },
10014     
10015     
10016     getSelectionModel : function(){
10017         if(!this.selModel){
10018             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10019         }
10020         return this.selModel;
10021     },
10022     /*
10023      * Render the Roo.bootstrap object from renderder
10024      */
10025     renderCellObject : function(r)
10026     {
10027         var _this = this;
10028         
10029         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10030         
10031         var t = r.cfg.render(r.container);
10032         
10033         if(r.cfg.cn){
10034             Roo.each(r.cfg.cn, function(c){
10035                 var child = {
10036                     container: t.getChildContainer(),
10037                     cfg: c
10038                 };
10039                 _this.renderCellObject(child);
10040             })
10041         }
10042     },
10043     /**
10044      * get the Row Index from a dom element.
10045      * @param {Roo.Element} row The row to look for
10046      * @returns {Number} the row
10047      */
10048     getRowIndex : function(row)
10049     {
10050         var rowIndex = -1;
10051         
10052         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10053             if(el != row){
10054                 return;
10055             }
10056             
10057             rowIndex = index;
10058         });
10059         
10060         return rowIndex;
10061     },
10062     /**
10063      * get the header TH element for columnIndex
10064      * @param {Number} columnIndex
10065      * @returns {Roo.Element}
10066      */
10067     getHeaderIndex: function(colIndex)
10068     {
10069         var cols = this.headEl.select('th', true).elements;
10070         return cols[colIndex]; 
10071     },
10072     /**
10073      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10074      * @param {domElement} cell to look for
10075      * @returns {Number} the column
10076      */
10077     getCellIndex : function(cell)
10078     {
10079         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10080         if(id){
10081             return parseInt(id[1], 10);
10082         }
10083         return 0;
10084     },
10085      /**
10086      * Returns the grid's underlying element = used by panel.Grid
10087      * @return {Element} The element
10088      */
10089     getGridEl : function(){
10090         return this.el;
10091     },
10092      /**
10093      * Forces a resize - used by panel.Grid
10094      * @return {Element} The element
10095      */
10096     autoSize : function()
10097     {
10098         //var ctr = Roo.get(this.container.dom.parentElement);
10099         var ctr = Roo.get(this.el.dom);
10100         
10101         var thd = this.getGridEl().select('thead',true).first();
10102         var tbd = this.getGridEl().select('tbody', true).first();
10103         var tfd = this.getGridEl().select('tfoot', true).first();
10104         
10105         var cw = ctr.getWidth();
10106         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10107         
10108         if (tbd) {
10109             
10110             tbd.setWidth(ctr.getWidth());
10111             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10112             // this needs fixing for various usage - currently only hydra job advers I think..
10113             //tdb.setHeight(
10114             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10115             //); 
10116             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10117             cw -= barsize;
10118         }
10119         cw = Math.max(cw, this.totalWidth);
10120         this.getGridEl().select('tbody tr',true).setWidth(cw);
10121         this.initCSS();
10122         
10123         // resize 'expandable coloumn?
10124         
10125         return; // we doe not have a view in this design..
10126         
10127     },
10128     onBodyScroll: function()
10129     {
10130         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10131         if(this.headEl){
10132             this.headEl.setStyle({
10133                 'position' : 'relative',
10134                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10135             });
10136         }
10137         
10138         if(this.lazyLoad){
10139             
10140             var scrollHeight = this.bodyEl.dom.scrollHeight;
10141             
10142             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10143             
10144             var height = this.bodyEl.getHeight();
10145             
10146             if(scrollHeight - height == scrollTop) {
10147                 
10148                 var total = this.ds.getTotalCount();
10149                 
10150                 if(this.footer.cursor + this.footer.pageSize < total){
10151                     
10152                     this.footer.ds.load({
10153                         params : {
10154                             start : this.footer.cursor + this.footer.pageSize,
10155                             limit : this.footer.pageSize
10156                         },
10157                         add : true
10158                     });
10159                 }
10160             }
10161             
10162         }
10163     },
10164     onColumnSplitterMoved : function(i, diff)
10165     {
10166         this.userResized = true;
10167         
10168         var cm = this.colModel;
10169         
10170         var w = this.getHeaderIndex(i).getWidth() + diff;
10171         
10172         
10173         cm.setColumnWidth(i, w, true);
10174         this.initCSS();
10175         //var cid = cm.getColumnId(i); << not used in this version?
10176        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10177         
10178         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10179         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10180         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10181 */
10182         //this.updateSplitters();
10183         //this.layout(); << ??
10184         this.fireEvent("columnresize", i, w);
10185     },
10186     onHeaderChange : function()
10187     {
10188         var header = this.renderHeader();
10189         var table = this.el.select('table', true).first();
10190         
10191         this.headEl.remove();
10192         this.headEl = table.createChild(header, this.bodyEl, false);
10193         
10194         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10195             e.on('click', this.sort, this);
10196         }, this);
10197         
10198         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10199             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10200         }
10201         
10202     },
10203     
10204     onHiddenChange : function(colModel, colIndex, hidden)
10205     {
10206         /*
10207         this.cm.setHidden()
10208         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10209         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10210         
10211         this.CSS.updateRule(thSelector, "display", "");
10212         this.CSS.updateRule(tdSelector, "display", "");
10213         
10214         if(hidden){
10215             this.CSS.updateRule(thSelector, "display", "none");
10216             this.CSS.updateRule(tdSelector, "display", "none");
10217         }
10218         */
10219         // onload calls initCSS()
10220         this.onHeaderChange();
10221         this.onLoad();
10222     },
10223     
10224     setColumnWidth: function(col_index, width)
10225     {
10226         // width = "md-2 xs-2..."
10227         if(!this.colModel.config[col_index]) {
10228             return;
10229         }
10230         
10231         var w = width.split(" ");
10232         
10233         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10234         
10235         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10236         
10237         
10238         for(var j = 0; j < w.length; j++) {
10239             
10240             if(!w[j]) {
10241                 continue;
10242             }
10243             
10244             var size_cls = w[j].split("-");
10245             
10246             if(!Number.isInteger(size_cls[1] * 1)) {
10247                 continue;
10248             }
10249             
10250             if(!this.colModel.config[col_index][size_cls[0]]) {
10251                 continue;
10252             }
10253             
10254             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10255                 continue;
10256             }
10257             
10258             h_row[0].classList.replace(
10259                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10260                 "col-"+size_cls[0]+"-"+size_cls[1]
10261             );
10262             
10263             for(var i = 0; i < rows.length; i++) {
10264                 
10265                 var size_cls = w[j].split("-");
10266                 
10267                 if(!Number.isInteger(size_cls[1] * 1)) {
10268                     continue;
10269                 }
10270                 
10271                 if(!this.colModel.config[col_index][size_cls[0]]) {
10272                     continue;
10273                 }
10274                 
10275                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10276                     continue;
10277                 }
10278                 
10279                 rows[i].classList.replace(
10280                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10281                     "col-"+size_cls[0]+"-"+size_cls[1]
10282                 );
10283             }
10284             
10285             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10286         }
10287     }
10288 });
10289
10290 // currently only used to find the split on drag.. 
10291 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10292
10293 /**
10294  * @depricated
10295 */
10296 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10297 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10298 /*
10299  * - LGPL
10300  *
10301  * table cell
10302  * 
10303  */
10304
10305 /**
10306  * @class Roo.bootstrap.TableCell
10307  * @extends Roo.bootstrap.Component
10308  * @children Roo.bootstrap.Component
10309  * @parent Roo.bootstrap.TableRow
10310  * Bootstrap TableCell class
10311  * 
10312  * @cfg {String} html cell contain text
10313  * @cfg {String} cls cell class
10314  * @cfg {String} tag cell tag (td|th) default td
10315  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10316  * @cfg {String} align Aligns the content in a cell
10317  * @cfg {String} axis Categorizes cells
10318  * @cfg {String} bgcolor Specifies the background color of a cell
10319  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10320  * @cfg {Number} colspan Specifies the number of columns a cell should span
10321  * @cfg {String} headers Specifies one or more header cells a cell is related to
10322  * @cfg {Number} height Sets the height of a cell
10323  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10324  * @cfg {Number} rowspan Sets the number of rows a cell should span
10325  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10326  * @cfg {String} valign Vertical aligns the content in a cell
10327  * @cfg {Number} width Specifies the width of a cell
10328  * 
10329  * @constructor
10330  * Create a new TableCell
10331  * @param {Object} config The config object
10332  */
10333
10334 Roo.bootstrap.TableCell = function(config){
10335     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10336 };
10337
10338 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10339     
10340     html: false,
10341     cls: false,
10342     tag: false,
10343     abbr: false,
10344     align: false,
10345     axis: false,
10346     bgcolor: false,
10347     charoff: false,
10348     colspan: false,
10349     headers: false,
10350     height: false,
10351     nowrap: false,
10352     rowspan: false,
10353     scope: false,
10354     valign: false,
10355     width: false,
10356     
10357     
10358     getAutoCreate : function(){
10359         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10360         
10361         cfg = {
10362             tag: 'td'
10363         };
10364         
10365         if(this.tag){
10366             cfg.tag = this.tag;
10367         }
10368         
10369         if (this.html) {
10370             cfg.html=this.html
10371         }
10372         if (this.cls) {
10373             cfg.cls=this.cls
10374         }
10375         if (this.abbr) {
10376             cfg.abbr=this.abbr
10377         }
10378         if (this.align) {
10379             cfg.align=this.align
10380         }
10381         if (this.axis) {
10382             cfg.axis=this.axis
10383         }
10384         if (this.bgcolor) {
10385             cfg.bgcolor=this.bgcolor
10386         }
10387         if (this.charoff) {
10388             cfg.charoff=this.charoff
10389         }
10390         if (this.colspan) {
10391             cfg.colspan=this.colspan
10392         }
10393         if (this.headers) {
10394             cfg.headers=this.headers
10395         }
10396         if (this.height) {
10397             cfg.height=this.height
10398         }
10399         if (this.nowrap) {
10400             cfg.nowrap=this.nowrap
10401         }
10402         if (this.rowspan) {
10403             cfg.rowspan=this.rowspan
10404         }
10405         if (this.scope) {
10406             cfg.scope=this.scope
10407         }
10408         if (this.valign) {
10409             cfg.valign=this.valign
10410         }
10411         if (this.width) {
10412             cfg.width=this.width
10413         }
10414         
10415         
10416         return cfg;
10417     }
10418    
10419 });
10420
10421  
10422
10423  /*
10424  * - LGPL
10425  *
10426  * table row
10427  * 
10428  */
10429
10430 /**
10431  * @class Roo.bootstrap.TableRow
10432  * @extends Roo.bootstrap.Component
10433  * @children Roo.bootstrap.TableCell
10434  * @parent Roo.bootstrap.TableBody
10435  * Bootstrap TableRow class
10436  * @cfg {String} cls row class
10437  * @cfg {String} align Aligns the content in a table row
10438  * @cfg {String} bgcolor Specifies a background color for a table row
10439  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10440  * @cfg {String} valign Vertical aligns the content in a table row
10441  * 
10442  * @constructor
10443  * Create a new TableRow
10444  * @param {Object} config The config object
10445  */
10446
10447 Roo.bootstrap.TableRow = function(config){
10448     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10449 };
10450
10451 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10452     
10453     cls: false,
10454     align: false,
10455     bgcolor: false,
10456     charoff: false,
10457     valign: false,
10458     
10459     getAutoCreate : function(){
10460         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10461         
10462         cfg = {
10463             tag: 'tr'
10464         };
10465             
10466         if(this.cls){
10467             cfg.cls = this.cls;
10468         }
10469         if(this.align){
10470             cfg.align = this.align;
10471         }
10472         if(this.bgcolor){
10473             cfg.bgcolor = this.bgcolor;
10474         }
10475         if(this.charoff){
10476             cfg.charoff = this.charoff;
10477         }
10478         if(this.valign){
10479             cfg.valign = this.valign;
10480         }
10481         
10482         return cfg;
10483     }
10484    
10485 });
10486
10487  
10488
10489  /*
10490  * - LGPL
10491  *
10492  * table body
10493  * 
10494  */
10495
10496 /**
10497  * @class Roo.bootstrap.TableBody
10498  * @extends Roo.bootstrap.Component
10499  * @children Roo.bootstrap.TableRow
10500  * @parent Roo.bootstrap.Table
10501  * Bootstrap TableBody class
10502  * @cfg {String} cls element class
10503  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10504  * @cfg {String} align Aligns the content inside the element
10505  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10506  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10507  * 
10508  * @constructor
10509  * Create a new TableBody
10510  * @param {Object} config The config object
10511  */
10512
10513 Roo.bootstrap.TableBody = function(config){
10514     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10515 };
10516
10517 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10518     
10519     cls: false,
10520     tag: false,
10521     align: false,
10522     charoff: false,
10523     valign: false,
10524     
10525     getAutoCreate : function(){
10526         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10527         
10528         cfg = {
10529             tag: 'tbody'
10530         };
10531             
10532         if (this.cls) {
10533             cfg.cls=this.cls
10534         }
10535         if(this.tag){
10536             cfg.tag = this.tag;
10537         }
10538         
10539         if(this.align){
10540             cfg.align = this.align;
10541         }
10542         if(this.charoff){
10543             cfg.charoff = this.charoff;
10544         }
10545         if(this.valign){
10546             cfg.valign = this.valign;
10547         }
10548         
10549         return cfg;
10550     }
10551     
10552     
10553 //    initEvents : function()
10554 //    {
10555 //        
10556 //        if(!this.store){
10557 //            return;
10558 //        }
10559 //        
10560 //        this.store = Roo.factory(this.store, Roo.data);
10561 //        this.store.on('load', this.onLoad, this);
10562 //        
10563 //        this.store.load();
10564 //        
10565 //    },
10566 //    
10567 //    onLoad: function () 
10568 //    {   
10569 //        this.fireEvent('load', this);
10570 //    }
10571 //    
10572 //   
10573 });
10574
10575  
10576
10577  /*
10578  * Based on:
10579  * Ext JS Library 1.1.1
10580  * Copyright(c) 2006-2007, Ext JS, LLC.
10581  *
10582  * Originally Released Under LGPL - original licence link has changed is not relivant.
10583  *
10584  * Fork - LGPL
10585  * <script type="text/javascript">
10586  */
10587
10588 // as we use this in bootstrap.
10589 Roo.namespace('Roo.form');
10590  /**
10591  * @class Roo.form.Action
10592  * Internal Class used to handle form actions
10593  * @constructor
10594  * @param {Roo.form.BasicForm} el The form element or its id
10595  * @param {Object} config Configuration options
10596  */
10597
10598  
10599  
10600 // define the action interface
10601 Roo.form.Action = function(form, options){
10602     this.form = form;
10603     this.options = options || {};
10604 };
10605 /**
10606  * Client Validation Failed
10607  * @const 
10608  */
10609 Roo.form.Action.CLIENT_INVALID = 'client';
10610 /**
10611  * Server Validation Failed
10612  * @const 
10613  */
10614 Roo.form.Action.SERVER_INVALID = 'server';
10615  /**
10616  * Connect to Server Failed
10617  * @const 
10618  */
10619 Roo.form.Action.CONNECT_FAILURE = 'connect';
10620 /**
10621  * Reading Data from Server Failed
10622  * @const 
10623  */
10624 Roo.form.Action.LOAD_FAILURE = 'load';
10625
10626 Roo.form.Action.prototype = {
10627     type : 'default',
10628     failureType : undefined,
10629     response : undefined,
10630     result : undefined,
10631
10632     // interface method
10633     run : function(options){
10634
10635     },
10636
10637     // interface method
10638     success : function(response){
10639
10640     },
10641
10642     // interface method
10643     handleResponse : function(response){
10644
10645     },
10646
10647     // default connection failure
10648     failure : function(response){
10649         
10650         this.response = response;
10651         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10652         this.form.afterAction(this, false);
10653     },
10654
10655     processResponse : function(response){
10656         this.response = response;
10657         if(!response.responseText){
10658             return true;
10659         }
10660         this.result = this.handleResponse(response);
10661         return this.result;
10662     },
10663
10664     // utility functions used internally
10665     getUrl : function(appendParams){
10666         var url = this.options.url || this.form.url || this.form.el.dom.action;
10667         if(appendParams){
10668             var p = this.getParams();
10669             if(p){
10670                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10671             }
10672         }
10673         return url;
10674     },
10675
10676     getMethod : function(){
10677         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10678     },
10679
10680     getParams : function(){
10681         var bp = this.form.baseParams;
10682         var p = this.options.params;
10683         if(p){
10684             if(typeof p == "object"){
10685                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10686             }else if(typeof p == 'string' && bp){
10687                 p += '&' + Roo.urlEncode(bp);
10688             }
10689         }else if(bp){
10690             p = Roo.urlEncode(bp);
10691         }
10692         return p;
10693     },
10694
10695     createCallback : function(){
10696         return {
10697             success: this.success,
10698             failure: this.failure,
10699             scope: this,
10700             timeout: (this.form.timeout*1000),
10701             upload: this.form.fileUpload ? this.success : undefined
10702         };
10703     }
10704 };
10705
10706 Roo.form.Action.Submit = function(form, options){
10707     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10708 };
10709
10710 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10711     type : 'submit',
10712
10713     haveProgress : false,
10714     uploadComplete : false,
10715     
10716     // uploadProgress indicator.
10717     uploadProgress : function()
10718     {
10719         if (!this.form.progressUrl) {
10720             return;
10721         }
10722         
10723         if (!this.haveProgress) {
10724             Roo.MessageBox.progress("Uploading", "Uploading");
10725         }
10726         if (this.uploadComplete) {
10727            Roo.MessageBox.hide();
10728            return;
10729         }
10730         
10731         this.haveProgress = true;
10732    
10733         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10734         
10735         var c = new Roo.data.Connection();
10736         c.request({
10737             url : this.form.progressUrl,
10738             params: {
10739                 id : uid
10740             },
10741             method: 'GET',
10742             success : function(req){
10743                //console.log(data);
10744                 var rdata = false;
10745                 var edata;
10746                 try  {
10747                    rdata = Roo.decode(req.responseText)
10748                 } catch (e) {
10749                     Roo.log("Invalid data from server..");
10750                     Roo.log(edata);
10751                     return;
10752                 }
10753                 if (!rdata || !rdata.success) {
10754                     Roo.log(rdata);
10755                     Roo.MessageBox.alert(Roo.encode(rdata));
10756                     return;
10757                 }
10758                 var data = rdata.data;
10759                 
10760                 if (this.uploadComplete) {
10761                    Roo.MessageBox.hide();
10762                    return;
10763                 }
10764                    
10765                 if (data){
10766                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10767                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10768                     );
10769                 }
10770                 this.uploadProgress.defer(2000,this);
10771             },
10772        
10773             failure: function(data) {
10774                 Roo.log('progress url failed ');
10775                 Roo.log(data);
10776             },
10777             scope : this
10778         });
10779            
10780     },
10781     
10782     
10783     run : function()
10784     {
10785         // run get Values on the form, so it syncs any secondary forms.
10786         this.form.getValues();
10787         
10788         var o = this.options;
10789         var method = this.getMethod();
10790         var isPost = method == 'POST';
10791         if(o.clientValidation === false || this.form.isValid()){
10792             
10793             if (this.form.progressUrl) {
10794                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10795                     (new Date() * 1) + '' + Math.random());
10796                     
10797             } 
10798             
10799             
10800             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10801                 form:this.form.el.dom,
10802                 url:this.getUrl(!isPost),
10803                 method: method,
10804                 params:isPost ? this.getParams() : null,
10805                 isUpload: this.form.fileUpload,
10806                 formData : this.form.formData
10807             }));
10808             
10809             this.uploadProgress();
10810
10811         }else if (o.clientValidation !== false){ // client validation failed
10812             this.failureType = Roo.form.Action.CLIENT_INVALID;
10813             this.form.afterAction(this, false);
10814         }
10815     },
10816
10817     success : function(response)
10818     {
10819         this.uploadComplete= true;
10820         if (this.haveProgress) {
10821             Roo.MessageBox.hide();
10822         }
10823         
10824         
10825         var result = this.processResponse(response);
10826         if(result === true || result.success){
10827             this.form.afterAction(this, true);
10828             return;
10829         }
10830         if(result.errors){
10831             this.form.markInvalid(result.errors);
10832             this.failureType = Roo.form.Action.SERVER_INVALID;
10833         }
10834         this.form.afterAction(this, false);
10835     },
10836     failure : function(response)
10837     {
10838         this.uploadComplete= true;
10839         if (this.haveProgress) {
10840             Roo.MessageBox.hide();
10841         }
10842         
10843         this.response = response;
10844         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10845         this.form.afterAction(this, false);
10846     },
10847     
10848     handleResponse : function(response){
10849         if(this.form.errorReader){
10850             var rs = this.form.errorReader.read(response);
10851             var errors = [];
10852             if(rs.records){
10853                 for(var i = 0, len = rs.records.length; i < len; i++) {
10854                     var r = rs.records[i];
10855                     errors[i] = r.data;
10856                 }
10857             }
10858             if(errors.length < 1){
10859                 errors = null;
10860             }
10861             return {
10862                 success : rs.success,
10863                 errors : errors
10864             };
10865         }
10866         var ret = false;
10867         try {
10868             ret = Roo.decode(response.responseText);
10869         } catch (e) {
10870             ret = {
10871                 success: false,
10872                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10873                 errors : []
10874             };
10875         }
10876         return ret;
10877         
10878     }
10879 });
10880
10881
10882 Roo.form.Action.Load = function(form, options){
10883     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10884     this.reader = this.form.reader;
10885 };
10886
10887 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10888     type : 'load',
10889
10890     run : function(){
10891         
10892         Roo.Ajax.request(Roo.apply(
10893                 this.createCallback(), {
10894                     method:this.getMethod(),
10895                     url:this.getUrl(false),
10896                     params:this.getParams()
10897         }));
10898     },
10899
10900     success : function(response){
10901         
10902         var result = this.processResponse(response);
10903         if(result === true || !result.success || !result.data){
10904             this.failureType = Roo.form.Action.LOAD_FAILURE;
10905             this.form.afterAction(this, false);
10906             return;
10907         }
10908         this.form.clearInvalid();
10909         this.form.setValues(result.data);
10910         this.form.afterAction(this, true);
10911     },
10912
10913     handleResponse : function(response){
10914         if(this.form.reader){
10915             var rs = this.form.reader.read(response);
10916             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10917             return {
10918                 success : rs.success,
10919                 data : data
10920             };
10921         }
10922         return Roo.decode(response.responseText);
10923     }
10924 });
10925
10926 Roo.form.Action.ACTION_TYPES = {
10927     'load' : Roo.form.Action.Load,
10928     'submit' : Roo.form.Action.Submit
10929 };/*
10930  * - LGPL
10931  *
10932  * form
10933  *
10934  */
10935
10936 /**
10937  * @class Roo.bootstrap.Form
10938  * @extends Roo.bootstrap.Component
10939  * @children Roo.bootstrap.Component
10940  * Bootstrap Form class
10941  * @cfg {String} method  GET | POST (default POST)
10942  * @cfg {String} labelAlign top | left (default top)
10943  * @cfg {String} align left  | right - for navbars
10944  * @cfg {Boolean} loadMask load mask when submit (default true)
10945
10946  *
10947  * @constructor
10948  * Create a new Form
10949  * @param {Object} config The config object
10950  */
10951
10952
10953 Roo.bootstrap.Form = function(config){
10954     
10955     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10956     
10957     Roo.bootstrap.Form.popover.apply();
10958     
10959     this.addEvents({
10960         /**
10961          * @event clientvalidation
10962          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10963          * @param {Form} this
10964          * @param {Boolean} valid true if the form has passed client-side validation
10965          */
10966         clientvalidation: true,
10967         /**
10968          * @event beforeaction
10969          * Fires before any action is performed. Return false to cancel the action.
10970          * @param {Form} this
10971          * @param {Action} action The action to be performed
10972          */
10973         beforeaction: true,
10974         /**
10975          * @event actionfailed
10976          * Fires when an action fails.
10977          * @param {Form} this
10978          * @param {Action} action The action that failed
10979          */
10980         actionfailed : true,
10981         /**
10982          * @event actioncomplete
10983          * Fires when an action is completed.
10984          * @param {Form} this
10985          * @param {Action} action The action that completed
10986          */
10987         actioncomplete : true
10988     });
10989 };
10990
10991 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10992
10993      /**
10994      * @cfg {String} method
10995      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10996      */
10997     method : 'POST',
10998     /**
10999      * @cfg {String} url
11000      * The URL to use for form actions if one isn't supplied in the action options.
11001      */
11002     /**
11003      * @cfg {Boolean} fileUpload
11004      * Set to true if this form is a file upload.
11005      */
11006
11007     /**
11008      * @cfg {Object} baseParams
11009      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11010      */
11011
11012     /**
11013      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11014      */
11015     timeout: 30,
11016     /**
11017      * @cfg {Sting} align (left|right) for navbar forms
11018      */
11019     align : 'left',
11020
11021     // private
11022     activeAction : null,
11023
11024     /**
11025      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11026      * element by passing it or its id or mask the form itself by passing in true.
11027      * @type Mixed
11028      */
11029     waitMsgTarget : false,
11030
11031     loadMask : true,
11032     
11033     /**
11034      * @cfg {Boolean} errorMask (true|false) default false
11035      */
11036     errorMask : false,
11037     
11038     /**
11039      * @cfg {Number} maskOffset Default 100
11040      */
11041     maskOffset : 100,
11042     
11043     /**
11044      * @cfg {Boolean} maskBody
11045      */
11046     maskBody : false,
11047
11048     getAutoCreate : function(){
11049
11050         var cfg = {
11051             tag: 'form',
11052             method : this.method || 'POST',
11053             id : this.id || Roo.id(),
11054             cls : ''
11055         };
11056         if (this.parent().xtype.match(/^Nav/)) {
11057             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11058
11059         }
11060
11061         if (this.labelAlign == 'left' ) {
11062             cfg.cls += ' form-horizontal';
11063         }
11064
11065
11066         return cfg;
11067     },
11068     initEvents : function()
11069     {
11070         this.el.on('submit', this.onSubmit, this);
11071         // this was added as random key presses on the form where triggering form submit.
11072         this.el.on('keypress', function(e) {
11073             if (e.getCharCode() != 13) {
11074                 return true;
11075             }
11076             // we might need to allow it for textareas.. and some other items.
11077             // check e.getTarget().
11078
11079             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11080                 return true;
11081             }
11082
11083             Roo.log("keypress blocked");
11084
11085             e.preventDefault();
11086             return false;
11087         });
11088         
11089     },
11090     // private
11091     onSubmit : function(e){
11092         e.stopEvent();
11093     },
11094
11095      /**
11096      * Returns true if client-side validation on the form is successful.
11097      * @return Boolean
11098      */
11099     isValid : function(){
11100         var items = this.getItems();
11101         var valid = true;
11102         var target = false;
11103         
11104         items.each(function(f){
11105             
11106             if(f.validate()){
11107                 return;
11108             }
11109             
11110             Roo.log('invalid field: ' + f.name);
11111             
11112             valid = false;
11113
11114             if(!target && f.el.isVisible(true)){
11115                 target = f;
11116             }
11117            
11118         });
11119         
11120         if(this.errorMask && !valid){
11121             Roo.bootstrap.Form.popover.mask(this, target);
11122         }
11123         
11124         return valid;
11125     },
11126     
11127     /**
11128      * Returns true if any fields in this form have changed since their original load.
11129      * @return Boolean
11130      */
11131     isDirty : function(){
11132         var dirty = false;
11133         var items = this.getItems();
11134         items.each(function(f){
11135            if(f.isDirty()){
11136                dirty = true;
11137                return false;
11138            }
11139            return true;
11140         });
11141         return dirty;
11142     },
11143      /**
11144      * Performs a predefined action (submit or load) or custom actions you define on this form.
11145      * @param {String} actionName The name of the action type
11146      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11147      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11148      * accept other config options):
11149      * <pre>
11150 Property          Type             Description
11151 ----------------  ---------------  ----------------------------------------------------------------------------------
11152 url               String           The url for the action (defaults to the form's url)
11153 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11154 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11155 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11156                                    validate the form on the client (defaults to false)
11157      * </pre>
11158      * @return {BasicForm} this
11159      */
11160     doAction : function(action, options){
11161         if(typeof action == 'string'){
11162             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11163         }
11164         if(this.fireEvent('beforeaction', this, action) !== false){
11165             this.beforeAction(action);
11166             action.run.defer(100, action);
11167         }
11168         return this;
11169     },
11170
11171     // private
11172     beforeAction : function(action){
11173         var o = action.options;
11174         
11175         if(this.loadMask){
11176             
11177             if(this.maskBody){
11178                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11179             } else {
11180                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11181             }
11182         }
11183         // not really supported yet.. ??
11184
11185         //if(this.waitMsgTarget === true){
11186         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11187         //}else if(this.waitMsgTarget){
11188         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11189         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11190         //}else {
11191         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11192        // }
11193
11194     },
11195
11196     // private
11197     afterAction : function(action, success){
11198         this.activeAction = null;
11199         var o = action.options;
11200
11201         if(this.loadMask){
11202             
11203             if(this.maskBody){
11204                 Roo.get(document.body).unmask();
11205             } else {
11206                 this.el.unmask();
11207             }
11208         }
11209         
11210         //if(this.waitMsgTarget === true){
11211 //            this.el.unmask();
11212         //}else if(this.waitMsgTarget){
11213         //    this.waitMsgTarget.unmask();
11214         //}else{
11215         //    Roo.MessageBox.updateProgress(1);
11216         //    Roo.MessageBox.hide();
11217        // }
11218         //
11219         if(success){
11220             if(o.reset){
11221                 this.reset();
11222             }
11223             Roo.callback(o.success, o.scope, [this, action]);
11224             this.fireEvent('actioncomplete', this, action);
11225
11226         }else{
11227
11228             // failure condition..
11229             // we have a scenario where updates need confirming.
11230             // eg. if a locking scenario exists..
11231             // we look for { errors : { needs_confirm : true }} in the response.
11232             if (
11233                 (typeof(action.result) != 'undefined')  &&
11234                 (typeof(action.result.errors) != 'undefined')  &&
11235                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11236            ){
11237                 var _t = this;
11238                 Roo.log("not supported yet");
11239                  /*
11240
11241                 Roo.MessageBox.confirm(
11242                     "Change requires confirmation",
11243                     action.result.errorMsg,
11244                     function(r) {
11245                         if (r != 'yes') {
11246                             return;
11247                         }
11248                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11249                     }
11250
11251                 );
11252                 */
11253
11254
11255                 return;
11256             }
11257
11258             Roo.callback(o.failure, o.scope, [this, action]);
11259             // show an error message if no failed handler is set..
11260             if (!this.hasListener('actionfailed')) {
11261                 Roo.log("need to add dialog support");
11262                 /*
11263                 Roo.MessageBox.alert("Error",
11264                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11265                         action.result.errorMsg :
11266                         "Saving Failed, please check your entries or try again"
11267                 );
11268                 */
11269             }
11270
11271             this.fireEvent('actionfailed', this, action);
11272         }
11273
11274     },
11275     /**
11276      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11277      * @param {String} id The value to search for
11278      * @return Field
11279      */
11280     findField : function(id){
11281         var items = this.getItems();
11282         var field = items.get(id);
11283         if(!field){
11284              items.each(function(f){
11285                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11286                     field = f;
11287                     return false;
11288                 }
11289                 return true;
11290             });
11291         }
11292         return field || null;
11293     },
11294      /**
11295      * Mark fields in this form invalid in bulk.
11296      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11297      * @return {BasicForm} this
11298      */
11299     markInvalid : function(errors){
11300         if(errors instanceof Array){
11301             for(var i = 0, len = errors.length; i < len; i++){
11302                 var fieldError = errors[i];
11303                 var f = this.findField(fieldError.id);
11304                 if(f){
11305                     f.markInvalid(fieldError.msg);
11306                 }
11307             }
11308         }else{
11309             var field, id;
11310             for(id in errors){
11311                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11312                     field.markInvalid(errors[id]);
11313                 }
11314             }
11315         }
11316         //Roo.each(this.childForms || [], function (f) {
11317         //    f.markInvalid(errors);
11318         //});
11319
11320         return this;
11321     },
11322
11323     /**
11324      * Set values for fields in this form in bulk.
11325      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11326      * @return {BasicForm} this
11327      */
11328     setValues : function(values){
11329         if(values instanceof Array){ // array of objects
11330             for(var i = 0, len = values.length; i < len; i++){
11331                 var v = values[i];
11332                 var f = this.findField(v.id);
11333                 if(f){
11334                     f.setValue(v.value);
11335                     if(this.trackResetOnLoad){
11336                         f.originalValue = f.getValue();
11337                     }
11338                 }
11339             }
11340         }else{ // object hash
11341             var field, id;
11342             for(id in values){
11343                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11344
11345                     if (field.setFromData &&
11346                         field.valueField &&
11347                         field.displayField &&
11348                         // combos' with local stores can
11349                         // be queried via setValue()
11350                         // to set their value..
11351                         (field.store && !field.store.isLocal)
11352                         ) {
11353                         // it's a combo
11354                         var sd = { };
11355                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11356                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11357                         field.setFromData(sd);
11358
11359                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11360                         
11361                         field.setFromData(values);
11362                         
11363                     } else {
11364                         field.setValue(values[id]);
11365                     }
11366
11367
11368                     if(this.trackResetOnLoad){
11369                         field.originalValue = field.getValue();
11370                     }
11371                 }
11372             }
11373         }
11374
11375         //Roo.each(this.childForms || [], function (f) {
11376         //    f.setValues(values);
11377         //});
11378
11379         return this;
11380     },
11381
11382     /**
11383      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11384      * they are returned as an array.
11385      * @param {Boolean} asString
11386      * @return {Object}
11387      */
11388     getValues : function(asString){
11389         //if (this.childForms) {
11390             // copy values from the child forms
11391         //    Roo.each(this.childForms, function (f) {
11392         //        this.setValues(f.getValues());
11393         //    }, this);
11394         //}
11395
11396
11397
11398         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11399         if(asString === true){
11400             return fs;
11401         }
11402         return Roo.urlDecode(fs);
11403     },
11404
11405     /**
11406      * Returns the fields in this form as an object with key/value pairs.
11407      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11408      * @return {Object}
11409      */
11410     getFieldValues : function(with_hidden)
11411     {
11412         var items = this.getItems();
11413         var ret = {};
11414         items.each(function(f){
11415             
11416             if (!f.getName()) {
11417                 return;
11418             }
11419             
11420             var v = f.getValue();
11421             
11422             if (f.inputType =='radio') {
11423                 if (typeof(ret[f.getName()]) == 'undefined') {
11424                     ret[f.getName()] = ''; // empty..
11425                 }
11426
11427                 if (!f.el.dom.checked) {
11428                     return;
11429
11430                 }
11431                 v = f.el.dom.value;
11432
11433             }
11434             
11435             if(f.xtype == 'MoneyField'){
11436                 ret[f.currencyName] = f.getCurrency();
11437             }
11438
11439             // not sure if this supported any more..
11440             if ((typeof(v) == 'object') && f.getRawValue) {
11441                 v = f.getRawValue() ; // dates..
11442             }
11443             // combo boxes where name != hiddenName...
11444             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11445                 ret[f.name] = f.getRawValue();
11446             }
11447             ret[f.getName()] = v;
11448         });
11449
11450         return ret;
11451     },
11452
11453     /**
11454      * Clears all invalid messages in this form.
11455      * @return {BasicForm} this
11456      */
11457     clearInvalid : function(){
11458         var items = this.getItems();
11459
11460         items.each(function(f){
11461            f.clearInvalid();
11462         });
11463
11464         return this;
11465     },
11466
11467     /**
11468      * Resets this form.
11469      * @return {BasicForm} this
11470      */
11471     reset : function(){
11472         var items = this.getItems();
11473         items.each(function(f){
11474             f.reset();
11475         });
11476
11477         Roo.each(this.childForms || [], function (f) {
11478             f.reset();
11479         });
11480
11481
11482         return this;
11483     },
11484     
11485     getItems : function()
11486     {
11487         var r=new Roo.util.MixedCollection(false, function(o){
11488             return o.id || (o.id = Roo.id());
11489         });
11490         var iter = function(el) {
11491             if (el.inputEl) {
11492                 r.add(el);
11493             }
11494             if (!el.items) {
11495                 return;
11496             }
11497             Roo.each(el.items,function(e) {
11498                 iter(e);
11499             });
11500         };
11501
11502         iter(this);
11503         return r;
11504     },
11505     
11506     hideFields : function(items)
11507     {
11508         Roo.each(items, function(i){
11509             
11510             var f = this.findField(i);
11511             
11512             if(!f){
11513                 return;
11514             }
11515             
11516             f.hide();
11517             
11518         }, this);
11519     },
11520     
11521     showFields : function(items)
11522     {
11523         Roo.each(items, function(i){
11524             
11525             var f = this.findField(i);
11526             
11527             if(!f){
11528                 return;
11529             }
11530             
11531             f.show();
11532             
11533         }, this);
11534     }
11535
11536 });
11537
11538 Roo.apply(Roo.bootstrap.Form, {
11539     
11540     popover : {
11541         
11542         padding : 5,
11543         
11544         isApplied : false,
11545         
11546         isMasked : false,
11547         
11548         form : false,
11549         
11550         target : false,
11551         
11552         toolTip : false,
11553         
11554         intervalID : false,
11555         
11556         maskEl : false,
11557         
11558         apply : function()
11559         {
11560             if(this.isApplied){
11561                 return;
11562             }
11563             
11564             this.maskEl = {
11565                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11566                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11567                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11568                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11569             };
11570             
11571             this.maskEl.top.enableDisplayMode("block");
11572             this.maskEl.left.enableDisplayMode("block");
11573             this.maskEl.bottom.enableDisplayMode("block");
11574             this.maskEl.right.enableDisplayMode("block");
11575             
11576             this.toolTip = new Roo.bootstrap.Tooltip({
11577                 cls : 'roo-form-error-popover',
11578                 alignment : {
11579                     'left' : ['r-l', [-2,0], 'right'],
11580                     'right' : ['l-r', [2,0], 'left'],
11581                     'bottom' : ['tl-bl', [0,2], 'top'],
11582                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11583                 }
11584             });
11585             
11586             this.toolTip.render(Roo.get(document.body));
11587
11588             this.toolTip.el.enableDisplayMode("block");
11589             
11590             Roo.get(document.body).on('click', function(){
11591                 this.unmask();
11592             }, this);
11593             
11594             Roo.get(document.body).on('touchstart', function(){
11595                 this.unmask();
11596             }, this);
11597             
11598             this.isApplied = true
11599         },
11600         
11601         mask : function(form, target)
11602         {
11603             this.form = form;
11604             
11605             this.target = target;
11606             
11607             if(!this.form.errorMask || !target.el){
11608                 return;
11609             }
11610             
11611             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11612             
11613             Roo.log(scrollable);
11614             
11615             var ot = this.target.el.calcOffsetsTo(scrollable);
11616             
11617             var scrollTo = ot[1] - this.form.maskOffset;
11618             
11619             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11620             
11621             scrollable.scrollTo('top', scrollTo);
11622             
11623             var box = this.target.el.getBox();
11624             Roo.log(box);
11625             var zIndex = Roo.bootstrap.Modal.zIndex++;
11626
11627             
11628             this.maskEl.top.setStyle('position', 'absolute');
11629             this.maskEl.top.setStyle('z-index', zIndex);
11630             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11631             this.maskEl.top.setLeft(0);
11632             this.maskEl.top.setTop(0);
11633             this.maskEl.top.show();
11634             
11635             this.maskEl.left.setStyle('position', 'absolute');
11636             this.maskEl.left.setStyle('z-index', zIndex);
11637             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11638             this.maskEl.left.setLeft(0);
11639             this.maskEl.left.setTop(box.y - this.padding);
11640             this.maskEl.left.show();
11641
11642             this.maskEl.bottom.setStyle('position', 'absolute');
11643             this.maskEl.bottom.setStyle('z-index', zIndex);
11644             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11645             this.maskEl.bottom.setLeft(0);
11646             this.maskEl.bottom.setTop(box.bottom + this.padding);
11647             this.maskEl.bottom.show();
11648
11649             this.maskEl.right.setStyle('position', 'absolute');
11650             this.maskEl.right.setStyle('z-index', zIndex);
11651             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11652             this.maskEl.right.setLeft(box.right + this.padding);
11653             this.maskEl.right.setTop(box.y - this.padding);
11654             this.maskEl.right.show();
11655
11656             this.toolTip.bindEl = this.target.el;
11657
11658             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11659
11660             var tip = this.target.blankText;
11661
11662             if(this.target.getValue() !== '' ) {
11663                 
11664                 if (this.target.invalidText.length) {
11665                     tip = this.target.invalidText;
11666                 } else if (this.target.regexText.length){
11667                     tip = this.target.regexText;
11668                 }
11669             }
11670
11671             this.toolTip.show(tip);
11672
11673             this.intervalID = window.setInterval(function() {
11674                 Roo.bootstrap.Form.popover.unmask();
11675             }, 10000);
11676
11677             window.onwheel = function(){ return false;};
11678             
11679             (function(){ this.isMasked = true; }).defer(500, this);
11680             
11681         },
11682         
11683         unmask : function()
11684         {
11685             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11686                 return;
11687             }
11688             
11689             this.maskEl.top.setStyle('position', 'absolute');
11690             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11691             this.maskEl.top.hide();
11692
11693             this.maskEl.left.setStyle('position', 'absolute');
11694             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11695             this.maskEl.left.hide();
11696
11697             this.maskEl.bottom.setStyle('position', 'absolute');
11698             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11699             this.maskEl.bottom.hide();
11700
11701             this.maskEl.right.setStyle('position', 'absolute');
11702             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11703             this.maskEl.right.hide();
11704             
11705             this.toolTip.hide();
11706             
11707             this.toolTip.el.hide();
11708             
11709             window.onwheel = function(){ return true;};
11710             
11711             if(this.intervalID){
11712                 window.clearInterval(this.intervalID);
11713                 this.intervalID = false;
11714             }
11715             
11716             this.isMasked = false;
11717             
11718         }
11719         
11720     }
11721     
11722 });
11723
11724 /*
11725  * Based on:
11726  * Ext JS Library 1.1.1
11727  * Copyright(c) 2006-2007, Ext JS, LLC.
11728  *
11729  * Originally Released Under LGPL - original licence link has changed is not relivant.
11730  *
11731  * Fork - LGPL
11732  * <script type="text/javascript">
11733  */
11734 /**
11735  * @class Roo.form.VTypes
11736  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11737  * @singleton
11738  */
11739 Roo.form.VTypes = function(){
11740     // closure these in so they are only created once.
11741     var alpha = /^[a-zA-Z_]+$/;
11742     var alphanum = /^[a-zA-Z0-9_]+$/;
11743     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11744     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11745
11746     // All these messages and functions are configurable
11747     return {
11748         /**
11749          * The function used to validate email addresses
11750          * @param {String} value The email address
11751          */
11752         'email' : function(v){
11753             return email.test(v);
11754         },
11755         /**
11756          * The error text to display when the email validation function returns false
11757          * @type String
11758          */
11759         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11760         /**
11761          * The keystroke filter mask to be applied on email input
11762          * @type RegExp
11763          */
11764         'emailMask' : /[a-z0-9_\.\-@]/i,
11765
11766         /**
11767          * The function used to validate URLs
11768          * @param {String} value The URL
11769          */
11770         'url' : function(v){
11771             return url.test(v);
11772         },
11773         /**
11774          * The error text to display when the url validation function returns false
11775          * @type String
11776          */
11777         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11778         
11779         /**
11780          * The function used to validate alpha values
11781          * @param {String} value The value
11782          */
11783         'alpha' : function(v){
11784             return alpha.test(v);
11785         },
11786         /**
11787          * The error text to display when the alpha validation function returns false
11788          * @type String
11789          */
11790         'alphaText' : 'This field should only contain letters and _',
11791         /**
11792          * The keystroke filter mask to be applied on alpha input
11793          * @type RegExp
11794          */
11795         'alphaMask' : /[a-z_]/i,
11796
11797         /**
11798          * The function used to validate alphanumeric values
11799          * @param {String} value The value
11800          */
11801         'alphanum' : function(v){
11802             return alphanum.test(v);
11803         },
11804         /**
11805          * The error text to display when the alphanumeric validation function returns false
11806          * @type String
11807          */
11808         'alphanumText' : 'This field should only contain letters, numbers and _',
11809         /**
11810          * The keystroke filter mask to be applied on alphanumeric input
11811          * @type RegExp
11812          */
11813         'alphanumMask' : /[a-z0-9_]/i
11814     };
11815 }();/*
11816  * - LGPL
11817  *
11818  * Input
11819  * 
11820  */
11821
11822 /**
11823  * @class Roo.bootstrap.Input
11824  * @extends Roo.bootstrap.Component
11825  * Bootstrap Input class
11826  * @cfg {Boolean} disabled is it disabled
11827  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11828  * @cfg {String} name name of the input
11829  * @cfg {string} fieldLabel - the label associated
11830  * @cfg {string} placeholder - placeholder to put in text.
11831  * @cfg {string}  before - input group add on before
11832  * @cfg {string} after - input group add on after
11833  * @cfg {string} size - (lg|sm) or leave empty..
11834  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11835  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11836  * @cfg {Number} md colspan out of 12 for computer-sized screens
11837  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11838  * @cfg {string} value default value of the input
11839  * @cfg {Number} labelWidth set the width of label 
11840  * @cfg {Number} labellg set the width of label (1-12)
11841  * @cfg {Number} labelmd set the width of label (1-12)
11842  * @cfg {Number} labelsm set the width of label (1-12)
11843  * @cfg {Number} labelxs set the width of label (1-12)
11844  * @cfg {String} labelAlign (top|left)
11845  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11846  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11847  * @cfg {String} indicatorpos (left|right) default left
11848  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11849  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11850  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11851
11852  * @cfg {String} align (left|center|right) Default left
11853  * @cfg {Boolean} forceFeedback (true|false) Default false
11854  * 
11855  * @constructor
11856  * Create a new Input
11857  * @param {Object} config The config object
11858  */
11859
11860 Roo.bootstrap.Input = function(config){
11861     
11862     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11863     
11864     this.addEvents({
11865         /**
11866          * @event focus
11867          * Fires when this field receives input focus.
11868          * @param {Roo.form.Field} this
11869          */
11870         focus : true,
11871         /**
11872          * @event blur
11873          * Fires when this field loses input focus.
11874          * @param {Roo.form.Field} this
11875          */
11876         blur : true,
11877         /**
11878          * @event specialkey
11879          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11880          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11881          * @param {Roo.form.Field} this
11882          * @param {Roo.EventObject} e The event object
11883          */
11884         specialkey : true,
11885         /**
11886          * @event change
11887          * Fires just before the field blurs if the field value has changed.
11888          * @param {Roo.form.Field} this
11889          * @param {Mixed} newValue The new value
11890          * @param {Mixed} oldValue The original value
11891          */
11892         change : true,
11893         /**
11894          * @event invalid
11895          * Fires after the field has been marked as invalid.
11896          * @param {Roo.form.Field} this
11897          * @param {String} msg The validation message
11898          */
11899         invalid : true,
11900         /**
11901          * @event valid
11902          * Fires after the field has been validated with no errors.
11903          * @param {Roo.form.Field} this
11904          */
11905         valid : true,
11906          /**
11907          * @event keyup
11908          * Fires after the key up
11909          * @param {Roo.form.Field} this
11910          * @param {Roo.EventObject}  e The event Object
11911          */
11912         keyup : true,
11913         /**
11914          * @event paste
11915          * Fires after the user pastes into input
11916          * @param {Roo.form.Field} this
11917          * @param {Roo.EventObject}  e The event Object
11918          */
11919         paste : true
11920     });
11921 };
11922
11923 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11924      /**
11925      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11926       automatic validation (defaults to "keyup").
11927      */
11928     validationEvent : "keyup",
11929      /**
11930      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11931      */
11932     validateOnBlur : true,
11933     /**
11934      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11935      */
11936     validationDelay : 250,
11937      /**
11938      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11939      */
11940     focusClass : "x-form-focus",  // not needed???
11941     
11942        
11943     /**
11944      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11945      */
11946     invalidClass : "has-warning",
11947     
11948     /**
11949      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11950      */
11951     validClass : "has-success",
11952     
11953     /**
11954      * @cfg {Boolean} hasFeedback (true|false) default true
11955      */
11956     hasFeedback : true,
11957     
11958     /**
11959      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11960      */
11961     invalidFeedbackClass : "glyphicon-warning-sign",
11962     
11963     /**
11964      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11965      */
11966     validFeedbackClass : "glyphicon-ok",
11967     
11968     /**
11969      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11970      */
11971     selectOnFocus : false,
11972     
11973      /**
11974      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11975      */
11976     maskRe : null,
11977        /**
11978      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11979      */
11980     vtype : null,
11981     
11982       /**
11983      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11984      */
11985     disableKeyFilter : false,
11986     
11987        /**
11988      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11989      */
11990     disabled : false,
11991      /**
11992      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11993      */
11994     allowBlank : true,
11995     /**
11996      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11997      */
11998     blankText : "Please complete this mandatory field",
11999     
12000      /**
12001      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12002      */
12003     minLength : 0,
12004     /**
12005      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12006      */
12007     maxLength : Number.MAX_VALUE,
12008     /**
12009      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12010      */
12011     minLengthText : "The minimum length for this field is {0}",
12012     /**
12013      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12014      */
12015     maxLengthText : "The maximum length for this field is {0}",
12016   
12017     
12018     /**
12019      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12020      * If available, this function will be called only after the basic validators all return true, and will be passed the
12021      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12022      */
12023     validator : null,
12024     /**
12025      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12026      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12027      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12028      */
12029     regex : null,
12030     /**
12031      * @cfg {String} regexText -- Depricated - use Invalid Text
12032      */
12033     regexText : "",
12034     
12035     /**
12036      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12037      */
12038     invalidText : "",
12039     
12040     
12041     
12042     autocomplete: false,
12043     
12044     
12045     fieldLabel : '',
12046     inputType : 'text',
12047     
12048     name : false,
12049     placeholder: false,
12050     before : false,
12051     after : false,
12052     size : false,
12053     hasFocus : false,
12054     preventMark: false,
12055     isFormField : true,
12056     value : '',
12057     labelWidth : 2,
12058     labelAlign : false,
12059     readOnly : false,
12060     align : false,
12061     formatedValue : false,
12062     forceFeedback : false,
12063     
12064     indicatorpos : 'left',
12065     
12066     labellg : 0,
12067     labelmd : 0,
12068     labelsm : 0,
12069     labelxs : 0,
12070     
12071     capture : '',
12072     accept : '',
12073     
12074     parentLabelAlign : function()
12075     {
12076         var parent = this;
12077         while (parent.parent()) {
12078             parent = parent.parent();
12079             if (typeof(parent.labelAlign) !='undefined') {
12080                 return parent.labelAlign;
12081             }
12082         }
12083         return 'left';
12084         
12085     },
12086     
12087     getAutoCreate : function()
12088     {
12089         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12090         
12091         var id = Roo.id();
12092         
12093         var cfg = {};
12094         
12095         if(this.inputType != 'hidden'){
12096             cfg.cls = 'form-group' //input-group
12097         }
12098         
12099         var input =  {
12100             tag: 'input',
12101             id : id,
12102             type : this.inputType,
12103             value : this.value,
12104             cls : 'form-control',
12105             placeholder : this.placeholder || '',
12106             autocomplete : this.autocomplete || 'new-password'
12107         };
12108         if (this.inputType == 'file') {
12109             input.style = 'overflow:hidden'; // why not in CSS?
12110         }
12111         
12112         if(this.capture.length){
12113             input.capture = this.capture;
12114         }
12115         
12116         if(this.accept.length){
12117             input.accept = this.accept + "/*";
12118         }
12119         
12120         if(this.align){
12121             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12122         }
12123         
12124         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12125             input.maxLength = this.maxLength;
12126         }
12127         
12128         if (this.disabled) {
12129             input.disabled=true;
12130         }
12131         
12132         if (this.readOnly) {
12133             input.readonly=true;
12134         }
12135         
12136         if (this.name) {
12137             input.name = this.name;
12138         }
12139         
12140         if (this.size) {
12141             input.cls += ' input-' + this.size;
12142         }
12143         
12144         var settings=this;
12145         ['xs','sm','md','lg'].map(function(size){
12146             if (settings[size]) {
12147                 cfg.cls += ' col-' + size + '-' + settings[size];
12148             }
12149         });
12150         
12151         var inputblock = input;
12152         
12153         var feedback = {
12154             tag: 'span',
12155             cls: 'glyphicon form-control-feedback'
12156         };
12157             
12158         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12159             
12160             inputblock = {
12161                 cls : 'has-feedback',
12162                 cn :  [
12163                     input,
12164                     feedback
12165                 ] 
12166             };  
12167         }
12168         
12169         if (this.before || this.after) {
12170             
12171             inputblock = {
12172                 cls : 'input-group',
12173                 cn :  [] 
12174             };
12175             
12176             if (this.before && typeof(this.before) == 'string') {
12177                 
12178                 inputblock.cn.push({
12179                     tag :'span',
12180                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12181                     html : this.before
12182                 });
12183             }
12184             if (this.before && typeof(this.before) == 'object') {
12185                 this.before = Roo.factory(this.before);
12186                 
12187                 inputblock.cn.push({
12188                     tag :'span',
12189                     cls : 'roo-input-before input-group-prepend   input-group-' +
12190                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12191                 });
12192             }
12193             
12194             inputblock.cn.push(input);
12195             
12196             if (this.after && typeof(this.after) == 'string') {
12197                 inputblock.cn.push({
12198                     tag :'span',
12199                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12200                     html : this.after
12201                 });
12202             }
12203             if (this.after && typeof(this.after) == 'object') {
12204                 this.after = Roo.factory(this.after);
12205                 
12206                 inputblock.cn.push({
12207                     tag :'span',
12208                     cls : 'roo-input-after input-group-append  input-group-' +
12209                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12210                 });
12211             }
12212             
12213             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12214                 inputblock.cls += ' has-feedback';
12215                 inputblock.cn.push(feedback);
12216             }
12217         };
12218         var indicator = {
12219             tag : 'i',
12220             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12221             tooltip : 'This field is required'
12222         };
12223         if (this.allowBlank ) {
12224             indicator.style = this.allowBlank ? ' display:none' : '';
12225         }
12226         if (align ==='left' && this.fieldLabel.length) {
12227             
12228             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12229             
12230             cfg.cn = [
12231                 indicator,
12232                 {
12233                     tag: 'label',
12234                     'for' :  id,
12235                     cls : 'control-label col-form-label',
12236                     html : this.fieldLabel
12237
12238                 },
12239                 {
12240                     cls : "", 
12241                     cn: [
12242                         inputblock
12243                     ]
12244                 }
12245             ];
12246             
12247             var labelCfg = cfg.cn[1];
12248             var contentCfg = cfg.cn[2];
12249             
12250             if(this.indicatorpos == 'right'){
12251                 cfg.cn = [
12252                     {
12253                         tag: 'label',
12254                         'for' :  id,
12255                         cls : 'control-label col-form-label',
12256                         cn : [
12257                             {
12258                                 tag : 'span',
12259                                 html : this.fieldLabel
12260                             },
12261                             indicator
12262                         ]
12263                     },
12264                     {
12265                         cls : "",
12266                         cn: [
12267                             inputblock
12268                         ]
12269                     }
12270
12271                 ];
12272                 
12273                 labelCfg = cfg.cn[0];
12274                 contentCfg = cfg.cn[1];
12275             
12276             }
12277             
12278             if(this.labelWidth > 12){
12279                 labelCfg.style = "width: " + this.labelWidth + 'px';
12280             }
12281             
12282             if(this.labelWidth < 13 && this.labelmd == 0){
12283                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12284             }
12285             
12286             if(this.labellg > 0){
12287                 labelCfg.cls += ' col-lg-' + this.labellg;
12288                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12289             }
12290             
12291             if(this.labelmd > 0){
12292                 labelCfg.cls += ' col-md-' + this.labelmd;
12293                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12294             }
12295             
12296             if(this.labelsm > 0){
12297                 labelCfg.cls += ' col-sm-' + this.labelsm;
12298                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12299             }
12300             
12301             if(this.labelxs > 0){
12302                 labelCfg.cls += ' col-xs-' + this.labelxs;
12303                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12304             }
12305             
12306             
12307         } else if ( this.fieldLabel.length) {
12308                 
12309             
12310             
12311             cfg.cn = [
12312                 {
12313                     tag : 'i',
12314                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12315                     tooltip : 'This field is required',
12316                     style : this.allowBlank ? ' display:none' : '' 
12317                 },
12318                 {
12319                     tag: 'label',
12320                    //cls : 'input-group-addon',
12321                     html : this.fieldLabel
12322
12323                 },
12324
12325                inputblock
12326
12327            ];
12328            
12329            if(this.indicatorpos == 'right'){
12330        
12331                 cfg.cn = [
12332                     {
12333                         tag: 'label',
12334                        //cls : 'input-group-addon',
12335                         html : this.fieldLabel
12336
12337                     },
12338                     {
12339                         tag : 'i',
12340                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12341                         tooltip : 'This field is required',
12342                         style : this.allowBlank ? ' display:none' : '' 
12343                     },
12344
12345                    inputblock
12346
12347                ];
12348
12349             }
12350
12351         } else {
12352             
12353             cfg.cn = [
12354
12355                     inputblock
12356
12357             ];
12358                 
12359                 
12360         };
12361         
12362         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12363            cfg.cls += ' navbar-form';
12364         }
12365         
12366         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12367             // on BS4 we do this only if not form 
12368             cfg.cls += ' navbar-form';
12369             cfg.tag = 'li';
12370         }
12371         
12372         return cfg;
12373         
12374     },
12375     /**
12376      * return the real input element.
12377      */
12378     inputEl: function ()
12379     {
12380         return this.el.select('input.form-control',true).first();
12381     },
12382     
12383     tooltipEl : function()
12384     {
12385         return this.inputEl();
12386     },
12387     
12388     indicatorEl : function()
12389     {
12390         if (Roo.bootstrap.version == 4) {
12391             return false; // not enabled in v4 yet.
12392         }
12393         
12394         var indicator = this.el.select('i.roo-required-indicator',true).first();
12395         
12396         if(!indicator){
12397             return false;
12398         }
12399         
12400         return indicator;
12401         
12402     },
12403     
12404     setDisabled : function(v)
12405     {
12406         var i  = this.inputEl().dom;
12407         if (!v) {
12408             i.removeAttribute('disabled');
12409             return;
12410             
12411         }
12412         i.setAttribute('disabled','true');
12413     },
12414     initEvents : function()
12415     {
12416           
12417         this.inputEl().on("keydown" , this.fireKey,  this);
12418         this.inputEl().on("focus", this.onFocus,  this);
12419         this.inputEl().on("blur", this.onBlur,  this);
12420         
12421         this.inputEl().relayEvent('keyup', this);
12422         this.inputEl().relayEvent('paste', this);
12423         
12424         this.indicator = this.indicatorEl();
12425         
12426         if(this.indicator){
12427             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12428         }
12429  
12430         // reference to original value for reset
12431         this.originalValue = this.getValue();
12432         //Roo.form.TextField.superclass.initEvents.call(this);
12433         if(this.validationEvent == 'keyup'){
12434             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12435             this.inputEl().on('keyup', this.filterValidation, this);
12436         }
12437         else if(this.validationEvent !== false){
12438             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12439         }
12440         
12441         if(this.selectOnFocus){
12442             this.on("focus", this.preFocus, this);
12443             
12444         }
12445         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12446             this.inputEl().on("keypress", this.filterKeys, this);
12447         } else {
12448             this.inputEl().relayEvent('keypress', this);
12449         }
12450        /* if(this.grow){
12451             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12452             this.el.on("click", this.autoSize,  this);
12453         }
12454         */
12455         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12456             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12457         }
12458         
12459         if (typeof(this.before) == 'object') {
12460             this.before.render(this.el.select('.roo-input-before',true).first());
12461         }
12462         if (typeof(this.after) == 'object') {
12463             this.after.render(this.el.select('.roo-input-after',true).first());
12464         }
12465         
12466         this.inputEl().on('change', this.onChange, this);
12467         
12468     },
12469     filterValidation : function(e){
12470         if(!e.isNavKeyPress()){
12471             this.validationTask.delay(this.validationDelay);
12472         }
12473     },
12474      /**
12475      * Validates the field value
12476      * @return {Boolean} True if the value is valid, else false
12477      */
12478     validate : function(){
12479         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12480         if(this.disabled || this.validateValue(this.getRawValue())){
12481             this.markValid();
12482             return true;
12483         }
12484         
12485         this.markInvalid();
12486         return false;
12487     },
12488     
12489     
12490     /**
12491      * Validates a value according to the field's validation rules and marks the field as invalid
12492      * if the validation fails
12493      * @param {Mixed} value The value to validate
12494      * @return {Boolean} True if the value is valid, else false
12495      */
12496     validateValue : function(value)
12497     {
12498         if(this.getVisibilityEl().hasClass('hidden')){
12499             return true;
12500         }
12501         
12502         if(value.length < 1)  { // if it's blank
12503             if(this.allowBlank){
12504                 return true;
12505             }
12506             return false;
12507         }
12508         
12509         if(value.length < this.minLength){
12510             return false;
12511         }
12512         if(value.length > this.maxLength){
12513             return false;
12514         }
12515         if(this.vtype){
12516             var vt = Roo.form.VTypes;
12517             if(!vt[this.vtype](value, this)){
12518                 return false;
12519             }
12520         }
12521         if(typeof this.validator == "function"){
12522             var msg = this.validator(value);
12523             if(msg !== true){
12524                 return false;
12525             }
12526             if (typeof(msg) == 'string') {
12527                 this.invalidText = msg;
12528             }
12529         }
12530         
12531         if(this.regex && !this.regex.test(value)){
12532             return false;
12533         }
12534         
12535         return true;
12536     },
12537     
12538      // private
12539     fireKey : function(e){
12540         //Roo.log('field ' + e.getKey());
12541         if(e.isNavKeyPress()){
12542             this.fireEvent("specialkey", this, e);
12543         }
12544     },
12545     focus : function (selectText){
12546         if(this.rendered){
12547             this.inputEl().focus();
12548             if(selectText === true){
12549                 this.inputEl().dom.select();
12550             }
12551         }
12552         return this;
12553     } ,
12554     
12555     onFocus : function(){
12556         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12557            // this.el.addClass(this.focusClass);
12558         }
12559         if(!this.hasFocus){
12560             this.hasFocus = true;
12561             this.startValue = this.getValue();
12562             this.fireEvent("focus", this);
12563         }
12564     },
12565     
12566     beforeBlur : Roo.emptyFn,
12567
12568     
12569     // private
12570     onBlur : function(){
12571         this.beforeBlur();
12572         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12573             //this.el.removeClass(this.focusClass);
12574         }
12575         this.hasFocus = false;
12576         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12577             this.validate();
12578         }
12579         var v = this.getValue();
12580         if(String(v) !== String(this.startValue)){
12581             this.fireEvent('change', this, v, this.startValue);
12582         }
12583         this.fireEvent("blur", this);
12584     },
12585     
12586     onChange : function(e)
12587     {
12588         var v = this.getValue();
12589         if(String(v) !== String(this.startValue)){
12590             this.fireEvent('change', this, v, this.startValue);
12591         }
12592         
12593     },
12594     
12595     /**
12596      * Resets the current field value to the originally loaded value and clears any validation messages
12597      */
12598     reset : function(){
12599         this.setValue(this.originalValue);
12600         this.validate();
12601     },
12602      /**
12603      * Returns the name of the field
12604      * @return {Mixed} name The name field
12605      */
12606     getName: function(){
12607         return this.name;
12608     },
12609      /**
12610      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12611      * @return {Mixed} value The field value
12612      */
12613     getValue : function(){
12614         
12615         var v = this.inputEl().getValue();
12616         
12617         return v;
12618     },
12619     /**
12620      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12621      * @return {Mixed} value The field value
12622      */
12623     getRawValue : function(){
12624         var v = this.inputEl().getValue();
12625         
12626         return v;
12627     },
12628     
12629     /**
12630      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12631      * @param {Mixed} value The value to set
12632      */
12633     setRawValue : function(v){
12634         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12635     },
12636     
12637     selectText : function(start, end){
12638         var v = this.getRawValue();
12639         if(v.length > 0){
12640             start = start === undefined ? 0 : start;
12641             end = end === undefined ? v.length : end;
12642             var d = this.inputEl().dom;
12643             if(d.setSelectionRange){
12644                 d.setSelectionRange(start, end);
12645             }else if(d.createTextRange){
12646                 var range = d.createTextRange();
12647                 range.moveStart("character", start);
12648                 range.moveEnd("character", v.length-end);
12649                 range.select();
12650             }
12651         }
12652     },
12653     
12654     /**
12655      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12656      * @param {Mixed} value The value to set
12657      */
12658     setValue : function(v){
12659         this.value = v;
12660         if(this.rendered){
12661             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12662             this.validate();
12663         }
12664     },
12665     
12666     /*
12667     processValue : function(value){
12668         if(this.stripCharsRe){
12669             var newValue = value.replace(this.stripCharsRe, '');
12670             if(newValue !== value){
12671                 this.setRawValue(newValue);
12672                 return newValue;
12673             }
12674         }
12675         return value;
12676     },
12677   */
12678     preFocus : function(){
12679         
12680         if(this.selectOnFocus){
12681             this.inputEl().dom.select();
12682         }
12683     },
12684     filterKeys : function(e){
12685         var k = e.getKey();
12686         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12687             return;
12688         }
12689         var c = e.getCharCode(), cc = String.fromCharCode(c);
12690         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12691             return;
12692         }
12693         if(!this.maskRe.test(cc)){
12694             e.stopEvent();
12695         }
12696     },
12697      /**
12698      * Clear any invalid styles/messages for this field
12699      */
12700     clearInvalid : function(){
12701         
12702         if(!this.el || this.preventMark){ // not rendered
12703             return;
12704         }
12705         
12706         
12707         this.el.removeClass([this.invalidClass, 'is-invalid']);
12708         
12709         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12710             
12711             var feedback = this.el.select('.form-control-feedback', true).first();
12712             
12713             if(feedback){
12714                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12715             }
12716             
12717         }
12718         
12719         if(this.indicator){
12720             this.indicator.removeClass('visible');
12721             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12722         }
12723         
12724         this.fireEvent('valid', this);
12725     },
12726     
12727      /**
12728      * Mark this field as valid
12729      */
12730     markValid : function()
12731     {
12732         if(!this.el  || this.preventMark){ // not rendered...
12733             return;
12734         }
12735         
12736         this.el.removeClass([this.invalidClass, this.validClass]);
12737         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12738
12739         var feedback = this.el.select('.form-control-feedback', true).first();
12740             
12741         if(feedback){
12742             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12743         }
12744         
12745         if(this.indicator){
12746             this.indicator.removeClass('visible');
12747             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12748         }
12749         
12750         if(this.disabled){
12751             return;
12752         }
12753         
12754            
12755         if(this.allowBlank && !this.getRawValue().length){
12756             return;
12757         }
12758         if (Roo.bootstrap.version == 3) {
12759             this.el.addClass(this.validClass);
12760         } else {
12761             this.inputEl().addClass('is-valid');
12762         }
12763
12764         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12765             
12766             var feedback = this.el.select('.form-control-feedback', true).first();
12767             
12768             if(feedback){
12769                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12770                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12771             }
12772             
12773         }
12774         
12775         this.fireEvent('valid', this);
12776     },
12777     
12778      /**
12779      * Mark this field as invalid
12780      * @param {String} msg The validation message
12781      */
12782     markInvalid : function(msg)
12783     {
12784         if(!this.el  || this.preventMark){ // not rendered
12785             return;
12786         }
12787         
12788         this.el.removeClass([this.invalidClass, this.validClass]);
12789         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12790         
12791         var feedback = this.el.select('.form-control-feedback', true).first();
12792             
12793         if(feedback){
12794             this.el.select('.form-control-feedback', true).first().removeClass(
12795                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12796         }
12797
12798         if(this.disabled){
12799             return;
12800         }
12801         
12802         if(this.allowBlank && !this.getRawValue().length){
12803             return;
12804         }
12805         
12806         if(this.indicator){
12807             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12808             this.indicator.addClass('visible');
12809         }
12810         if (Roo.bootstrap.version == 3) {
12811             this.el.addClass(this.invalidClass);
12812         } else {
12813             this.inputEl().addClass('is-invalid');
12814         }
12815         
12816         
12817         
12818         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12819             
12820             var feedback = this.el.select('.form-control-feedback', true).first();
12821             
12822             if(feedback){
12823                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12824                 
12825                 if(this.getValue().length || this.forceFeedback){
12826                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12827                 }
12828                 
12829             }
12830             
12831         }
12832         
12833         this.fireEvent('invalid', this, msg);
12834     },
12835     // private
12836     SafariOnKeyDown : function(event)
12837     {
12838         // this is a workaround for a password hang bug on chrome/ webkit.
12839         if (this.inputEl().dom.type != 'password') {
12840             return;
12841         }
12842         
12843         var isSelectAll = false;
12844         
12845         if(this.inputEl().dom.selectionEnd > 0){
12846             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12847         }
12848         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12849             event.preventDefault();
12850             this.setValue('');
12851             return;
12852         }
12853         
12854         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12855             
12856             event.preventDefault();
12857             // this is very hacky as keydown always get's upper case.
12858             //
12859             var cc = String.fromCharCode(event.getCharCode());
12860             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12861             
12862         }
12863     },
12864     adjustWidth : function(tag, w){
12865         tag = tag.toLowerCase();
12866         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12867             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12868                 if(tag == 'input'){
12869                     return w + 2;
12870                 }
12871                 if(tag == 'textarea'){
12872                     return w-2;
12873                 }
12874             }else if(Roo.isOpera){
12875                 if(tag == 'input'){
12876                     return w + 2;
12877                 }
12878                 if(tag == 'textarea'){
12879                     return w-2;
12880                 }
12881             }
12882         }
12883         return w;
12884     },
12885     
12886     setFieldLabel : function(v)
12887     {
12888         if(!this.rendered){
12889             return;
12890         }
12891         
12892         if(this.indicatorEl()){
12893             var ar = this.el.select('label > span',true);
12894             
12895             if (ar.elements.length) {
12896                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12897                 this.fieldLabel = v;
12898                 return;
12899             }
12900             
12901             var br = this.el.select('label',true);
12902             
12903             if(br.elements.length) {
12904                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12905                 this.fieldLabel = v;
12906                 return;
12907             }
12908             
12909             Roo.log('Cannot Found any of label > span || label in input');
12910             return;
12911         }
12912         
12913         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12914         this.fieldLabel = v;
12915         
12916         
12917     }
12918 });
12919
12920  
12921 /*
12922  * - LGPL
12923  *
12924  * Input
12925  * 
12926  */
12927
12928 /**
12929  * @class Roo.bootstrap.TextArea
12930  * @extends Roo.bootstrap.Input
12931  * Bootstrap TextArea class
12932  * @cfg {Number} cols Specifies the visible width of a text area
12933  * @cfg {Number} rows Specifies the visible number of lines in a text area
12934  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12935  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12936  * @cfg {string} html text
12937  * 
12938  * @constructor
12939  * Create a new TextArea
12940  * @param {Object} config The config object
12941  */
12942
12943 Roo.bootstrap.TextArea = function(config){
12944     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12945    
12946 };
12947
12948 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12949      
12950     cols : false,
12951     rows : 5,
12952     readOnly : false,
12953     warp : 'soft',
12954     resize : false,
12955     value: false,
12956     html: false,
12957     
12958     getAutoCreate : function(){
12959         
12960         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12961         
12962         var id = Roo.id();
12963         
12964         var cfg = {};
12965         
12966         if(this.inputType != 'hidden'){
12967             cfg.cls = 'form-group' //input-group
12968         }
12969         
12970         var input =  {
12971             tag: 'textarea',
12972             id : id,
12973             warp : this.warp,
12974             rows : this.rows,
12975             value : this.value || '',
12976             html: this.html || '',
12977             cls : 'form-control',
12978             placeholder : this.placeholder || '' 
12979             
12980         };
12981         
12982         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12983             input.maxLength = this.maxLength;
12984         }
12985         
12986         if(this.resize){
12987             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12988         }
12989         
12990         if(this.cols){
12991             input.cols = this.cols;
12992         }
12993         
12994         if (this.readOnly) {
12995             input.readonly = true;
12996         }
12997         
12998         if (this.name) {
12999             input.name = this.name;
13000         }
13001         
13002         if (this.size) {
13003             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13004         }
13005         
13006         var settings=this;
13007         ['xs','sm','md','lg'].map(function(size){
13008             if (settings[size]) {
13009                 cfg.cls += ' col-' + size + '-' + settings[size];
13010             }
13011         });
13012         
13013         var inputblock = input;
13014         
13015         if(this.hasFeedback && !this.allowBlank){
13016             
13017             var feedback = {
13018                 tag: 'span',
13019                 cls: 'glyphicon form-control-feedback'
13020             };
13021
13022             inputblock = {
13023                 cls : 'has-feedback',
13024                 cn :  [
13025                     input,
13026                     feedback
13027                 ] 
13028             };  
13029         }
13030         
13031         
13032         if (this.before || this.after) {
13033             
13034             inputblock = {
13035                 cls : 'input-group',
13036                 cn :  [] 
13037             };
13038             if (this.before) {
13039                 inputblock.cn.push({
13040                     tag :'span',
13041                     cls : 'input-group-addon',
13042                     html : this.before
13043                 });
13044             }
13045             
13046             inputblock.cn.push(input);
13047             
13048             if(this.hasFeedback && !this.allowBlank){
13049                 inputblock.cls += ' has-feedback';
13050                 inputblock.cn.push(feedback);
13051             }
13052             
13053             if (this.after) {
13054                 inputblock.cn.push({
13055                     tag :'span',
13056                     cls : 'input-group-addon',
13057                     html : this.after
13058                 });
13059             }
13060             
13061         }
13062         
13063         if (align ==='left' && this.fieldLabel.length) {
13064             cfg.cn = [
13065                 {
13066                     tag: 'label',
13067                     'for' :  id,
13068                     cls : 'control-label',
13069                     html : this.fieldLabel
13070                 },
13071                 {
13072                     cls : "",
13073                     cn: [
13074                         inputblock
13075                     ]
13076                 }
13077
13078             ];
13079             
13080             if(this.labelWidth > 12){
13081                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13082             }
13083
13084             if(this.labelWidth < 13 && this.labelmd == 0){
13085                 this.labelmd = this.labelWidth;
13086             }
13087
13088             if(this.labellg > 0){
13089                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13090                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13091             }
13092
13093             if(this.labelmd > 0){
13094                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13095                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13096             }
13097
13098             if(this.labelsm > 0){
13099                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13100                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13101             }
13102
13103             if(this.labelxs > 0){
13104                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13105                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13106             }
13107             
13108         } else if ( this.fieldLabel.length) {
13109             cfg.cn = [
13110
13111                {
13112                    tag: 'label',
13113                    //cls : 'input-group-addon',
13114                    html : this.fieldLabel
13115
13116                },
13117
13118                inputblock
13119
13120            ];
13121
13122         } else {
13123
13124             cfg.cn = [
13125
13126                 inputblock
13127
13128             ];
13129                 
13130         }
13131         
13132         if (this.disabled) {
13133             input.disabled=true;
13134         }
13135         
13136         return cfg;
13137         
13138     },
13139     /**
13140      * return the real textarea element.
13141      */
13142     inputEl: function ()
13143     {
13144         return this.el.select('textarea.form-control',true).first();
13145     },
13146     
13147     /**
13148      * Clear any invalid styles/messages for this field
13149      */
13150     clearInvalid : function()
13151     {
13152         
13153         if(!this.el || this.preventMark){ // not rendered
13154             return;
13155         }
13156         
13157         var label = this.el.select('label', true).first();
13158         var icon = this.el.select('i.fa-star', true).first();
13159         
13160         if(label && icon){
13161             icon.remove();
13162         }
13163         this.el.removeClass( this.validClass);
13164         this.inputEl().removeClass('is-invalid');
13165          
13166         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13167             
13168             var feedback = this.el.select('.form-control-feedback', true).first();
13169             
13170             if(feedback){
13171                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13172             }
13173             
13174         }
13175         
13176         this.fireEvent('valid', this);
13177     },
13178     
13179      /**
13180      * Mark this field as valid
13181      */
13182     markValid : function()
13183     {
13184         if(!this.el  || this.preventMark){ // not rendered
13185             return;
13186         }
13187         
13188         this.el.removeClass([this.invalidClass, this.validClass]);
13189         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13190         
13191         var feedback = this.el.select('.form-control-feedback', true).first();
13192             
13193         if(feedback){
13194             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13195         }
13196
13197         if(this.disabled || this.allowBlank){
13198             return;
13199         }
13200         
13201         var label = this.el.select('label', true).first();
13202         var icon = this.el.select('i.fa-star', true).first();
13203         
13204         if(label && icon){
13205             icon.remove();
13206         }
13207         if (Roo.bootstrap.version == 3) {
13208             this.el.addClass(this.validClass);
13209         } else {
13210             this.inputEl().addClass('is-valid');
13211         }
13212         
13213         
13214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13215             
13216             var feedback = this.el.select('.form-control-feedback', true).first();
13217             
13218             if(feedback){
13219                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13220                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13221             }
13222             
13223         }
13224         
13225         this.fireEvent('valid', this);
13226     },
13227     
13228      /**
13229      * Mark this field as invalid
13230      * @param {String} msg The validation message
13231      */
13232     markInvalid : function(msg)
13233     {
13234         if(!this.el  || this.preventMark){ // not rendered
13235             return;
13236         }
13237         
13238         this.el.removeClass([this.invalidClass, this.validClass]);
13239         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13240         
13241         var feedback = this.el.select('.form-control-feedback', true).first();
13242             
13243         if(feedback){
13244             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13245         }
13246
13247         if(this.disabled || this.allowBlank){
13248             return;
13249         }
13250         
13251         var label = this.el.select('label', true).first();
13252         var icon = this.el.select('i.fa-star', true).first();
13253         
13254         if(!this.getValue().length && label && !icon){
13255             this.el.createChild({
13256                 tag : 'i',
13257                 cls : 'text-danger fa fa-lg fa-star',
13258                 tooltip : 'This field is required',
13259                 style : 'margin-right:5px;'
13260             }, label, true);
13261         }
13262         
13263         if (Roo.bootstrap.version == 3) {
13264             this.el.addClass(this.invalidClass);
13265         } else {
13266             this.inputEl().addClass('is-invalid');
13267         }
13268         
13269         // fixme ... this may be depricated need to test..
13270         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13271             
13272             var feedback = this.el.select('.form-control-feedback', true).first();
13273             
13274             if(feedback){
13275                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13276                 
13277                 if(this.getValue().length || this.forceFeedback){
13278                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13279                 }
13280                 
13281             }
13282             
13283         }
13284         
13285         this.fireEvent('invalid', this, msg);
13286     }
13287 });
13288
13289  
13290 /*
13291  * - LGPL
13292  *
13293  * trigger field - base class for combo..
13294  * 
13295  */
13296  
13297 /**
13298  * @class Roo.bootstrap.TriggerField
13299  * @extends Roo.bootstrap.Input
13300  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13301  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13302  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13303  * for which you can provide a custom implementation.  For example:
13304  * <pre><code>
13305 var trigger = new Roo.bootstrap.TriggerField();
13306 trigger.onTriggerClick = myTriggerFn;
13307 trigger.applyTo('my-field');
13308 </code></pre>
13309  *
13310  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13311  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13312  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13313  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13314  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13315
13316  * @constructor
13317  * Create a new TriggerField.
13318  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13319  * to the base TextField)
13320  */
13321 Roo.bootstrap.TriggerField = function(config){
13322     this.mimicing = false;
13323     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13324 };
13325
13326 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13327     /**
13328      * @cfg {String} triggerClass A CSS class to apply to the trigger
13329      */
13330      /**
13331      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13332      */
13333     hideTrigger:false,
13334
13335     /**
13336      * @cfg {Boolean} removable (true|false) special filter default false
13337      */
13338     removable : false,
13339     
13340     /** @cfg {Boolean} grow @hide */
13341     /** @cfg {Number} growMin @hide */
13342     /** @cfg {Number} growMax @hide */
13343
13344     /**
13345      * @hide 
13346      * @method
13347      */
13348     autoSize: Roo.emptyFn,
13349     // private
13350     monitorTab : true,
13351     // private
13352     deferHeight : true,
13353
13354     
13355     actionMode : 'wrap',
13356     
13357     caret : false,
13358     
13359     
13360     getAutoCreate : function(){
13361        
13362         var align = this.labelAlign || this.parentLabelAlign();
13363         
13364         var id = Roo.id();
13365         
13366         var cfg = {
13367             cls: 'form-group' //input-group
13368         };
13369         
13370         
13371         var input =  {
13372             tag: 'input',
13373             id : id,
13374             type : this.inputType,
13375             cls : 'form-control',
13376             autocomplete: 'new-password',
13377             placeholder : this.placeholder || '' 
13378             
13379         };
13380         if (this.name) {
13381             input.name = this.name;
13382         }
13383         if (this.size) {
13384             input.cls += ' input-' + this.size;
13385         }
13386         
13387         if (this.disabled) {
13388             input.disabled=true;
13389         }
13390         
13391         var inputblock = input;
13392         
13393         if(this.hasFeedback && !this.allowBlank){
13394             
13395             var feedback = {
13396                 tag: 'span',
13397                 cls: 'glyphicon form-control-feedback'
13398             };
13399             
13400             if(this.removable && !this.editable  ){
13401                 inputblock = {
13402                     cls : 'has-feedback',
13403                     cn :  [
13404                         inputblock,
13405                         {
13406                             tag: 'button',
13407                             html : 'x',
13408                             cls : 'roo-combo-removable-btn close'
13409                         },
13410                         feedback
13411                     ] 
13412                 };
13413             } else {
13414                 inputblock = {
13415                     cls : 'has-feedback',
13416                     cn :  [
13417                         inputblock,
13418                         feedback
13419                     ] 
13420                 };
13421             }
13422
13423         } else {
13424             if(this.removable && !this.editable ){
13425                 inputblock = {
13426                     cls : 'roo-removable',
13427                     cn :  [
13428                         inputblock,
13429                         {
13430                             tag: 'button',
13431                             html : 'x',
13432                             cls : 'roo-combo-removable-btn close'
13433                         }
13434                     ] 
13435                 };
13436             }
13437         }
13438         
13439         if (this.before || this.after) {
13440             
13441             inputblock = {
13442                 cls : 'input-group',
13443                 cn :  [] 
13444             };
13445             if (this.before) {
13446                 inputblock.cn.push({
13447                     tag :'span',
13448                     cls : 'input-group-addon input-group-prepend input-group-text',
13449                     html : this.before
13450                 });
13451             }
13452             
13453             inputblock.cn.push(input);
13454             
13455             if(this.hasFeedback && !this.allowBlank){
13456                 inputblock.cls += ' has-feedback';
13457                 inputblock.cn.push(feedback);
13458             }
13459             
13460             if (this.after) {
13461                 inputblock.cn.push({
13462                     tag :'span',
13463                     cls : 'input-group-addon input-group-append input-group-text',
13464                     html : this.after
13465                 });
13466             }
13467             
13468         };
13469         
13470       
13471         
13472         var ibwrap = inputblock;
13473         
13474         if(this.multiple){
13475             ibwrap = {
13476                 tag: 'ul',
13477                 cls: 'roo-select2-choices',
13478                 cn:[
13479                     {
13480                         tag: 'li',
13481                         cls: 'roo-select2-search-field',
13482                         cn: [
13483
13484                             inputblock
13485                         ]
13486                     }
13487                 ]
13488             };
13489                 
13490         }
13491         
13492         var combobox = {
13493             cls: 'roo-select2-container input-group',
13494             cn: [
13495                  {
13496                     tag: 'input',
13497                     type : 'hidden',
13498                     cls: 'form-hidden-field'
13499                 },
13500                 ibwrap
13501             ]
13502         };
13503         
13504         if(!this.multiple && this.showToggleBtn){
13505             
13506             var caret = {
13507                         tag: 'span',
13508                         cls: 'caret'
13509              };
13510             if (this.caret != false) {
13511                 caret = {
13512                      tag: 'i',
13513                      cls: 'fa fa-' + this.caret
13514                 };
13515                 
13516             }
13517             
13518             combobox.cn.push({
13519                 tag :'span',
13520                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13521                 cn : [
13522                     Roo.bootstrap.version == 3 ? caret : '',
13523                     {
13524                         tag: 'span',
13525                         cls: 'combobox-clear',
13526                         cn  : [
13527                             {
13528                                 tag : 'i',
13529                                 cls: 'icon-remove'
13530                             }
13531                         ]
13532                     }
13533                 ]
13534
13535             })
13536         }
13537         
13538         if(this.multiple){
13539             combobox.cls += ' roo-select2-container-multi';
13540         }
13541          var indicator = {
13542             tag : 'i',
13543             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13544             tooltip : 'This field is required'
13545         };
13546         if (Roo.bootstrap.version == 4) {
13547             indicator = {
13548                 tag : 'i',
13549                 style : 'display:none'
13550             };
13551         }
13552         
13553         
13554         if (align ==='left' && this.fieldLabel.length) {
13555             
13556             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13557
13558             cfg.cn = [
13559                 indicator,
13560                 {
13561                     tag: 'label',
13562                     'for' :  id,
13563                     cls : 'control-label',
13564                     html : this.fieldLabel
13565
13566                 },
13567                 {
13568                     cls : "", 
13569                     cn: [
13570                         combobox
13571                     ]
13572                 }
13573
13574             ];
13575             
13576             var labelCfg = cfg.cn[1];
13577             var contentCfg = cfg.cn[2];
13578             
13579             if(this.indicatorpos == 'right'){
13580                 cfg.cn = [
13581                     {
13582                         tag: 'label',
13583                         'for' :  id,
13584                         cls : 'control-label',
13585                         cn : [
13586                             {
13587                                 tag : 'span',
13588                                 html : this.fieldLabel
13589                             },
13590                             indicator
13591                         ]
13592                     },
13593                     {
13594                         cls : "", 
13595                         cn: [
13596                             combobox
13597                         ]
13598                     }
13599
13600                 ];
13601                 
13602                 labelCfg = cfg.cn[0];
13603                 contentCfg = cfg.cn[1];
13604             }
13605             
13606             if(this.labelWidth > 12){
13607                 labelCfg.style = "width: " + this.labelWidth + 'px';
13608             }
13609             
13610             if(this.labelWidth < 13 && this.labelmd == 0){
13611                 this.labelmd = this.labelWidth;
13612             }
13613             
13614             if(this.labellg > 0){
13615                 labelCfg.cls += ' col-lg-' + this.labellg;
13616                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13617             }
13618             
13619             if(this.labelmd > 0){
13620                 labelCfg.cls += ' col-md-' + this.labelmd;
13621                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13622             }
13623             
13624             if(this.labelsm > 0){
13625                 labelCfg.cls += ' col-sm-' + this.labelsm;
13626                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13627             }
13628             
13629             if(this.labelxs > 0){
13630                 labelCfg.cls += ' col-xs-' + this.labelxs;
13631                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13632             }
13633             
13634         } else if ( this.fieldLabel.length) {
13635 //                Roo.log(" label");
13636             cfg.cn = [
13637                 indicator,
13638                {
13639                    tag: 'label',
13640                    //cls : 'input-group-addon',
13641                    html : this.fieldLabel
13642
13643                },
13644
13645                combobox
13646
13647             ];
13648             
13649             if(this.indicatorpos == 'right'){
13650                 
13651                 cfg.cn = [
13652                     {
13653                        tag: 'label',
13654                        cn : [
13655                            {
13656                                tag : 'span',
13657                                html : this.fieldLabel
13658                            },
13659                            indicator
13660                        ]
13661
13662                     },
13663                     combobox
13664
13665                 ];
13666
13667             }
13668
13669         } else {
13670             
13671 //                Roo.log(" no label && no align");
13672                 cfg = combobox
13673                      
13674                 
13675         }
13676         
13677         var settings=this;
13678         ['xs','sm','md','lg'].map(function(size){
13679             if (settings[size]) {
13680                 cfg.cls += ' col-' + size + '-' + settings[size];
13681             }
13682         });
13683         
13684         return cfg;
13685         
13686     },
13687     
13688     
13689     
13690     // private
13691     onResize : function(w, h){
13692 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13693 //        if(typeof w == 'number'){
13694 //            var x = w - this.trigger.getWidth();
13695 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13696 //            this.trigger.setStyle('left', x+'px');
13697 //        }
13698     },
13699
13700     // private
13701     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13702
13703     // private
13704     getResizeEl : function(){
13705         return this.inputEl();
13706     },
13707
13708     // private
13709     getPositionEl : function(){
13710         return this.inputEl();
13711     },
13712
13713     // private
13714     alignErrorIcon : function(){
13715         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13716     },
13717
13718     // private
13719     initEvents : function(){
13720         
13721         this.createList();
13722         
13723         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13724         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13725         if(!this.multiple && this.showToggleBtn){
13726             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13727             if(this.hideTrigger){
13728                 this.trigger.setDisplayed(false);
13729             }
13730             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13731         }
13732         
13733         if(this.multiple){
13734             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13735         }
13736         
13737         if(this.removable && !this.editable && !this.tickable){
13738             var close = this.closeTriggerEl();
13739             
13740             if(close){
13741                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13742                 close.on('click', this.removeBtnClick, this, close);
13743             }
13744         }
13745         
13746         //this.trigger.addClassOnOver('x-form-trigger-over');
13747         //this.trigger.addClassOnClick('x-form-trigger-click');
13748         
13749         //if(!this.width){
13750         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13751         //}
13752     },
13753     
13754     closeTriggerEl : function()
13755     {
13756         var close = this.el.select('.roo-combo-removable-btn', true).first();
13757         return close ? close : false;
13758     },
13759     
13760     removeBtnClick : function(e, h, el)
13761     {
13762         e.preventDefault();
13763         
13764         if(this.fireEvent("remove", this) !== false){
13765             this.reset();
13766             this.fireEvent("afterremove", this)
13767         }
13768     },
13769     
13770     createList : function()
13771     {
13772         this.list = Roo.get(document.body).createChild({
13773             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13774             cls: 'typeahead typeahead-long dropdown-menu shadow',
13775             style: 'display:none'
13776         });
13777         
13778         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13779         
13780     },
13781
13782     // private
13783     initTrigger : function(){
13784        
13785     },
13786
13787     // private
13788     onDestroy : function(){
13789         if(this.trigger){
13790             this.trigger.removeAllListeners();
13791           //  this.trigger.remove();
13792         }
13793         //if(this.wrap){
13794         //    this.wrap.remove();
13795         //}
13796         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13797     },
13798
13799     // private
13800     onFocus : function(){
13801         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13802         /*
13803         if(!this.mimicing){
13804             this.wrap.addClass('x-trigger-wrap-focus');
13805             this.mimicing = true;
13806             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13807             if(this.monitorTab){
13808                 this.el.on("keydown", this.checkTab, this);
13809             }
13810         }
13811         */
13812     },
13813
13814     // private
13815     checkTab : function(e){
13816         if(e.getKey() == e.TAB){
13817             this.triggerBlur();
13818         }
13819     },
13820
13821     // private
13822     onBlur : function(){
13823         // do nothing
13824     },
13825
13826     // private
13827     mimicBlur : function(e, t){
13828         /*
13829         if(!this.wrap.contains(t) && this.validateBlur()){
13830             this.triggerBlur();
13831         }
13832         */
13833     },
13834
13835     // private
13836     triggerBlur : function(){
13837         this.mimicing = false;
13838         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13839         if(this.monitorTab){
13840             this.el.un("keydown", this.checkTab, this);
13841         }
13842         //this.wrap.removeClass('x-trigger-wrap-focus');
13843         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13844     },
13845
13846     // private
13847     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13848     validateBlur : function(e, t){
13849         return true;
13850     },
13851
13852     // private
13853     onDisable : function(){
13854         this.inputEl().dom.disabled = true;
13855         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13856         //if(this.wrap){
13857         //    this.wrap.addClass('x-item-disabled');
13858         //}
13859     },
13860
13861     // private
13862     onEnable : function(){
13863         this.inputEl().dom.disabled = false;
13864         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13865         //if(this.wrap){
13866         //    this.el.removeClass('x-item-disabled');
13867         //}
13868     },
13869
13870     // private
13871     onShow : function(){
13872         var ae = this.getActionEl();
13873         
13874         if(ae){
13875             ae.dom.style.display = '';
13876             ae.dom.style.visibility = 'visible';
13877         }
13878     },
13879
13880     // private
13881     
13882     onHide : function(){
13883         var ae = this.getActionEl();
13884         ae.dom.style.display = 'none';
13885     },
13886
13887     /**
13888      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13889      * by an implementing function.
13890      * @method
13891      * @param {EventObject} e
13892      */
13893     onTriggerClick : Roo.emptyFn
13894 });
13895  
13896 /*
13897 * Licence: LGPL
13898 */
13899
13900 /**
13901  * @class Roo.bootstrap.CardUploader
13902  * @extends Roo.bootstrap.Button
13903  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13904  * @cfg {Number} errorTimeout default 3000
13905  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13906  * @cfg {Array}  html The button text.
13907
13908  *
13909  * @constructor
13910  * Create a new CardUploader
13911  * @param {Object} config The config object
13912  */
13913
13914 Roo.bootstrap.CardUploader = function(config){
13915     
13916  
13917     
13918     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13919     
13920     
13921     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13922         return r.data.id
13923      });
13924     
13925      this.addEvents({
13926          // raw events
13927         /**
13928          * @event preview
13929          * When a image is clicked on - and needs to display a slideshow or similar..
13930          * @param {Roo.bootstrap.Card} this
13931          * @param {Object} The image information data 
13932          *
13933          */
13934         'preview' : true,
13935          /**
13936          * @event download
13937          * When a the download link is clicked
13938          * @param {Roo.bootstrap.Card} this
13939          * @param {Object} The image information data  contains 
13940          */
13941         'download' : true
13942         
13943     });
13944 };
13945  
13946 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13947     
13948      
13949     errorTimeout : 3000,
13950      
13951     images : false,
13952    
13953     fileCollection : false,
13954     allowBlank : true,
13955     
13956     getAutoCreate : function()
13957     {
13958         
13959         var cfg =  {
13960             cls :'form-group' ,
13961             cn : [
13962                
13963                 {
13964                     tag: 'label',
13965                    //cls : 'input-group-addon',
13966                     html : this.fieldLabel
13967
13968                 },
13969
13970                 {
13971                     tag: 'input',
13972                     type : 'hidden',
13973                     name : this.name,
13974                     value : this.value,
13975                     cls : 'd-none  form-control'
13976                 },
13977                 
13978                 {
13979                     tag: 'input',
13980                     multiple : 'multiple',
13981                     type : 'file',
13982                     cls : 'd-none  roo-card-upload-selector'
13983                 },
13984                 
13985                 {
13986                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13987                 },
13988                 {
13989                     cls : 'card-columns roo-card-uploader-container'
13990                 }
13991
13992             ]
13993         };
13994            
13995          
13996         return cfg;
13997     },
13998     
13999     getChildContainer : function() /// what children are added to.
14000     {
14001         return this.containerEl;
14002     },
14003    
14004     getButtonContainer : function() /// what children are added to.
14005     {
14006         return this.el.select(".roo-card-uploader-button-container").first();
14007     },
14008    
14009     initEvents : function()
14010     {
14011         
14012         Roo.bootstrap.Input.prototype.initEvents.call(this);
14013         
14014         var t = this;
14015         this.addxtype({
14016             xns: Roo.bootstrap,
14017
14018             xtype : 'Button',
14019             container_method : 'getButtonContainer' ,            
14020             html :  this.html, // fix changable?
14021             cls : 'w-100 ',
14022             listeners : {
14023                 'click' : function(btn, e) {
14024                     t.onClick(e);
14025                 }
14026             }
14027         });
14028         
14029         
14030         
14031         
14032         this.urlAPI = (window.createObjectURL && window) || 
14033                                 (window.URL && URL.revokeObjectURL && URL) || 
14034                                 (window.webkitURL && webkitURL);
14035                         
14036          
14037          
14038          
14039         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14040         
14041         this.selectorEl.on('change', this.onFileSelected, this);
14042         if (this.images) {
14043             var t = this;
14044             this.images.forEach(function(img) {
14045                 t.addCard(img)
14046             });
14047             this.images = false;
14048         }
14049         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14050          
14051        
14052     },
14053     
14054    
14055     onClick : function(e)
14056     {
14057         e.preventDefault();
14058          
14059         this.selectorEl.dom.click();
14060          
14061     },
14062     
14063     onFileSelected : function(e)
14064     {
14065         e.preventDefault();
14066         
14067         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14068             return;
14069         }
14070         
14071         Roo.each(this.selectorEl.dom.files, function(file){    
14072             this.addFile(file);
14073         }, this);
14074          
14075     },
14076     
14077       
14078     
14079       
14080     
14081     addFile : function(file)
14082     {
14083            
14084         if(typeof(file) === 'string'){
14085             throw "Add file by name?"; // should not happen
14086             return;
14087         }
14088         
14089         if(!file || !this.urlAPI){
14090             return;
14091         }
14092         
14093         // file;
14094         // file.type;
14095         
14096         var _this = this;
14097         
14098         
14099         var url = _this.urlAPI.createObjectURL( file);
14100            
14101         this.addCard({
14102             id : Roo.bootstrap.CardUploader.ID--,
14103             is_uploaded : false,
14104             src : url,
14105             srcfile : file,
14106             title : file.name,
14107             mimetype : file.type,
14108             preview : false,
14109             is_deleted : 0
14110         });
14111         
14112     },
14113     
14114     /**
14115      * addCard - add an Attachment to the uploader
14116      * @param data - the data about the image to upload
14117      *
14118      * {
14119           id : 123
14120           title : "Title of file",
14121           is_uploaded : false,
14122           src : "http://.....",
14123           srcfile : { the File upload object },
14124           mimetype : file.type,
14125           preview : false,
14126           is_deleted : 0
14127           .. any other data...
14128         }
14129      *
14130      * 
14131     */
14132     
14133     addCard : function (data)
14134     {
14135         // hidden input element?
14136         // if the file is not an image...
14137         //then we need to use something other that and header_image
14138         var t = this;
14139         //   remove.....
14140         var footer = [
14141             {
14142                 xns : Roo.bootstrap,
14143                 xtype : 'CardFooter',
14144                  items: [
14145                     {
14146                         xns : Roo.bootstrap,
14147                         xtype : 'Element',
14148                         cls : 'd-flex',
14149                         items : [
14150                             
14151                             {
14152                                 xns : Roo.bootstrap,
14153                                 xtype : 'Button',
14154                                 html : String.format("<small>{0}</small>", data.title),
14155                                 cls : 'col-10 text-left',
14156                                 size: 'sm',
14157                                 weight: 'link',
14158                                 fa : 'download',
14159                                 listeners : {
14160                                     click : function() {
14161                                      
14162                                         t.fireEvent( "download", t, data );
14163                                     }
14164                                 }
14165                             },
14166                           
14167                             {
14168                                 xns : Roo.bootstrap,
14169                                 xtype : 'Button',
14170                                 style: 'max-height: 28px; ',
14171                                 size : 'sm',
14172                                 weight: 'danger',
14173                                 cls : 'col-2',
14174                                 fa : 'times',
14175                                 listeners : {
14176                                     click : function() {
14177                                         t.removeCard(data.id)
14178                                     }
14179                                 }
14180                             }
14181                         ]
14182                     }
14183                     
14184                 ] 
14185             }
14186             
14187         ];
14188         
14189         var cn = this.addxtype(
14190             {
14191                  
14192                 xns : Roo.bootstrap,
14193                 xtype : 'Card',
14194                 closeable : true,
14195                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14196                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14197                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14198                 data : data,
14199                 html : false,
14200                  
14201                 items : footer,
14202                 initEvents : function() {
14203                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14204                     var card = this;
14205                     this.imgEl = this.el.select('.card-img-top').first();
14206                     if (this.imgEl) {
14207                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14208                         this.imgEl.set({ 'pointer' : 'cursor' });
14209                                   
14210                     }
14211                     this.getCardFooter().addClass('p-1');
14212                     
14213                   
14214                 }
14215                 
14216             }
14217         );
14218         // dont' really need ot update items.
14219         // this.items.push(cn);
14220         this.fileCollection.add(cn);
14221         
14222         if (!data.srcfile) {
14223             this.updateInput();
14224             return;
14225         }
14226             
14227         var _t = this;
14228         var reader = new FileReader();
14229         reader.addEventListener("load", function() {  
14230             data.srcdata =  reader.result;
14231             _t.updateInput();
14232         });
14233         reader.readAsDataURL(data.srcfile);
14234         
14235         
14236         
14237     },
14238     removeCard : function(id)
14239     {
14240         
14241         var card  = this.fileCollection.get(id);
14242         card.data.is_deleted = 1;
14243         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14244         //this.fileCollection.remove(card);
14245         //this.items = this.items.filter(function(e) { return e != card });
14246         // dont' really need ot update items.
14247         card.el.dom.parentNode.removeChild(card.el.dom);
14248         this.updateInput();
14249
14250         
14251     },
14252     reset: function()
14253     {
14254         this.fileCollection.each(function(card) {
14255             if (card.el.dom && card.el.dom.parentNode) {
14256                 card.el.dom.parentNode.removeChild(card.el.dom);
14257             }
14258         });
14259         this.fileCollection.clear();
14260         this.updateInput();
14261     },
14262     
14263     updateInput : function()
14264     {
14265          var data = [];
14266         this.fileCollection.each(function(e) {
14267             data.push(e.data);
14268             
14269         });
14270         this.inputEl().dom.value = JSON.stringify(data);
14271         
14272         
14273         
14274     }
14275     
14276     
14277 });
14278
14279
14280 Roo.bootstrap.CardUploader.ID = -1;/*
14281  * Based on:
14282  * Ext JS Library 1.1.1
14283  * Copyright(c) 2006-2007, Ext JS, LLC.
14284  *
14285  * Originally Released Under LGPL - original licence link has changed is not relivant.
14286  *
14287  * Fork - LGPL
14288  * <script type="text/javascript">
14289  */
14290
14291
14292 /**
14293  * @class Roo.data.SortTypes
14294  * @singleton
14295  * Defines the default sorting (casting?) comparison functions used when sorting data.
14296  */
14297 Roo.data.SortTypes = {
14298     /**
14299      * Default sort that does nothing
14300      * @param {Mixed} s The value being converted
14301      * @return {Mixed} The comparison value
14302      */
14303     none : function(s){
14304         return s;
14305     },
14306     
14307     /**
14308      * The regular expression used to strip tags
14309      * @type {RegExp}
14310      * @property
14311      */
14312     stripTagsRE : /<\/?[^>]+>/gi,
14313     
14314     /**
14315      * Strips all HTML tags to sort on text only
14316      * @param {Mixed} s The value being converted
14317      * @return {String} The comparison value
14318      */
14319     asText : function(s){
14320         return String(s).replace(this.stripTagsRE, "");
14321     },
14322     
14323     /**
14324      * Strips all HTML tags to sort on text only - Case insensitive
14325      * @param {Mixed} s The value being converted
14326      * @return {String} The comparison value
14327      */
14328     asUCText : function(s){
14329         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14330     },
14331     
14332     /**
14333      * Case insensitive string
14334      * @param {Mixed} s The value being converted
14335      * @return {String} The comparison value
14336      */
14337     asUCString : function(s) {
14338         return String(s).toUpperCase();
14339     },
14340     
14341     /**
14342      * Date sorting
14343      * @param {Mixed} s The value being converted
14344      * @return {Number} The comparison value
14345      */
14346     asDate : function(s) {
14347         if(!s){
14348             return 0;
14349         }
14350         if(s instanceof Date){
14351             return s.getTime();
14352         }
14353         return Date.parse(String(s));
14354     },
14355     
14356     /**
14357      * Float sorting
14358      * @param {Mixed} s The value being converted
14359      * @return {Float} The comparison value
14360      */
14361     asFloat : function(s) {
14362         var val = parseFloat(String(s).replace(/,/g, ""));
14363         if(isNaN(val)) {
14364             val = 0;
14365         }
14366         return val;
14367     },
14368     
14369     /**
14370      * Integer sorting
14371      * @param {Mixed} s The value being converted
14372      * @return {Number} The comparison value
14373      */
14374     asInt : function(s) {
14375         var val = parseInt(String(s).replace(/,/g, ""));
14376         if(isNaN(val)) {
14377             val = 0;
14378         }
14379         return val;
14380     }
14381 };/*
14382  * Based on:
14383  * Ext JS Library 1.1.1
14384  * Copyright(c) 2006-2007, Ext JS, LLC.
14385  *
14386  * Originally Released Under LGPL - original licence link has changed is not relivant.
14387  *
14388  * Fork - LGPL
14389  * <script type="text/javascript">
14390  */
14391
14392 /**
14393 * @class Roo.data.Record
14394  * Instances of this class encapsulate both record <em>definition</em> information, and record
14395  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14396  * to access Records cached in an {@link Roo.data.Store} object.<br>
14397  * <p>
14398  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14399  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14400  * objects.<br>
14401  * <p>
14402  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14403  * @constructor
14404  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14405  * {@link #create}. The parameters are the same.
14406  * @param {Array} data An associative Array of data values keyed by the field name.
14407  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14408  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14409  * not specified an integer id is generated.
14410  */
14411 Roo.data.Record = function(data, id){
14412     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14413     this.data = data;
14414 };
14415
14416 /**
14417  * Generate a constructor for a specific record layout.
14418  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14419  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14420  * Each field definition object may contain the following properties: <ul>
14421  * <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,
14422  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14423  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14424  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14425  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14426  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14427  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14428  * this may be omitted.</p></li>
14429  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14430  * <ul><li>auto (Default, implies no conversion)</li>
14431  * <li>string</li>
14432  * <li>int</li>
14433  * <li>float</li>
14434  * <li>boolean</li>
14435  * <li>date</li></ul></p></li>
14436  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14437  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14438  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14439  * by the Reader into an object that will be stored in the Record. It is passed the
14440  * following parameters:<ul>
14441  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14442  * </ul></p></li>
14443  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14444  * </ul>
14445  * <br>usage:<br><pre><code>
14446 var TopicRecord = Roo.data.Record.create(
14447     {name: 'title', mapping: 'topic_title'},
14448     {name: 'author', mapping: 'username'},
14449     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14450     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14451     {name: 'lastPoster', mapping: 'user2'},
14452     {name: 'excerpt', mapping: 'post_text'}
14453 );
14454
14455 var myNewRecord = new TopicRecord({
14456     title: 'Do my job please',
14457     author: 'noobie',
14458     totalPosts: 1,
14459     lastPost: new Date(),
14460     lastPoster: 'Animal',
14461     excerpt: 'No way dude!'
14462 });
14463 myStore.add(myNewRecord);
14464 </code></pre>
14465  * @method create
14466  * @static
14467  */
14468 Roo.data.Record.create = function(o){
14469     var f = function(){
14470         f.superclass.constructor.apply(this, arguments);
14471     };
14472     Roo.extend(f, Roo.data.Record);
14473     var p = f.prototype;
14474     p.fields = new Roo.util.MixedCollection(false, function(field){
14475         return field.name;
14476     });
14477     for(var i = 0, len = o.length; i < len; i++){
14478         p.fields.add(new Roo.data.Field(o[i]));
14479     }
14480     f.getField = function(name){
14481         return p.fields.get(name);  
14482     };
14483     return f;
14484 };
14485
14486 Roo.data.Record.AUTO_ID = 1000;
14487 Roo.data.Record.EDIT = 'edit';
14488 Roo.data.Record.REJECT = 'reject';
14489 Roo.data.Record.COMMIT = 'commit';
14490
14491 Roo.data.Record.prototype = {
14492     /**
14493      * Readonly flag - true if this record has been modified.
14494      * @type Boolean
14495      */
14496     dirty : false,
14497     editing : false,
14498     error: null,
14499     modified: null,
14500
14501     // private
14502     join : function(store){
14503         this.store = store;
14504     },
14505
14506     /**
14507      * Set the named field to the specified value.
14508      * @param {String} name The name of the field to set.
14509      * @param {Object} value The value to set the field to.
14510      */
14511     set : function(name, value){
14512         if(this.data[name] == value){
14513             return;
14514         }
14515         this.dirty = true;
14516         if(!this.modified){
14517             this.modified = {};
14518         }
14519         if(typeof this.modified[name] == 'undefined'){
14520             this.modified[name] = this.data[name];
14521         }
14522         this.data[name] = value;
14523         if(!this.editing && this.store){
14524             this.store.afterEdit(this);
14525         }       
14526     },
14527
14528     /**
14529      * Get the value of the named field.
14530      * @param {String} name The name of the field to get the value of.
14531      * @return {Object} The value of the field.
14532      */
14533     get : function(name){
14534         return this.data[name]; 
14535     },
14536
14537     // private
14538     beginEdit : function(){
14539         this.editing = true;
14540         this.modified = {}; 
14541     },
14542
14543     // private
14544     cancelEdit : function(){
14545         this.editing = false;
14546         delete this.modified;
14547     },
14548
14549     // private
14550     endEdit : function(){
14551         this.editing = false;
14552         if(this.dirty && this.store){
14553             this.store.afterEdit(this);
14554         }
14555     },
14556
14557     /**
14558      * Usually called by the {@link Roo.data.Store} which owns the Record.
14559      * Rejects all changes made to the Record since either creation, or the last commit operation.
14560      * Modified fields are reverted to their original values.
14561      * <p>
14562      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14563      * of reject operations.
14564      */
14565     reject : function(){
14566         var m = this.modified;
14567         for(var n in m){
14568             if(typeof m[n] != "function"){
14569                 this.data[n] = m[n];
14570             }
14571         }
14572         this.dirty = false;
14573         delete this.modified;
14574         this.editing = false;
14575         if(this.store){
14576             this.store.afterReject(this);
14577         }
14578     },
14579
14580     /**
14581      * Usually called by the {@link Roo.data.Store} which owns the Record.
14582      * Commits all changes made to the Record since either creation, or the last commit operation.
14583      * <p>
14584      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14585      * of commit operations.
14586      */
14587     commit : function(){
14588         this.dirty = false;
14589         delete this.modified;
14590         this.editing = false;
14591         if(this.store){
14592             this.store.afterCommit(this);
14593         }
14594     },
14595
14596     // private
14597     hasError : function(){
14598         return this.error != null;
14599     },
14600
14601     // private
14602     clearError : function(){
14603         this.error = null;
14604     },
14605
14606     /**
14607      * Creates a copy of this record.
14608      * @param {String} id (optional) A new record id if you don't want to use this record's id
14609      * @return {Record}
14610      */
14611     copy : function(newId) {
14612         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14613     }
14614 };/*
14615  * Based on:
14616  * Ext JS Library 1.1.1
14617  * Copyright(c) 2006-2007, Ext JS, LLC.
14618  *
14619  * Originally Released Under LGPL - original licence link has changed is not relivant.
14620  *
14621  * Fork - LGPL
14622  * <script type="text/javascript">
14623  */
14624
14625
14626
14627 /**
14628  * @class Roo.data.Store
14629  * @extends Roo.util.Observable
14630  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14631  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14632  * <p>
14633  * 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
14634  * has no knowledge of the format of the data returned by the Proxy.<br>
14635  * <p>
14636  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14637  * instances from the data object. These records are cached and made available through accessor functions.
14638  * @constructor
14639  * Creates a new Store.
14640  * @param {Object} config A config object containing the objects needed for the Store to access data,
14641  * and read the data into Records.
14642  */
14643 Roo.data.Store = function(config){
14644     this.data = new Roo.util.MixedCollection(false);
14645     this.data.getKey = function(o){
14646         return o.id;
14647     };
14648     this.baseParams = {};
14649     // private
14650     this.paramNames = {
14651         "start" : "start",
14652         "limit" : "limit",
14653         "sort" : "sort",
14654         "dir" : "dir",
14655         "multisort" : "_multisort"
14656     };
14657
14658     if(config && config.data){
14659         this.inlineData = config.data;
14660         delete config.data;
14661     }
14662
14663     Roo.apply(this, config);
14664     
14665     if(this.reader){ // reader passed
14666         this.reader = Roo.factory(this.reader, Roo.data);
14667         this.reader.xmodule = this.xmodule || false;
14668         if(!this.recordType){
14669             this.recordType = this.reader.recordType;
14670         }
14671         if(this.reader.onMetaChange){
14672             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14673         }
14674     }
14675
14676     if(this.recordType){
14677         this.fields = this.recordType.prototype.fields;
14678     }
14679     this.modified = [];
14680
14681     this.addEvents({
14682         /**
14683          * @event datachanged
14684          * Fires when the data cache has changed, and a widget which is using this Store
14685          * as a Record cache should refresh its view.
14686          * @param {Store} this
14687          */
14688         datachanged : true,
14689         /**
14690          * @event metachange
14691          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14692          * @param {Store} this
14693          * @param {Object} meta The JSON metadata
14694          */
14695         metachange : true,
14696         /**
14697          * @event add
14698          * Fires when Records have been added to the Store
14699          * @param {Store} this
14700          * @param {Roo.data.Record[]} records The array of Records added
14701          * @param {Number} index The index at which the record(s) were added
14702          */
14703         add : true,
14704         /**
14705          * @event remove
14706          * Fires when a Record has been removed from the Store
14707          * @param {Store} this
14708          * @param {Roo.data.Record} record The Record that was removed
14709          * @param {Number} index The index at which the record was removed
14710          */
14711         remove : true,
14712         /**
14713          * @event update
14714          * Fires when a Record has been updated
14715          * @param {Store} this
14716          * @param {Roo.data.Record} record The Record that was updated
14717          * @param {String} operation The update operation being performed.  Value may be one of:
14718          * <pre><code>
14719  Roo.data.Record.EDIT
14720  Roo.data.Record.REJECT
14721  Roo.data.Record.COMMIT
14722          * </code></pre>
14723          */
14724         update : true,
14725         /**
14726          * @event clear
14727          * Fires when the data cache has been cleared.
14728          * @param {Store} this
14729          */
14730         clear : true,
14731         /**
14732          * @event beforeload
14733          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14734          * the load action will be canceled.
14735          * @param {Store} this
14736          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14737          */
14738         beforeload : true,
14739         /**
14740          * @event beforeloadadd
14741          * Fires after a new set of Records has been loaded.
14742          * @param {Store} this
14743          * @param {Roo.data.Record[]} records The Records that were loaded
14744          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14745          */
14746         beforeloadadd : true,
14747         /**
14748          * @event load
14749          * Fires after a new set of Records has been loaded, before they are added to the store.
14750          * @param {Store} this
14751          * @param {Roo.data.Record[]} records The Records that were loaded
14752          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14753          * @params {Object} return from reader
14754          */
14755         load : true,
14756         /**
14757          * @event loadexception
14758          * Fires if an exception occurs in the Proxy during loading.
14759          * Called with the signature of the Proxy's "loadexception" event.
14760          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14761          * 
14762          * @param {Proxy} 
14763          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14764          * @param {Object} load options 
14765          * @param {Object} jsonData from your request (normally this contains the Exception)
14766          */
14767         loadexception : true
14768     });
14769     
14770     if(this.proxy){
14771         this.proxy = Roo.factory(this.proxy, Roo.data);
14772         this.proxy.xmodule = this.xmodule || false;
14773         this.relayEvents(this.proxy,  ["loadexception"]);
14774     }
14775     this.sortToggle = {};
14776     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14777
14778     Roo.data.Store.superclass.constructor.call(this);
14779
14780     if(this.inlineData){
14781         this.loadData(this.inlineData);
14782         delete this.inlineData;
14783     }
14784 };
14785
14786 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14787      /**
14788     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14789     * without a remote query - used by combo/forms at present.
14790     */
14791     
14792     /**
14793     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14794     */
14795     /**
14796     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14797     */
14798     /**
14799     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14800     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14801     */
14802     /**
14803     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14804     * on any HTTP request
14805     */
14806     /**
14807     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14808     */
14809     /**
14810     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14811     */
14812     multiSort: false,
14813     /**
14814     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14815     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14816     */
14817     remoteSort : false,
14818
14819     /**
14820     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14821      * loaded or when a record is removed. (defaults to false).
14822     */
14823     pruneModifiedRecords : false,
14824
14825     // private
14826     lastOptions : null,
14827
14828     /**
14829      * Add Records to the Store and fires the add event.
14830      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14831      */
14832     add : function(records){
14833         records = [].concat(records);
14834         for(var i = 0, len = records.length; i < len; i++){
14835             records[i].join(this);
14836         }
14837         var index = this.data.length;
14838         this.data.addAll(records);
14839         this.fireEvent("add", this, records, index);
14840     },
14841
14842     /**
14843      * Remove a Record from the Store and fires the remove event.
14844      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14845      */
14846     remove : function(record){
14847         var index = this.data.indexOf(record);
14848         this.data.removeAt(index);
14849  
14850         if(this.pruneModifiedRecords){
14851             this.modified.remove(record);
14852         }
14853         this.fireEvent("remove", this, record, index);
14854     },
14855
14856     /**
14857      * Remove all Records from the Store and fires the clear event.
14858      */
14859     removeAll : function(){
14860         this.data.clear();
14861         if(this.pruneModifiedRecords){
14862             this.modified = [];
14863         }
14864         this.fireEvent("clear", this);
14865     },
14866
14867     /**
14868      * Inserts Records to the Store at the given index and fires the add event.
14869      * @param {Number} index The start index at which to insert the passed Records.
14870      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14871      */
14872     insert : function(index, records){
14873         records = [].concat(records);
14874         for(var i = 0, len = records.length; i < len; i++){
14875             this.data.insert(index, records[i]);
14876             records[i].join(this);
14877         }
14878         this.fireEvent("add", this, records, index);
14879     },
14880
14881     /**
14882      * Get the index within the cache of the passed Record.
14883      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14884      * @return {Number} The index of the passed Record. Returns -1 if not found.
14885      */
14886     indexOf : function(record){
14887         return this.data.indexOf(record);
14888     },
14889
14890     /**
14891      * Get the index within the cache of the Record with the passed id.
14892      * @param {String} id The id of the Record to find.
14893      * @return {Number} The index of the Record. Returns -1 if not found.
14894      */
14895     indexOfId : function(id){
14896         return this.data.indexOfKey(id);
14897     },
14898
14899     /**
14900      * Get the Record with the specified id.
14901      * @param {String} id The id of the Record to find.
14902      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14903      */
14904     getById : function(id){
14905         return this.data.key(id);
14906     },
14907
14908     /**
14909      * Get the Record at the specified index.
14910      * @param {Number} index The index of the Record to find.
14911      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14912      */
14913     getAt : function(index){
14914         return this.data.itemAt(index);
14915     },
14916
14917     /**
14918      * Returns a range of Records between specified indices.
14919      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14920      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14921      * @return {Roo.data.Record[]} An array of Records
14922      */
14923     getRange : function(start, end){
14924         return this.data.getRange(start, end);
14925     },
14926
14927     // private
14928     storeOptions : function(o){
14929         o = Roo.apply({}, o);
14930         delete o.callback;
14931         delete o.scope;
14932         this.lastOptions = o;
14933     },
14934
14935     /**
14936      * Loads the Record cache from the configured Proxy using the configured Reader.
14937      * <p>
14938      * If using remote paging, then the first load call must specify the <em>start</em>
14939      * and <em>limit</em> properties in the options.params property to establish the initial
14940      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14941      * <p>
14942      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14943      * and this call will return before the new data has been loaded. Perform any post-processing
14944      * in a callback function, or in a "load" event handler.</strong>
14945      * <p>
14946      * @param {Object} options An object containing properties which control loading options:<ul>
14947      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14948      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14949      * passed the following arguments:<ul>
14950      * <li>r : Roo.data.Record[]</li>
14951      * <li>options: Options object from the load call</li>
14952      * <li>success: Boolean success indicator</li></ul></li>
14953      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14954      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14955      * </ul>
14956      */
14957     load : function(options){
14958         options = options || {};
14959         if(this.fireEvent("beforeload", this, options) !== false){
14960             this.storeOptions(options);
14961             var p = Roo.apply(options.params || {}, this.baseParams);
14962             // if meta was not loaded from remote source.. try requesting it.
14963             if (!this.reader.metaFromRemote) {
14964                 p._requestMeta = 1;
14965             }
14966             if(this.sortInfo && this.remoteSort){
14967                 var pn = this.paramNames;
14968                 p[pn["sort"]] = this.sortInfo.field;
14969                 p[pn["dir"]] = this.sortInfo.direction;
14970             }
14971             if (this.multiSort) {
14972                 var pn = this.paramNames;
14973                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14974             }
14975             
14976             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14977         }
14978     },
14979
14980     /**
14981      * Reloads the Record cache from the configured Proxy using the configured Reader and
14982      * the options from the last load operation performed.
14983      * @param {Object} options (optional) An object containing properties which may override the options
14984      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14985      * the most recently used options are reused).
14986      */
14987     reload : function(options){
14988         this.load(Roo.applyIf(options||{}, this.lastOptions));
14989     },
14990
14991     // private
14992     // Called as a callback by the Reader during a load operation.
14993     loadRecords : function(o, options, success){
14994         if(!o || success === false){
14995             if(success !== false){
14996                 this.fireEvent("load", this, [], options, o);
14997             }
14998             if(options.callback){
14999                 options.callback.call(options.scope || this, [], options, false);
15000             }
15001             return;
15002         }
15003         // if data returned failure - throw an exception.
15004         if (o.success === false) {
15005             // show a message if no listener is registered.
15006             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15007                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15008             }
15009             // loadmask wil be hooked into this..
15010             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15011             return;
15012         }
15013         var r = o.records, t = o.totalRecords || r.length;
15014         
15015         this.fireEvent("beforeloadadd", this, r, options, o);
15016         
15017         if(!options || options.add !== true){
15018             if(this.pruneModifiedRecords){
15019                 this.modified = [];
15020             }
15021             for(var i = 0, len = r.length; i < len; i++){
15022                 r[i].join(this);
15023             }
15024             if(this.snapshot){
15025                 this.data = this.snapshot;
15026                 delete this.snapshot;
15027             }
15028             this.data.clear();
15029             this.data.addAll(r);
15030             this.totalLength = t;
15031             this.applySort();
15032             this.fireEvent("datachanged", this);
15033         }else{
15034             this.totalLength = Math.max(t, this.data.length+r.length);
15035             this.add(r);
15036         }
15037         
15038         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15039                 
15040             var e = new Roo.data.Record({});
15041
15042             e.set(this.parent.displayField, this.parent.emptyTitle);
15043             e.set(this.parent.valueField, '');
15044
15045             this.insert(0, e);
15046         }
15047             
15048         this.fireEvent("load", this, r, options, o);
15049         if(options.callback){
15050             options.callback.call(options.scope || this, r, options, true);
15051         }
15052     },
15053
15054
15055     /**
15056      * Loads data from a passed data block. A Reader which understands the format of the data
15057      * must have been configured in the constructor.
15058      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15059      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15060      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15061      */
15062     loadData : function(o, append){
15063         var r = this.reader.readRecords(o);
15064         this.loadRecords(r, {add: append}, true);
15065     },
15066     
15067      /**
15068      * using 'cn' the nested child reader read the child array into it's child stores.
15069      * @param {Object} rec The record with a 'children array
15070      */
15071     loadDataFromChildren : function(rec)
15072     {
15073         this.loadData(this.reader.toLoadData(rec));
15074     },
15075     
15076
15077     /**
15078      * Gets the number of cached records.
15079      * <p>
15080      * <em>If using paging, this may not be the total size of the dataset. If the data object
15081      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15082      * the data set size</em>
15083      */
15084     getCount : function(){
15085         return this.data.length || 0;
15086     },
15087
15088     /**
15089      * Gets the total number of records in the dataset as returned by the server.
15090      * <p>
15091      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15092      * the dataset size</em>
15093      */
15094     getTotalCount : function(){
15095         return this.totalLength || 0;
15096     },
15097
15098     /**
15099      * Returns the sort state of the Store as an object with two properties:
15100      * <pre><code>
15101  field {String} The name of the field by which the Records are sorted
15102  direction {String} The sort order, "ASC" or "DESC"
15103      * </code></pre>
15104      */
15105     getSortState : function(){
15106         return this.sortInfo;
15107     },
15108
15109     // private
15110     applySort : function(){
15111         if(this.sortInfo && !this.remoteSort){
15112             var s = this.sortInfo, f = s.field;
15113             var st = this.fields.get(f).sortType;
15114             var fn = function(r1, r2){
15115                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15116                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15117             };
15118             this.data.sort(s.direction, fn);
15119             if(this.snapshot && this.snapshot != this.data){
15120                 this.snapshot.sort(s.direction, fn);
15121             }
15122         }
15123     },
15124
15125     /**
15126      * Sets the default sort column and order to be used by the next load operation.
15127      * @param {String} fieldName The name of the field to sort by.
15128      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15129      */
15130     setDefaultSort : function(field, dir){
15131         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15132     },
15133
15134     /**
15135      * Sort the Records.
15136      * If remote sorting is used, the sort is performed on the server, and the cache is
15137      * reloaded. If local sorting is used, the cache is sorted internally.
15138      * @param {String} fieldName The name of the field to sort by.
15139      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15140      */
15141     sort : function(fieldName, dir){
15142         var f = this.fields.get(fieldName);
15143         if(!dir){
15144             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15145             
15146             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15147                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15148             }else{
15149                 dir = f.sortDir;
15150             }
15151         }
15152         this.sortToggle[f.name] = dir;
15153         this.sortInfo = {field: f.name, direction: dir};
15154         if(!this.remoteSort){
15155             this.applySort();
15156             this.fireEvent("datachanged", this);
15157         }else{
15158             this.load(this.lastOptions);
15159         }
15160     },
15161
15162     /**
15163      * Calls the specified function for each of the Records in the cache.
15164      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15165      * Returning <em>false</em> aborts and exits the iteration.
15166      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15167      */
15168     each : function(fn, scope){
15169         this.data.each(fn, scope);
15170     },
15171
15172     /**
15173      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15174      * (e.g., during paging).
15175      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15176      */
15177     getModifiedRecords : function(){
15178         return this.modified;
15179     },
15180
15181     // private
15182     createFilterFn : function(property, value, anyMatch){
15183         if(!value.exec){ // not a regex
15184             value = String(value);
15185             if(value.length == 0){
15186                 return false;
15187             }
15188             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15189         }
15190         return function(r){
15191             return value.test(r.data[property]);
15192         };
15193     },
15194
15195     /**
15196      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15197      * @param {String} property A field on your records
15198      * @param {Number} start The record index to start at (defaults to 0)
15199      * @param {Number} end The last record index to include (defaults to length - 1)
15200      * @return {Number} The sum
15201      */
15202     sum : function(property, start, end){
15203         var rs = this.data.items, v = 0;
15204         start = start || 0;
15205         end = (end || end === 0) ? end : rs.length-1;
15206
15207         for(var i = start; i <= end; i++){
15208             v += (rs[i].data[property] || 0);
15209         }
15210         return v;
15211     },
15212
15213     /**
15214      * Filter the records by a specified property.
15215      * @param {String} field A field on your records
15216      * @param {String/RegExp} value Either a string that the field
15217      * should start with or a RegExp to test against the field
15218      * @param {Boolean} anyMatch True to match any part not just the beginning
15219      */
15220     filter : function(property, value, anyMatch){
15221         var fn = this.createFilterFn(property, value, anyMatch);
15222         return fn ? this.filterBy(fn) : this.clearFilter();
15223     },
15224
15225     /**
15226      * Filter by a function. The specified function will be called with each
15227      * record in this data source. If the function returns true the record is included,
15228      * otherwise it is filtered.
15229      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15230      * @param {Object} scope (optional) The scope of the function (defaults to this)
15231      */
15232     filterBy : function(fn, scope){
15233         this.snapshot = this.snapshot || this.data;
15234         this.data = this.queryBy(fn, scope||this);
15235         this.fireEvent("datachanged", this);
15236     },
15237
15238     /**
15239      * Query the records by a specified property.
15240      * @param {String} field A field on your records
15241      * @param {String/RegExp} value Either a string that the field
15242      * should start with or a RegExp to test against the field
15243      * @param {Boolean} anyMatch True to match any part not just the beginning
15244      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15245      */
15246     query : function(property, value, anyMatch){
15247         var fn = this.createFilterFn(property, value, anyMatch);
15248         return fn ? this.queryBy(fn) : this.data.clone();
15249     },
15250
15251     /**
15252      * Query by a function. The specified function will be called with each
15253      * record in this data source. If the function returns true the record is included
15254      * in the results.
15255      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15256      * @param {Object} scope (optional) The scope of the function (defaults to this)
15257       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15258      **/
15259     queryBy : function(fn, scope){
15260         var data = this.snapshot || this.data;
15261         return data.filterBy(fn, scope||this);
15262     },
15263
15264     /**
15265      * Collects unique values for a particular dataIndex from this store.
15266      * @param {String} dataIndex The property to collect
15267      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15268      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15269      * @return {Array} An array of the unique values
15270      **/
15271     collect : function(dataIndex, allowNull, bypassFilter){
15272         var d = (bypassFilter === true && this.snapshot) ?
15273                 this.snapshot.items : this.data.items;
15274         var v, sv, r = [], l = {};
15275         for(var i = 0, len = d.length; i < len; i++){
15276             v = d[i].data[dataIndex];
15277             sv = String(v);
15278             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15279                 l[sv] = true;
15280                 r[r.length] = v;
15281             }
15282         }
15283         return r;
15284     },
15285
15286     /**
15287      * Revert to a view of the Record cache with no filtering applied.
15288      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15289      */
15290     clearFilter : function(suppressEvent){
15291         if(this.snapshot && this.snapshot != this.data){
15292             this.data = this.snapshot;
15293             delete this.snapshot;
15294             if(suppressEvent !== true){
15295                 this.fireEvent("datachanged", this);
15296             }
15297         }
15298     },
15299
15300     // private
15301     afterEdit : function(record){
15302         if(this.modified.indexOf(record) == -1){
15303             this.modified.push(record);
15304         }
15305         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15306     },
15307     
15308     // private
15309     afterReject : function(record){
15310         this.modified.remove(record);
15311         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15312     },
15313
15314     // private
15315     afterCommit : function(record){
15316         this.modified.remove(record);
15317         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15318     },
15319
15320     /**
15321      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15322      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15323      */
15324     commitChanges : function(){
15325         var m = this.modified.slice(0);
15326         this.modified = [];
15327         for(var i = 0, len = m.length; i < len; i++){
15328             m[i].commit();
15329         }
15330     },
15331
15332     /**
15333      * Cancel outstanding changes on all changed records.
15334      */
15335     rejectChanges : function(){
15336         var m = this.modified.slice(0);
15337         this.modified = [];
15338         for(var i = 0, len = m.length; i < len; i++){
15339             m[i].reject();
15340         }
15341     },
15342
15343     onMetaChange : function(meta, rtype, o){
15344         this.recordType = rtype;
15345         this.fields = rtype.prototype.fields;
15346         delete this.snapshot;
15347         this.sortInfo = meta.sortInfo || this.sortInfo;
15348         this.modified = [];
15349         this.fireEvent('metachange', this, this.reader.meta);
15350     },
15351     
15352     moveIndex : function(data, type)
15353     {
15354         var index = this.indexOf(data);
15355         
15356         var newIndex = index + type;
15357         
15358         this.remove(data);
15359         
15360         this.insert(newIndex, data);
15361         
15362     }
15363 });/*
15364  * Based on:
15365  * Ext JS Library 1.1.1
15366  * Copyright(c) 2006-2007, Ext JS, LLC.
15367  *
15368  * Originally Released Under LGPL - original licence link has changed is not relivant.
15369  *
15370  * Fork - LGPL
15371  * <script type="text/javascript">
15372  */
15373
15374 /**
15375  * @class Roo.data.SimpleStore
15376  * @extends Roo.data.Store
15377  * Small helper class to make creating Stores from Array data easier.
15378  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15379  * @cfg {Array} fields An array of field definition objects, or field name strings.
15380  * @cfg {Object} an existing reader (eg. copied from another store)
15381  * @cfg {Array} data The multi-dimensional array of data
15382  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15383  * @cfg {Roo.data.Reader} reader  [not-required] 
15384  * @constructor
15385  * @param {Object} config
15386  */
15387 Roo.data.SimpleStore = function(config)
15388 {
15389     Roo.data.SimpleStore.superclass.constructor.call(this, {
15390         isLocal : true,
15391         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15392                 id: config.id
15393             },
15394             Roo.data.Record.create(config.fields)
15395         ),
15396         proxy : new Roo.data.MemoryProxy(config.data)
15397     });
15398     this.load();
15399 };
15400 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15401  * Based on:
15402  * Ext JS Library 1.1.1
15403  * Copyright(c) 2006-2007, Ext JS, LLC.
15404  *
15405  * Originally Released Under LGPL - original licence link has changed is not relivant.
15406  *
15407  * Fork - LGPL
15408  * <script type="text/javascript">
15409  */
15410
15411 /**
15412 /**
15413  * @extends Roo.data.Store
15414  * @class Roo.data.JsonStore
15415  * Small helper class to make creating Stores for JSON data easier. <br/>
15416 <pre><code>
15417 var store = new Roo.data.JsonStore({
15418     url: 'get-images.php',
15419     root: 'images',
15420     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15421 });
15422 </code></pre>
15423  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15424  * JsonReader and HttpProxy (unless inline data is provided).</b>
15425  * @cfg {Array} fields An array of field definition objects, or field name strings.
15426  * @constructor
15427  * @param {Object} config
15428  */
15429 Roo.data.JsonStore = function(c){
15430     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15431         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15432         reader: new Roo.data.JsonReader(c, c.fields)
15433     }));
15434 };
15435 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15436  * Based on:
15437  * Ext JS Library 1.1.1
15438  * Copyright(c) 2006-2007, Ext JS, LLC.
15439  *
15440  * Originally Released Under LGPL - original licence link has changed is not relivant.
15441  *
15442  * Fork - LGPL
15443  * <script type="text/javascript">
15444  */
15445
15446  
15447 Roo.data.Field = function(config){
15448     if(typeof config == "string"){
15449         config = {name: config};
15450     }
15451     Roo.apply(this, config);
15452     
15453     if(!this.type){
15454         this.type = "auto";
15455     }
15456     
15457     var st = Roo.data.SortTypes;
15458     // named sortTypes are supported, here we look them up
15459     if(typeof this.sortType == "string"){
15460         this.sortType = st[this.sortType];
15461     }
15462     
15463     // set default sortType for strings and dates
15464     if(!this.sortType){
15465         switch(this.type){
15466             case "string":
15467                 this.sortType = st.asUCString;
15468                 break;
15469             case "date":
15470                 this.sortType = st.asDate;
15471                 break;
15472             default:
15473                 this.sortType = st.none;
15474         }
15475     }
15476
15477     // define once
15478     var stripRe = /[\$,%]/g;
15479
15480     // prebuilt conversion function for this field, instead of
15481     // switching every time we're reading a value
15482     if(!this.convert){
15483         var cv, dateFormat = this.dateFormat;
15484         switch(this.type){
15485             case "":
15486             case "auto":
15487             case undefined:
15488                 cv = function(v){ return v; };
15489                 break;
15490             case "string":
15491                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15492                 break;
15493             case "int":
15494                 cv = function(v){
15495                     return v !== undefined && v !== null && v !== '' ?
15496                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15497                     };
15498                 break;
15499             case "float":
15500                 cv = function(v){
15501                     return v !== undefined && v !== null && v !== '' ?
15502                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15503                     };
15504                 break;
15505             case "bool":
15506             case "boolean":
15507                 cv = function(v){ return v === true || v === "true" || v == 1; };
15508                 break;
15509             case "date":
15510                 cv = function(v){
15511                     if(!v){
15512                         return '';
15513                     }
15514                     if(v instanceof Date){
15515                         return v;
15516                     }
15517                     if(dateFormat){
15518                         if(dateFormat == "timestamp"){
15519                             return new Date(v*1000);
15520                         }
15521                         return Date.parseDate(v, dateFormat);
15522                     }
15523                     var parsed = Date.parse(v);
15524                     return parsed ? new Date(parsed) : null;
15525                 };
15526              break;
15527             
15528         }
15529         this.convert = cv;
15530     }
15531 };
15532
15533 Roo.data.Field.prototype = {
15534     dateFormat: null,
15535     defaultValue: "",
15536     mapping: null,
15537     sortType : null,
15538     sortDir : "ASC"
15539 };/*
15540  * Based on:
15541  * Ext JS Library 1.1.1
15542  * Copyright(c) 2006-2007, Ext JS, LLC.
15543  *
15544  * Originally Released Under LGPL - original licence link has changed is not relivant.
15545  *
15546  * Fork - LGPL
15547  * <script type="text/javascript">
15548  */
15549  
15550 // Base class for reading structured data from a data source.  This class is intended to be
15551 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15552
15553 /**
15554  * @class Roo.data.DataReader
15555  * @abstract
15556  * Base class for reading structured data from a data source.  This class is intended to be
15557  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15558  */
15559
15560 Roo.data.DataReader = function(meta, recordType){
15561     
15562     this.meta = meta;
15563     
15564     this.recordType = recordType instanceof Array ? 
15565         Roo.data.Record.create(recordType) : recordType;
15566 };
15567
15568 Roo.data.DataReader.prototype = {
15569     
15570     
15571     readerType : 'Data',
15572      /**
15573      * Create an empty record
15574      * @param {Object} data (optional) - overlay some values
15575      * @return {Roo.data.Record} record created.
15576      */
15577     newRow :  function(d) {
15578         var da =  {};
15579         this.recordType.prototype.fields.each(function(c) {
15580             switch( c.type) {
15581                 case 'int' : da[c.name] = 0; break;
15582                 case 'date' : da[c.name] = new Date(); break;
15583                 case 'float' : da[c.name] = 0.0; break;
15584                 case 'boolean' : da[c.name] = false; break;
15585                 default : da[c.name] = ""; break;
15586             }
15587             
15588         });
15589         return new this.recordType(Roo.apply(da, d));
15590     }
15591     
15592     
15593 };/*
15594  * Based on:
15595  * Ext JS Library 1.1.1
15596  * Copyright(c) 2006-2007, Ext JS, LLC.
15597  *
15598  * Originally Released Under LGPL - original licence link has changed is not relivant.
15599  *
15600  * Fork - LGPL
15601  * <script type="text/javascript">
15602  */
15603
15604 /**
15605  * @class Roo.data.DataProxy
15606  * @extends Roo.data.Observable
15607  * @abstract
15608  * This class is an abstract base class for implementations which provide retrieval of
15609  * unformatted data objects.<br>
15610  * <p>
15611  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15612  * (of the appropriate type which knows how to parse the data object) to provide a block of
15613  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15614  * <p>
15615  * Custom implementations must implement the load method as described in
15616  * {@link Roo.data.HttpProxy#load}.
15617  */
15618 Roo.data.DataProxy = function(){
15619     this.addEvents({
15620         /**
15621          * @event beforeload
15622          * Fires before a network request is made to retrieve a data object.
15623          * @param {Object} This DataProxy object.
15624          * @param {Object} params The params parameter to the load function.
15625          */
15626         beforeload : true,
15627         /**
15628          * @event load
15629          * Fires before the load method's callback is called.
15630          * @param {Object} This DataProxy object.
15631          * @param {Object} o The data object.
15632          * @param {Object} arg The callback argument object passed to the load function.
15633          */
15634         load : true,
15635         /**
15636          * @event loadexception
15637          * Fires if an Exception occurs during data retrieval.
15638          * @param {Object} This DataProxy object.
15639          * @param {Object} o The data object.
15640          * @param {Object} arg The callback argument object passed to the load function.
15641          * @param {Object} e The Exception.
15642          */
15643         loadexception : true
15644     });
15645     Roo.data.DataProxy.superclass.constructor.call(this);
15646 };
15647
15648 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15649
15650     /**
15651      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15652      */
15653 /*
15654  * Based on:
15655  * Ext JS Library 1.1.1
15656  * Copyright(c) 2006-2007, Ext JS, LLC.
15657  *
15658  * Originally Released Under LGPL - original licence link has changed is not relivant.
15659  *
15660  * Fork - LGPL
15661  * <script type="text/javascript">
15662  */
15663 /**
15664  * @class Roo.data.MemoryProxy
15665  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15666  * to the Reader when its load method is called.
15667  * @constructor
15668  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15669  */
15670 Roo.data.MemoryProxy = function(data){
15671     if (data.data) {
15672         data = data.data;
15673     }
15674     Roo.data.MemoryProxy.superclass.constructor.call(this);
15675     this.data = data;
15676 };
15677
15678 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15679     
15680     /**
15681      * Load data from the requested source (in this case an in-memory
15682      * data object passed to the constructor), read the data object into
15683      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15684      * process that block using the passed callback.
15685      * @param {Object} params This parameter is not used by the MemoryProxy class.
15686      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15687      * object into a block of Roo.data.Records.
15688      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15689      * The function must be passed <ul>
15690      * <li>The Record block object</li>
15691      * <li>The "arg" argument from the load function</li>
15692      * <li>A boolean success indicator</li>
15693      * </ul>
15694      * @param {Object} scope The scope in which to call the callback
15695      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15696      */
15697     load : function(params, reader, callback, scope, arg){
15698         params = params || {};
15699         var result;
15700         try {
15701             result = reader.readRecords(params.data ? params.data :this.data);
15702         }catch(e){
15703             this.fireEvent("loadexception", this, arg, null, e);
15704             callback.call(scope, null, arg, false);
15705             return;
15706         }
15707         callback.call(scope, result, arg, true);
15708     },
15709     
15710     // private
15711     update : function(params, records){
15712         
15713     }
15714 });/*
15715  * Based on:
15716  * Ext JS Library 1.1.1
15717  * Copyright(c) 2006-2007, Ext JS, LLC.
15718  *
15719  * Originally Released Under LGPL - original licence link has changed is not relivant.
15720  *
15721  * Fork - LGPL
15722  * <script type="text/javascript">
15723  */
15724 /**
15725  * @class Roo.data.HttpProxy
15726  * @extends Roo.data.DataProxy
15727  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15728  * configured to reference a certain URL.<br><br>
15729  * <p>
15730  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15731  * from which the running page was served.<br><br>
15732  * <p>
15733  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15734  * <p>
15735  * Be aware that to enable the browser to parse an XML document, the server must set
15736  * the Content-Type header in the HTTP response to "text/xml".
15737  * @constructor
15738  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15739  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15740  * will be used to make the request.
15741  */
15742 Roo.data.HttpProxy = function(conn){
15743     Roo.data.HttpProxy.superclass.constructor.call(this);
15744     // is conn a conn config or a real conn?
15745     this.conn = conn;
15746     this.useAjax = !conn || !conn.events;
15747   
15748 };
15749
15750 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15751     // thse are take from connection...
15752     
15753     /**
15754      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15755      */
15756     /**
15757      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15758      * extra parameters to each request made by this object. (defaults to undefined)
15759      */
15760     /**
15761      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15762      *  to each request made by this object. (defaults to undefined)
15763      */
15764     /**
15765      * @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)
15766      */
15767     /**
15768      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15769      */
15770      /**
15771      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15772      * @type Boolean
15773      */
15774   
15775
15776     /**
15777      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15778      * @type Boolean
15779      */
15780     /**
15781      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15782      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15783      * a finer-grained basis than the DataProxy events.
15784      */
15785     getConnection : function(){
15786         return this.useAjax ? Roo.Ajax : this.conn;
15787     },
15788
15789     /**
15790      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15791      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15792      * process that block using the passed callback.
15793      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15794      * for the request to the remote server.
15795      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15796      * object into a block of Roo.data.Records.
15797      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15798      * The function must be passed <ul>
15799      * <li>The Record block object</li>
15800      * <li>The "arg" argument from the load function</li>
15801      * <li>A boolean success indicator</li>
15802      * </ul>
15803      * @param {Object} scope The scope in which to call the callback
15804      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15805      */
15806     load : function(params, reader, callback, scope, arg){
15807         if(this.fireEvent("beforeload", this, params) !== false){
15808             var  o = {
15809                 params : params || {},
15810                 request: {
15811                     callback : callback,
15812                     scope : scope,
15813                     arg : arg
15814                 },
15815                 reader: reader,
15816                 callback : this.loadResponse,
15817                 scope: this
15818             };
15819             if(this.useAjax){
15820                 Roo.applyIf(o, this.conn);
15821                 if(this.activeRequest){
15822                     Roo.Ajax.abort(this.activeRequest);
15823                 }
15824                 this.activeRequest = Roo.Ajax.request(o);
15825             }else{
15826                 this.conn.request(o);
15827             }
15828         }else{
15829             callback.call(scope||this, null, arg, false);
15830         }
15831     },
15832
15833     // private
15834     loadResponse : function(o, success, response){
15835         delete this.activeRequest;
15836         if(!success){
15837             this.fireEvent("loadexception", this, o, response);
15838             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15839             return;
15840         }
15841         var result;
15842         try {
15843             result = o.reader.read(response);
15844         }catch(e){
15845             this.fireEvent("loadexception", this, o, response, e);
15846             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15847             return;
15848         }
15849         
15850         this.fireEvent("load", this, o, o.request.arg);
15851         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15852     },
15853
15854     // private
15855     update : function(dataSet){
15856
15857     },
15858
15859     // private
15860     updateResponse : function(dataSet){
15861
15862     }
15863 });/*
15864  * Based on:
15865  * Ext JS Library 1.1.1
15866  * Copyright(c) 2006-2007, Ext JS, LLC.
15867  *
15868  * Originally Released Under LGPL - original licence link has changed is not relivant.
15869  *
15870  * Fork - LGPL
15871  * <script type="text/javascript">
15872  */
15873
15874 /**
15875  * @class Roo.data.ScriptTagProxy
15876  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15877  * other than the originating domain of the running page.<br><br>
15878  * <p>
15879  * <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
15880  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15881  * <p>
15882  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15883  * source code that is used as the source inside a &lt;script> tag.<br><br>
15884  * <p>
15885  * In order for the browser to process the returned data, the server must wrap the data object
15886  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15887  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15888  * depending on whether the callback name was passed:
15889  * <p>
15890  * <pre><code>
15891 boolean scriptTag = false;
15892 String cb = request.getParameter("callback");
15893 if (cb != null) {
15894     scriptTag = true;
15895     response.setContentType("text/javascript");
15896 } else {
15897     response.setContentType("application/x-json");
15898 }
15899 Writer out = response.getWriter();
15900 if (scriptTag) {
15901     out.write(cb + "(");
15902 }
15903 out.print(dataBlock.toJsonString());
15904 if (scriptTag) {
15905     out.write(");");
15906 }
15907 </pre></code>
15908  *
15909  * @constructor
15910  * @param {Object} config A configuration object.
15911  */
15912 Roo.data.ScriptTagProxy = function(config){
15913     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15914     Roo.apply(this, config);
15915     this.head = document.getElementsByTagName("head")[0];
15916 };
15917
15918 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15919
15920 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15921     /**
15922      * @cfg {String} url The URL from which to request the data object.
15923      */
15924     /**
15925      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15926      */
15927     timeout : 30000,
15928     /**
15929      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15930      * the server the name of the callback function set up by the load call to process the returned data object.
15931      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15932      * javascript output which calls this named function passing the data object as its only parameter.
15933      */
15934     callbackParam : "callback",
15935     /**
15936      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15937      * name to the request.
15938      */
15939     nocache : true,
15940
15941     /**
15942      * Load data from the configured URL, read the data object into
15943      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15944      * process that block using the passed callback.
15945      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15946      * for the request to the remote server.
15947      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15948      * object into a block of Roo.data.Records.
15949      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15950      * The function must be passed <ul>
15951      * <li>The Record block object</li>
15952      * <li>The "arg" argument from the load function</li>
15953      * <li>A boolean success indicator</li>
15954      * </ul>
15955      * @param {Object} scope The scope in which to call the callback
15956      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15957      */
15958     load : function(params, reader, callback, scope, arg){
15959         if(this.fireEvent("beforeload", this, params) !== false){
15960
15961             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15962
15963             var url = this.url;
15964             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15965             if(this.nocache){
15966                 url += "&_dc=" + (new Date().getTime());
15967             }
15968             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15969             var trans = {
15970                 id : transId,
15971                 cb : "stcCallback"+transId,
15972                 scriptId : "stcScript"+transId,
15973                 params : params,
15974                 arg : arg,
15975                 url : url,
15976                 callback : callback,
15977                 scope : scope,
15978                 reader : reader
15979             };
15980             var conn = this;
15981
15982             window[trans.cb] = function(o){
15983                 conn.handleResponse(o, trans);
15984             };
15985
15986             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15987
15988             if(this.autoAbort !== false){
15989                 this.abort();
15990             }
15991
15992             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15993
15994             var script = document.createElement("script");
15995             script.setAttribute("src", url);
15996             script.setAttribute("type", "text/javascript");
15997             script.setAttribute("id", trans.scriptId);
15998             this.head.appendChild(script);
15999
16000             this.trans = trans;
16001         }else{
16002             callback.call(scope||this, null, arg, false);
16003         }
16004     },
16005
16006     // private
16007     isLoading : function(){
16008         return this.trans ? true : false;
16009     },
16010
16011     /**
16012      * Abort the current server request.
16013      */
16014     abort : function(){
16015         if(this.isLoading()){
16016             this.destroyTrans(this.trans);
16017         }
16018     },
16019
16020     // private
16021     destroyTrans : function(trans, isLoaded){
16022         this.head.removeChild(document.getElementById(trans.scriptId));
16023         clearTimeout(trans.timeoutId);
16024         if(isLoaded){
16025             window[trans.cb] = undefined;
16026             try{
16027                 delete window[trans.cb];
16028             }catch(e){}
16029         }else{
16030             // if hasn't been loaded, wait for load to remove it to prevent script error
16031             window[trans.cb] = function(){
16032                 window[trans.cb] = undefined;
16033                 try{
16034                     delete window[trans.cb];
16035                 }catch(e){}
16036             };
16037         }
16038     },
16039
16040     // private
16041     handleResponse : function(o, trans){
16042         this.trans = false;
16043         this.destroyTrans(trans, true);
16044         var result;
16045         try {
16046             result = trans.reader.readRecords(o);
16047         }catch(e){
16048             this.fireEvent("loadexception", this, o, trans.arg, e);
16049             trans.callback.call(trans.scope||window, null, trans.arg, false);
16050             return;
16051         }
16052         this.fireEvent("load", this, o, trans.arg);
16053         trans.callback.call(trans.scope||window, result, trans.arg, true);
16054     },
16055
16056     // private
16057     handleFailure : function(trans){
16058         this.trans = false;
16059         this.destroyTrans(trans, false);
16060         this.fireEvent("loadexception", this, null, trans.arg);
16061         trans.callback.call(trans.scope||window, null, trans.arg, false);
16062     }
16063 });/*
16064  * Based on:
16065  * Ext JS Library 1.1.1
16066  * Copyright(c) 2006-2007, Ext JS, LLC.
16067  *
16068  * Originally Released Under LGPL - original licence link has changed is not relivant.
16069  *
16070  * Fork - LGPL
16071  * <script type="text/javascript">
16072  */
16073
16074 /**
16075  * @class Roo.data.JsonReader
16076  * @extends Roo.data.DataReader
16077  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16078  * based on mappings in a provided Roo.data.Record constructor.
16079  * 
16080  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16081  * in the reply previously. 
16082  * 
16083  * <p>
16084  * Example code:
16085  * <pre><code>
16086 var RecordDef = Roo.data.Record.create([
16087     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16088     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16089 ]);
16090 var myReader = new Roo.data.JsonReader({
16091     totalProperty: "results",    // The property which contains the total dataset size (optional)
16092     root: "rows",                // The property which contains an Array of row objects
16093     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16094 }, RecordDef);
16095 </code></pre>
16096  * <p>
16097  * This would consume a JSON file like this:
16098  * <pre><code>
16099 { 'results': 2, 'rows': [
16100     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16101     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16102 }
16103 </code></pre>
16104  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16105  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16106  * paged from the remote server.
16107  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16108  * @cfg {String} root name of the property which contains the Array of row objects.
16109  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16110  * @cfg {Array} fields Array of field definition objects
16111  * @constructor
16112  * Create a new JsonReader
16113  * @param {Object} meta Metadata configuration options
16114  * @param {Object} recordType Either an Array of field definition objects,
16115  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16116  */
16117 Roo.data.JsonReader = function(meta, recordType){
16118     
16119     meta = meta || {};
16120     // set some defaults:
16121     Roo.applyIf(meta, {
16122         totalProperty: 'total',
16123         successProperty : 'success',
16124         root : 'data',
16125         id : 'id'
16126     });
16127     
16128     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16129 };
16130 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16131     
16132     readerType : 'Json',
16133     
16134     /**
16135      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16136      * Used by Store query builder to append _requestMeta to params.
16137      * 
16138      */
16139     metaFromRemote : false,
16140     /**
16141      * This method is only used by a DataProxy which has retrieved data from a remote server.
16142      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16143      * @return {Object} data A data block which is used by an Roo.data.Store object as
16144      * a cache of Roo.data.Records.
16145      */
16146     read : function(response){
16147         var json = response.responseText;
16148        
16149         var o = /* eval:var:o */ eval("("+json+")");
16150         if(!o) {
16151             throw {message: "JsonReader.read: Json object not found"};
16152         }
16153         
16154         if(o.metaData){
16155             
16156             delete this.ef;
16157             this.metaFromRemote = true;
16158             this.meta = o.metaData;
16159             this.recordType = Roo.data.Record.create(o.metaData.fields);
16160             this.onMetaChange(this.meta, this.recordType, o);
16161         }
16162         return this.readRecords(o);
16163     },
16164
16165     // private function a store will implement
16166     onMetaChange : function(meta, recordType, o){
16167
16168     },
16169
16170     /**
16171          * @ignore
16172          */
16173     simpleAccess: function(obj, subsc) {
16174         return obj[subsc];
16175     },
16176
16177         /**
16178          * @ignore
16179          */
16180     getJsonAccessor: function(){
16181         var re = /[\[\.]/;
16182         return function(expr) {
16183             try {
16184                 return(re.test(expr))
16185                     ? new Function("obj", "return obj." + expr)
16186                     : function(obj){
16187                         return obj[expr];
16188                     };
16189             } catch(e){}
16190             return Roo.emptyFn;
16191         };
16192     }(),
16193
16194     /**
16195      * Create a data block containing Roo.data.Records from an XML document.
16196      * @param {Object} o An object which contains an Array of row objects in the property specified
16197      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16198      * which contains the total size of the dataset.
16199      * @return {Object} data A data block which is used by an Roo.data.Store object as
16200      * a cache of Roo.data.Records.
16201      */
16202     readRecords : function(o){
16203         /**
16204          * After any data loads, the raw JSON data is available for further custom processing.
16205          * @type Object
16206          */
16207         this.o = o;
16208         var s = this.meta, Record = this.recordType,
16209             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16210
16211 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16212         if (!this.ef) {
16213             if(s.totalProperty) {
16214                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16215                 }
16216                 if(s.successProperty) {
16217                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16218                 }
16219                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16220                 if (s.id) {
16221                         var g = this.getJsonAccessor(s.id);
16222                         this.getId = function(rec) {
16223                                 var r = g(rec);  
16224                                 return (r === undefined || r === "") ? null : r;
16225                         };
16226                 } else {
16227                         this.getId = function(){return null;};
16228                 }
16229             this.ef = [];
16230             for(var jj = 0; jj < fl; jj++){
16231                 f = fi[jj];
16232                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16233                 this.ef[jj] = this.getJsonAccessor(map);
16234             }
16235         }
16236
16237         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16238         if(s.totalProperty){
16239             var vt = parseInt(this.getTotal(o), 10);
16240             if(!isNaN(vt)){
16241                 totalRecords = vt;
16242             }
16243         }
16244         if(s.successProperty){
16245             var vs = this.getSuccess(o);
16246             if(vs === false || vs === 'false'){
16247                 success = false;
16248             }
16249         }
16250         var records = [];
16251         for(var i = 0; i < c; i++){
16252                 var n = root[i];
16253             var values = {};
16254             var id = this.getId(n);
16255             for(var j = 0; j < fl; j++){
16256                 f = fi[j];
16257             var v = this.ef[j](n);
16258             if (!f.convert) {
16259                 Roo.log('missing convert for ' + f.name);
16260                 Roo.log(f);
16261                 continue;
16262             }
16263             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16264             }
16265             var record = new Record(values, id);
16266             record.json = n;
16267             records[i] = record;
16268         }
16269         return {
16270             raw : o,
16271             success : success,
16272             records : records,
16273             totalRecords : totalRecords
16274         };
16275     },
16276     // used when loading children.. @see loadDataFromChildren
16277     toLoadData: function(rec)
16278     {
16279         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16280         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16281         return { data : data, total : data.length };
16282         
16283     }
16284 });/*
16285  * Based on:
16286  * Ext JS Library 1.1.1
16287  * Copyright(c) 2006-2007, Ext JS, LLC.
16288  *
16289  * Originally Released Under LGPL - original licence link has changed is not relivant.
16290  *
16291  * Fork - LGPL
16292  * <script type="text/javascript">
16293  */
16294
16295 /**
16296  * @class Roo.data.ArrayReader
16297  * @extends Roo.data.DataReader
16298  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16299  * Each element of that Array represents a row of data fields. The
16300  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16301  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16302  * <p>
16303  * Example code:.
16304  * <pre><code>
16305 var RecordDef = Roo.data.Record.create([
16306     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16307     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16308 ]);
16309 var myReader = new Roo.data.ArrayReader({
16310     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16311 }, RecordDef);
16312 </code></pre>
16313  * <p>
16314  * This would consume an Array like this:
16315  * <pre><code>
16316 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16317   </code></pre>
16318  
16319  * @constructor
16320  * Create a new JsonReader
16321  * @param {Object} meta Metadata configuration options.
16322  * @param {Object|Array} recordType Either an Array of field definition objects
16323  * 
16324  * @cfg {Array} fields Array of field definition objects
16325  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16326  * as specified to {@link Roo.data.Record#create},
16327  * or an {@link Roo.data.Record} object
16328  *
16329  * 
16330  * created using {@link Roo.data.Record#create}.
16331  */
16332 Roo.data.ArrayReader = function(meta, recordType)
16333 {    
16334     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16335 };
16336
16337 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16338     
16339       /**
16340      * Create a data block containing Roo.data.Records from an XML document.
16341      * @param {Object} o An Array of row objects which represents the dataset.
16342      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16343      * a cache of Roo.data.Records.
16344      */
16345     readRecords : function(o)
16346     {
16347         var sid = this.meta ? this.meta.id : null;
16348         var recordType = this.recordType, fields = recordType.prototype.fields;
16349         var records = [];
16350         var root = o;
16351         for(var i = 0; i < root.length; i++){
16352             var n = root[i];
16353             var values = {};
16354             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16355             for(var j = 0, jlen = fields.length; j < jlen; j++){
16356                 var f = fields.items[j];
16357                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16358                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16359                 v = f.convert(v);
16360                 values[f.name] = v;
16361             }
16362             var record = new recordType(values, id);
16363             record.json = n;
16364             records[records.length] = record;
16365         }
16366         return {
16367             records : records,
16368             totalRecords : records.length
16369         };
16370     },
16371     // used when loading children.. @see loadDataFromChildren
16372     toLoadData: function(rec)
16373     {
16374         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16375         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16376         
16377     }
16378     
16379     
16380 });/*
16381  * - LGPL
16382  * * 
16383  */
16384
16385 /**
16386  * @class Roo.bootstrap.ComboBox
16387  * @extends Roo.bootstrap.TriggerField
16388  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16389  * @cfg {Boolean} append (true|false) default false
16390  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16391  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16392  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16393  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16394  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16395  * @cfg {Boolean} animate default true
16396  * @cfg {Boolean} emptyResultText only for touch device
16397  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16398  * @cfg {String} emptyTitle default ''
16399  * @cfg {Number} width fixed with? experimental
16400  * @constructor
16401  * Create a new ComboBox.
16402  * @param {Object} config Configuration options
16403  */
16404 Roo.bootstrap.ComboBox = function(config){
16405     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16406     this.addEvents({
16407         /**
16408          * @event expand
16409          * Fires when the dropdown list is expanded
16410         * @param {Roo.bootstrap.ComboBox} combo This combo box
16411         */
16412         'expand' : true,
16413         /**
16414          * @event collapse
16415          * Fires when the dropdown list is collapsed
16416         * @param {Roo.bootstrap.ComboBox} combo This combo box
16417         */
16418         'collapse' : true,
16419         /**
16420          * @event beforeselect
16421          * Fires before a list item is selected. Return false to cancel the selection.
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         * @param {Roo.data.Record} record The data record returned from the underlying store
16424         * @param {Number} index The index of the selected item in the dropdown list
16425         */
16426         'beforeselect' : true,
16427         /**
16428          * @event select
16429          * Fires when a list item is selected
16430         * @param {Roo.bootstrap.ComboBox} combo This combo box
16431         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16432         * @param {Number} index The index of the selected item in the dropdown list
16433         */
16434         'select' : true,
16435         /**
16436          * @event beforequery
16437          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16438          * The event object passed has these properties:
16439         * @param {Roo.bootstrap.ComboBox} combo This combo box
16440         * @param {String} query The query
16441         * @param {Boolean} forceAll true to force "all" query
16442         * @param {Boolean} cancel true to cancel the query
16443         * @param {Object} e The query event object
16444         */
16445         'beforequery': true,
16446          /**
16447          * @event add
16448          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16449         * @param {Roo.bootstrap.ComboBox} combo This combo box
16450         */
16451         'add' : true,
16452         /**
16453          * @event edit
16454          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16455         * @param {Roo.bootstrap.ComboBox} combo This combo box
16456         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16457         */
16458         'edit' : true,
16459         /**
16460          * @event remove
16461          * Fires when the remove value from the combobox array
16462         * @param {Roo.bootstrap.ComboBox} combo This combo box
16463         */
16464         'remove' : true,
16465         /**
16466          * @event afterremove
16467          * Fires when the remove value from the combobox array
16468         * @param {Roo.bootstrap.ComboBox} combo This combo box
16469         */
16470         'afterremove' : true,
16471         /**
16472          * @event specialfilter
16473          * Fires when specialfilter
16474             * @param {Roo.bootstrap.ComboBox} combo This combo box
16475             */
16476         'specialfilter' : true,
16477         /**
16478          * @event tick
16479          * Fires when tick the element
16480             * @param {Roo.bootstrap.ComboBox} combo This combo box
16481             */
16482         'tick' : true,
16483         /**
16484          * @event touchviewdisplay
16485          * Fires when touch view require special display (default is using displayField)
16486             * @param {Roo.bootstrap.ComboBox} combo This combo box
16487             * @param {Object} cfg set html .
16488             */
16489         'touchviewdisplay' : true
16490         
16491     });
16492     
16493     this.item = [];
16494     this.tickItems = [];
16495     
16496     this.selectedIndex = -1;
16497     if(this.mode == 'local'){
16498         if(config.queryDelay === undefined){
16499             this.queryDelay = 10;
16500         }
16501         if(config.minChars === undefined){
16502             this.minChars = 0;
16503         }
16504     }
16505 };
16506
16507 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16508      
16509     /**
16510      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16511      * rendering into an Roo.Editor, defaults to false)
16512      */
16513     /**
16514      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16515      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16516      */
16517     /**
16518      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16519      */
16520     /**
16521      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16522      * the dropdown list (defaults to undefined, with no header element)
16523      */
16524
16525      /**
16526      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16527      */
16528      
16529      /**
16530      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16531      */
16532     listWidth: undefined,
16533     /**
16534      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16535      * mode = 'remote' or 'text' if mode = 'local')
16536      */
16537     displayField: undefined,
16538     
16539     /**
16540      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16541      * mode = 'remote' or 'value' if mode = 'local'). 
16542      * Note: use of a valueField requires the user make a selection
16543      * in order for a value to be mapped.
16544      */
16545     valueField: undefined,
16546     /**
16547      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16548      */
16549     modalTitle : '',
16550     
16551     /**
16552      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16553      * field's data value (defaults to the underlying DOM element's name)
16554      */
16555     hiddenName: undefined,
16556     /**
16557      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16558      */
16559     listClass: '',
16560     /**
16561      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16562      */
16563     selectedClass: 'active',
16564     
16565     /**
16566      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16567      */
16568     shadow:'sides',
16569     /**
16570      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16571      * anchor positions (defaults to 'tl-bl')
16572      */
16573     listAlign: 'tl-bl?',
16574     /**
16575      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16576      */
16577     maxHeight: 300,
16578     /**
16579      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16580      * query specified by the allQuery config option (defaults to 'query')
16581      */
16582     triggerAction: 'query',
16583     /**
16584      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16585      * (defaults to 4, does not apply if editable = false)
16586      */
16587     minChars : 4,
16588     /**
16589      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16590      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16591      */
16592     typeAhead: false,
16593     /**
16594      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16595      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16596      */
16597     queryDelay: 500,
16598     /**
16599      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16600      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16601      */
16602     pageSize: 0,
16603     /**
16604      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16605      * when editable = true (defaults to false)
16606      */
16607     selectOnFocus:false,
16608     /**
16609      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16610      */
16611     queryParam: 'query',
16612     /**
16613      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16614      * when mode = 'remote' (defaults to 'Loading...')
16615      */
16616     loadingText: 'Loading...',
16617     /**
16618      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16619      */
16620     resizable: false,
16621     /**
16622      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16623      */
16624     handleHeight : 8,
16625     /**
16626      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16627      * traditional select (defaults to true)
16628      */
16629     editable: true,
16630     /**
16631      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16632      */
16633     allQuery: '',
16634     /**
16635      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16636      */
16637     mode: 'remote',
16638     /**
16639      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16640      * listWidth has a higher value)
16641      */
16642     minListWidth : 70,
16643     /**
16644      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16645      * allow the user to set arbitrary text into the field (defaults to false)
16646      */
16647     forceSelection:false,
16648     /**
16649      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16650      * if typeAhead = true (defaults to 250)
16651      */
16652     typeAheadDelay : 250,
16653     /**
16654      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16655      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16656      */
16657     valueNotFoundText : undefined,
16658     /**
16659      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16660      */
16661     blockFocus : false,
16662     
16663     /**
16664      * @cfg {Boolean} disableClear Disable showing of clear button.
16665      */
16666     disableClear : false,
16667     /**
16668      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16669      */
16670     alwaysQuery : false,
16671     
16672     /**
16673      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16674      */
16675     multiple : false,
16676     
16677     /**
16678      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16679      */
16680     invalidClass : "has-warning",
16681     
16682     /**
16683      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16684      */
16685     validClass : "has-success",
16686     
16687     /**
16688      * @cfg {Boolean} specialFilter (true|false) special filter default false
16689      */
16690     specialFilter : false,
16691     
16692     /**
16693      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16694      */
16695     mobileTouchView : true,
16696     
16697     /**
16698      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16699      */
16700     useNativeIOS : false,
16701     
16702     /**
16703      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16704      */
16705     mobile_restrict_height : false,
16706     
16707     ios_options : false,
16708     
16709     //private
16710     addicon : false,
16711     editicon: false,
16712     
16713     page: 0,
16714     hasQuery: false,
16715     append: false,
16716     loadNext: false,
16717     autoFocus : true,
16718     tickable : false,
16719     btnPosition : 'right',
16720     triggerList : true,
16721     showToggleBtn : true,
16722     animate : true,
16723     emptyResultText: 'Empty',
16724     triggerText : 'Select',
16725     emptyTitle : '',
16726     width : false,
16727     
16728     // element that contains real text value.. (when hidden is used..)
16729     
16730     getAutoCreate : function()
16731     {   
16732         var cfg = false;
16733         //render
16734         /*
16735          * Render classic select for iso
16736          */
16737         
16738         if(Roo.isIOS && this.useNativeIOS){
16739             cfg = this.getAutoCreateNativeIOS();
16740             return cfg;
16741         }
16742         
16743         /*
16744          * Touch Devices
16745          */
16746         
16747         if(Roo.isTouch && this.mobileTouchView){
16748             cfg = this.getAutoCreateTouchView();
16749             return cfg;;
16750         }
16751         
16752         /*
16753          *  Normal ComboBox
16754          */
16755         if(!this.tickable){
16756             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16757             return cfg;
16758         }
16759         
16760         /*
16761          *  ComboBox with tickable selections
16762          */
16763              
16764         var align = this.labelAlign || this.parentLabelAlign();
16765         
16766         cfg = {
16767             cls : 'form-group roo-combobox-tickable' //input-group
16768         };
16769         
16770         var btn_text_select = '';
16771         var btn_text_done = '';
16772         var btn_text_cancel = '';
16773         
16774         if (this.btn_text_show) {
16775             btn_text_select = 'Select';
16776             btn_text_done = 'Done';
16777             btn_text_cancel = 'Cancel'; 
16778         }
16779         
16780         var buttons = {
16781             tag : 'div',
16782             cls : 'tickable-buttons',
16783             cn : [
16784                 {
16785                     tag : 'button',
16786                     type : 'button',
16787                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16788                     //html : this.triggerText
16789                     html: btn_text_select
16790                 },
16791                 {
16792                     tag : 'button',
16793                     type : 'button',
16794                     name : 'ok',
16795                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16796                     //html : 'Done'
16797                     html: btn_text_done
16798                 },
16799                 {
16800                     tag : 'button',
16801                     type : 'button',
16802                     name : 'cancel',
16803                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16804                     //html : 'Cancel'
16805                     html: btn_text_cancel
16806                 }
16807             ]
16808         };
16809         
16810         if(this.editable){
16811             buttons.cn.unshift({
16812                 tag: 'input',
16813                 cls: 'roo-select2-search-field-input'
16814             });
16815         }
16816         
16817         var _this = this;
16818         
16819         Roo.each(buttons.cn, function(c){
16820             if (_this.size) {
16821                 c.cls += ' btn-' + _this.size;
16822             }
16823
16824             if (_this.disabled) {
16825                 c.disabled = true;
16826             }
16827         });
16828         
16829         var box = {
16830             tag: 'div',
16831             style : 'display: contents',
16832             cn: [
16833                 {
16834                     tag: 'input',
16835                     type : 'hidden',
16836                     cls: 'form-hidden-field'
16837                 },
16838                 {
16839                     tag: 'ul',
16840                     cls: 'roo-select2-choices',
16841                     cn:[
16842                         {
16843                             tag: 'li',
16844                             cls: 'roo-select2-search-field',
16845                             cn: [
16846                                 buttons
16847                             ]
16848                         }
16849                     ]
16850                 }
16851             ]
16852         };
16853         
16854         var combobox = {
16855             cls: 'roo-select2-container input-group roo-select2-container-multi',
16856             cn: [
16857                 
16858                 box
16859 //                {
16860 //                    tag: 'ul',
16861 //                    cls: 'typeahead typeahead-long dropdown-menu',
16862 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16863 //                }
16864             ]
16865         };
16866         
16867         if(this.hasFeedback && !this.allowBlank){
16868             
16869             var feedback = {
16870                 tag: 'span',
16871                 cls: 'glyphicon form-control-feedback'
16872             };
16873
16874             combobox.cn.push(feedback);
16875         }
16876         
16877         
16878         
16879         var indicator = {
16880             tag : 'i',
16881             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16882             tooltip : 'This field is required'
16883         };
16884         if (Roo.bootstrap.version == 4) {
16885             indicator = {
16886                 tag : 'i',
16887                 style : 'display:none'
16888             };
16889         }
16890         if (align ==='left' && this.fieldLabel.length) {
16891             
16892             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16893             
16894             cfg.cn = [
16895                 indicator,
16896                 {
16897                     tag: 'label',
16898                     'for' :  id,
16899                     cls : 'control-label col-form-label',
16900                     html : this.fieldLabel
16901
16902                 },
16903                 {
16904                     cls : "", 
16905                     cn: [
16906                         combobox
16907                     ]
16908                 }
16909
16910             ];
16911             
16912             var labelCfg = cfg.cn[1];
16913             var contentCfg = cfg.cn[2];
16914             
16915
16916             if(this.indicatorpos == 'right'){
16917                 
16918                 cfg.cn = [
16919                     {
16920                         tag: 'label',
16921                         'for' :  id,
16922                         cls : 'control-label col-form-label',
16923                         cn : [
16924                             {
16925                                 tag : 'span',
16926                                 html : this.fieldLabel
16927                             },
16928                             indicator
16929                         ]
16930                     },
16931                     {
16932                         cls : "",
16933                         cn: [
16934                             combobox
16935                         ]
16936                     }
16937
16938                 ];
16939                 
16940                 
16941                 
16942                 labelCfg = cfg.cn[0];
16943                 contentCfg = cfg.cn[1];
16944             
16945             }
16946             
16947             if(this.labelWidth > 12){
16948                 labelCfg.style = "width: " + this.labelWidth + 'px';
16949             }
16950             if(this.width * 1 > 0){
16951                 contentCfg.style = "width: " + this.width + 'px';
16952             }
16953             if(this.labelWidth < 13 && this.labelmd == 0){
16954                 this.labelmd = this.labelWidth;
16955             }
16956             
16957             if(this.labellg > 0){
16958                 labelCfg.cls += ' col-lg-' + this.labellg;
16959                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16960             }
16961             
16962             if(this.labelmd > 0){
16963                 labelCfg.cls += ' col-md-' + this.labelmd;
16964                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16965             }
16966             
16967             if(this.labelsm > 0){
16968                 labelCfg.cls += ' col-sm-' + this.labelsm;
16969                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16970             }
16971             
16972             if(this.labelxs > 0){
16973                 labelCfg.cls += ' col-xs-' + this.labelxs;
16974                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16975             }
16976                 
16977                 
16978         } else if ( this.fieldLabel.length) {
16979 //                Roo.log(" label");
16980                  cfg.cn = [
16981                    indicator,
16982                     {
16983                         tag: 'label',
16984                         //cls : 'input-group-addon',
16985                         html : this.fieldLabel
16986                     },
16987                     combobox
16988                 ];
16989                 
16990                 if(this.indicatorpos == 'right'){
16991                     cfg.cn = [
16992                         {
16993                             tag: 'label',
16994                             //cls : 'input-group-addon',
16995                             html : this.fieldLabel
16996                         },
16997                         indicator,
16998                         combobox
16999                     ];
17000                     
17001                 }
17002
17003         } else {
17004             
17005 //                Roo.log(" no label && no align");
17006                 cfg = combobox
17007                      
17008                 
17009         }
17010          
17011         var settings=this;
17012         ['xs','sm','md','lg'].map(function(size){
17013             if (settings[size]) {
17014                 cfg.cls += ' col-' + size + '-' + settings[size];
17015             }
17016         });
17017         
17018         return cfg;
17019         
17020     },
17021     
17022     _initEventsCalled : false,
17023     
17024     // private
17025     initEvents: function()
17026     {   
17027         if (this._initEventsCalled) { // as we call render... prevent looping...
17028             return;
17029         }
17030         this._initEventsCalled = true;
17031         
17032         if (!this.store) {
17033             throw "can not find store for combo";
17034         }
17035         
17036         this.indicator = this.indicatorEl();
17037         
17038         this.store = Roo.factory(this.store, Roo.data);
17039         this.store.parent = this;
17040         
17041         // if we are building from html. then this element is so complex, that we can not really
17042         // use the rendered HTML.
17043         // so we have to trash and replace the previous code.
17044         if (Roo.XComponent.build_from_html) {
17045             // remove this element....
17046             var e = this.el.dom, k=0;
17047             while (e ) { e = e.previousSibling;  ++k;}
17048
17049             this.el.remove();
17050             
17051             this.el=false;
17052             this.rendered = false;
17053             
17054             this.render(this.parent().getChildContainer(true), k);
17055         }
17056         
17057         if(Roo.isIOS && this.useNativeIOS){
17058             this.initIOSView();
17059             return;
17060         }
17061         
17062         /*
17063          * Touch Devices
17064          */
17065         
17066         if(Roo.isTouch && this.mobileTouchView){
17067             this.initTouchView();
17068             return;
17069         }
17070         
17071         if(this.tickable){
17072             this.initTickableEvents();
17073             return;
17074         }
17075         
17076         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17077         
17078         if(this.hiddenName){
17079             
17080             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17081             
17082             this.hiddenField.dom.value =
17083                 this.hiddenValue !== undefined ? this.hiddenValue :
17084                 this.value !== undefined ? this.value : '';
17085
17086             // prevent input submission
17087             this.el.dom.removeAttribute('name');
17088             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17089              
17090              
17091         }
17092         //if(Roo.isGecko){
17093         //    this.el.dom.setAttribute('autocomplete', 'off');
17094         //}
17095         
17096         var cls = 'x-combo-list';
17097         
17098         //this.list = new Roo.Layer({
17099         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17100         //});
17101         
17102         var _this = this;
17103         
17104         (function(){
17105             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17106             _this.list.setWidth(lw);
17107         }).defer(100);
17108         
17109         this.list.on('mouseover', this.onViewOver, this);
17110         this.list.on('mousemove', this.onViewMove, this);
17111         this.list.on('scroll', this.onViewScroll, this);
17112         
17113         /*
17114         this.list.swallowEvent('mousewheel');
17115         this.assetHeight = 0;
17116
17117         if(this.title){
17118             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17119             this.assetHeight += this.header.getHeight();
17120         }
17121
17122         this.innerList = this.list.createChild({cls:cls+'-inner'});
17123         this.innerList.on('mouseover', this.onViewOver, this);
17124         this.innerList.on('mousemove', this.onViewMove, this);
17125         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17126         
17127         if(this.allowBlank && !this.pageSize && !this.disableClear){
17128             this.footer = this.list.createChild({cls:cls+'-ft'});
17129             this.pageTb = new Roo.Toolbar(this.footer);
17130            
17131         }
17132         if(this.pageSize){
17133             this.footer = this.list.createChild({cls:cls+'-ft'});
17134             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17135                     {pageSize: this.pageSize});
17136             
17137         }
17138         
17139         if (this.pageTb && this.allowBlank && !this.disableClear) {
17140             var _this = this;
17141             this.pageTb.add(new Roo.Toolbar.Fill(), {
17142                 cls: 'x-btn-icon x-btn-clear',
17143                 text: '&#160;',
17144                 handler: function()
17145                 {
17146                     _this.collapse();
17147                     _this.clearValue();
17148                     _this.onSelect(false, -1);
17149                 }
17150             });
17151         }
17152         if (this.footer) {
17153             this.assetHeight += this.footer.getHeight();
17154         }
17155         */
17156             
17157         if(!this.tpl){
17158             this.tpl = Roo.bootstrap.version == 4 ?
17159                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17160                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17161         }
17162
17163         this.view = new Roo.View(this.list, this.tpl, {
17164             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17165         });
17166         //this.view.wrapEl.setDisplayed(false);
17167         this.view.on('click', this.onViewClick, this);
17168         
17169         
17170         this.store.on('beforeload', this.onBeforeLoad, this);
17171         this.store.on('load', this.onLoad, this);
17172         this.store.on('loadexception', this.onLoadException, this);
17173         /*
17174         if(this.resizable){
17175             this.resizer = new Roo.Resizable(this.list,  {
17176                pinned:true, handles:'se'
17177             });
17178             this.resizer.on('resize', function(r, w, h){
17179                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17180                 this.listWidth = w;
17181                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17182                 this.restrictHeight();
17183             }, this);
17184             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17185         }
17186         */
17187         if(!this.editable){
17188             this.editable = true;
17189             this.setEditable(false);
17190         }
17191         
17192         /*
17193         
17194         if (typeof(this.events.add.listeners) != 'undefined') {
17195             
17196             this.addicon = this.wrap.createChild(
17197                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17198        
17199             this.addicon.on('click', function(e) {
17200                 this.fireEvent('add', this);
17201             }, this);
17202         }
17203         if (typeof(this.events.edit.listeners) != 'undefined') {
17204             
17205             this.editicon = this.wrap.createChild(
17206                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17207             if (this.addicon) {
17208                 this.editicon.setStyle('margin-left', '40px');
17209             }
17210             this.editicon.on('click', function(e) {
17211                 
17212                 // we fire even  if inothing is selected..
17213                 this.fireEvent('edit', this, this.lastData );
17214                 
17215             }, this);
17216         }
17217         */
17218         
17219         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17220             "up" : function(e){
17221                 this.inKeyMode = true;
17222                 this.selectPrev();
17223             },
17224
17225             "down" : function(e){
17226                 if(!this.isExpanded()){
17227                     this.onTriggerClick();
17228                 }else{
17229                     this.inKeyMode = true;
17230                     this.selectNext();
17231                 }
17232             },
17233
17234             "enter" : function(e){
17235 //                this.onViewClick();
17236                 //return true;
17237                 this.collapse();
17238                 
17239                 if(this.fireEvent("specialkey", this, e)){
17240                     this.onViewClick(false);
17241                 }
17242                 
17243                 return true;
17244             },
17245
17246             "esc" : function(e){
17247                 this.collapse();
17248             },
17249
17250             "tab" : function(e){
17251                 this.collapse();
17252                 
17253                 if(this.fireEvent("specialkey", this, e)){
17254                     this.onViewClick(false);
17255                 }
17256                 
17257                 return true;
17258             },
17259
17260             scope : this,
17261
17262             doRelay : function(foo, bar, hname){
17263                 if(hname == 'down' || this.scope.isExpanded()){
17264                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17265                 }
17266                 return true;
17267             },
17268
17269             forceKeyDown: true
17270         });
17271         
17272         
17273         this.queryDelay = Math.max(this.queryDelay || 10,
17274                 this.mode == 'local' ? 10 : 250);
17275         
17276         
17277         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17278         
17279         if(this.typeAhead){
17280             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17281         }
17282         if(this.editable !== false){
17283             this.inputEl().on("keyup", this.onKeyUp, this);
17284         }
17285         if(this.forceSelection){
17286             this.inputEl().on('blur', this.doForce, this);
17287         }
17288         
17289         if(this.multiple){
17290             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17291             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17292         }
17293     },
17294     
17295     initTickableEvents: function()
17296     {   
17297         this.createList();
17298         
17299         if(this.hiddenName){
17300             
17301             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17302             
17303             this.hiddenField.dom.value =
17304                 this.hiddenValue !== undefined ? this.hiddenValue :
17305                 this.value !== undefined ? this.value : '';
17306
17307             // prevent input submission
17308             this.el.dom.removeAttribute('name');
17309             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17310              
17311              
17312         }
17313         
17314 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17315         
17316         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17317         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17318         if(this.triggerList){
17319             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17320         }
17321          
17322         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17323         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17324         
17325         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17326         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17327         
17328         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17329         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17330         
17331         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17332         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17333         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17334         
17335         this.okBtn.hide();
17336         this.cancelBtn.hide();
17337         
17338         var _this = this;
17339         
17340         (function(){
17341             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17342             _this.list.setWidth(lw);
17343         }).defer(100);
17344         
17345         this.list.on('mouseover', this.onViewOver, this);
17346         this.list.on('mousemove', this.onViewMove, this);
17347         
17348         this.list.on('scroll', this.onViewScroll, this);
17349         
17350         if(!this.tpl){
17351             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17352                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17353         }
17354
17355         this.view = new Roo.View(this.list, this.tpl, {
17356             singleSelect:true,
17357             tickable:true,
17358             parent:this,
17359             store: this.store,
17360             selectedClass: this.selectedClass
17361         });
17362         
17363         //this.view.wrapEl.setDisplayed(false);
17364         this.view.on('click', this.onViewClick, this);
17365         
17366         
17367         
17368         this.store.on('beforeload', this.onBeforeLoad, this);
17369         this.store.on('load', this.onLoad, this);
17370         this.store.on('loadexception', this.onLoadException, this);
17371         
17372         if(this.editable){
17373             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17374                 "up" : function(e){
17375                     this.inKeyMode = true;
17376                     this.selectPrev();
17377                 },
17378
17379                 "down" : function(e){
17380                     this.inKeyMode = true;
17381                     this.selectNext();
17382                 },
17383
17384                 "enter" : function(e){
17385                     if(this.fireEvent("specialkey", this, e)){
17386                         this.onViewClick(false);
17387                     }
17388                     
17389                     return true;
17390                 },
17391
17392                 "esc" : function(e){
17393                     this.onTickableFooterButtonClick(e, false, false);
17394                 },
17395
17396                 "tab" : function(e){
17397                     this.fireEvent("specialkey", this, e);
17398                     
17399                     this.onTickableFooterButtonClick(e, false, false);
17400                     
17401                     return true;
17402                 },
17403
17404                 scope : this,
17405
17406                 doRelay : function(e, fn, key){
17407                     if(this.scope.isExpanded()){
17408                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17409                     }
17410                     return true;
17411                 },
17412
17413                 forceKeyDown: true
17414             });
17415         }
17416         
17417         this.queryDelay = Math.max(this.queryDelay || 10,
17418                 this.mode == 'local' ? 10 : 250);
17419         
17420         
17421         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17422         
17423         if(this.typeAhead){
17424             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17425         }
17426         
17427         if(this.editable !== false){
17428             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17429         }
17430         
17431         this.indicator = this.indicatorEl();
17432         
17433         if(this.indicator){
17434             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17435             this.indicator.hide();
17436         }
17437         
17438     },
17439
17440     onDestroy : function(){
17441         if(this.view){
17442             this.view.setStore(null);
17443             this.view.el.removeAllListeners();
17444             this.view.el.remove();
17445             this.view.purgeListeners();
17446         }
17447         if(this.list){
17448             this.list.dom.innerHTML  = '';
17449         }
17450         
17451         if(this.store){
17452             this.store.un('beforeload', this.onBeforeLoad, this);
17453             this.store.un('load', this.onLoad, this);
17454             this.store.un('loadexception', this.onLoadException, this);
17455         }
17456         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17457     },
17458
17459     // private
17460     fireKey : function(e){
17461         if(e.isNavKeyPress() && !this.list.isVisible()){
17462             this.fireEvent("specialkey", this, e);
17463         }
17464     },
17465
17466     // private
17467     onResize: function(w, h)
17468     {
17469         
17470         
17471 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17472 //        
17473 //        if(typeof w != 'number'){
17474 //            // we do not handle it!?!?
17475 //            return;
17476 //        }
17477 //        var tw = this.trigger.getWidth();
17478 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17479 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17480 //        var x = w - tw;
17481 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17482 //            
17483 //        //this.trigger.setStyle('left', x+'px');
17484 //        
17485 //        if(this.list && this.listWidth === undefined){
17486 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17487 //            this.list.setWidth(lw);
17488 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17489 //        }
17490         
17491     
17492         
17493     },
17494
17495     /**
17496      * Allow or prevent the user from directly editing the field text.  If false is passed,
17497      * the user will only be able to select from the items defined in the dropdown list.  This method
17498      * is the runtime equivalent of setting the 'editable' config option at config time.
17499      * @param {Boolean} value True to allow the user to directly edit the field text
17500      */
17501     setEditable : function(value){
17502         if(value == this.editable){
17503             return;
17504         }
17505         this.editable = value;
17506         if(!value){
17507             this.inputEl().dom.setAttribute('readOnly', true);
17508             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17509             this.inputEl().addClass('x-combo-noedit');
17510         }else{
17511             this.inputEl().dom.removeAttribute('readOnly');
17512             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17513             this.inputEl().removeClass('x-combo-noedit');
17514         }
17515     },
17516
17517     // private
17518     
17519     onBeforeLoad : function(combo,opts){
17520         if(!this.hasFocus){
17521             return;
17522         }
17523          if (!opts.add) {
17524             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17525          }
17526         this.restrictHeight();
17527         this.selectedIndex = -1;
17528     },
17529
17530     // private
17531     onLoad : function(){
17532         
17533         this.hasQuery = false;
17534         
17535         if(!this.hasFocus){
17536             return;
17537         }
17538         
17539         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17540             this.loading.hide();
17541         }
17542         
17543         if(this.store.getCount() > 0){
17544             
17545             this.expand();
17546             this.restrictHeight();
17547             if(this.lastQuery == this.allQuery){
17548                 if(this.editable && !this.tickable){
17549                     this.inputEl().dom.select();
17550                 }
17551                 
17552                 if(
17553                     !this.selectByValue(this.value, true) &&
17554                     this.autoFocus && 
17555                     (
17556                         !this.store.lastOptions ||
17557                         typeof(this.store.lastOptions.add) == 'undefined' || 
17558                         this.store.lastOptions.add != true
17559                     )
17560                 ){
17561                     this.select(0, true);
17562                 }
17563             }else{
17564                 if(this.autoFocus){
17565                     this.selectNext();
17566                 }
17567                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17568                     this.taTask.delay(this.typeAheadDelay);
17569                 }
17570             }
17571         }else{
17572             this.onEmptyResults();
17573         }
17574         
17575         //this.el.focus();
17576     },
17577     // private
17578     onLoadException : function()
17579     {
17580         this.hasQuery = false;
17581         
17582         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17583             this.loading.hide();
17584         }
17585         
17586         if(this.tickable && this.editable){
17587             return;
17588         }
17589         
17590         this.collapse();
17591         // only causes errors at present
17592         //Roo.log(this.store.reader.jsonData);
17593         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17594             // fixme
17595             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17596         //}
17597         
17598         
17599     },
17600     // private
17601     onTypeAhead : function(){
17602         if(this.store.getCount() > 0){
17603             var r = this.store.getAt(0);
17604             var newValue = r.data[this.displayField];
17605             var len = newValue.length;
17606             var selStart = this.getRawValue().length;
17607             
17608             if(selStart != len){
17609                 this.setRawValue(newValue);
17610                 this.selectText(selStart, newValue.length);
17611             }
17612         }
17613     },
17614
17615     // private
17616     onSelect : function(record, index){
17617         
17618         if(this.fireEvent('beforeselect', this, record, index) !== false){
17619         
17620             this.setFromData(index > -1 ? record.data : false);
17621             
17622             this.collapse();
17623             this.fireEvent('select', this, record, index);
17624         }
17625     },
17626
17627     /**
17628      * Returns the currently selected field value or empty string if no value is set.
17629      * @return {String} value The selected value
17630      */
17631     getValue : function()
17632     {
17633         if(Roo.isIOS && this.useNativeIOS){
17634             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17635         }
17636         
17637         if(this.multiple){
17638             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17639         }
17640         
17641         if(this.valueField){
17642             return typeof this.value != 'undefined' ? this.value : '';
17643         }else{
17644             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17645         }
17646     },
17647     
17648     getRawValue : function()
17649     {
17650         if(Roo.isIOS && this.useNativeIOS){
17651             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17652         }
17653         
17654         var v = this.inputEl().getValue();
17655         
17656         return v;
17657     },
17658
17659     /**
17660      * Clears any text/value currently set in the field
17661      */
17662     clearValue : function(){
17663         
17664         if(this.hiddenField){
17665             this.hiddenField.dom.value = '';
17666         }
17667         this.value = '';
17668         this.setRawValue('');
17669         this.lastSelectionText = '';
17670         this.lastData = false;
17671         
17672         var close = this.closeTriggerEl();
17673         
17674         if(close){
17675             close.hide();
17676         }
17677         
17678         this.validate();
17679         
17680     },
17681
17682     /**
17683      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17684      * will be displayed in the field.  If the value does not match the data value of an existing item,
17685      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17686      * Otherwise the field will be blank (although the value will still be set).
17687      * @param {String} value The value to match
17688      */
17689     setValue : function(v)
17690     {
17691         if(Roo.isIOS && this.useNativeIOS){
17692             this.setIOSValue(v);
17693             return;
17694         }
17695         
17696         if(this.multiple){
17697             this.syncValue();
17698             return;
17699         }
17700         
17701         var text = v;
17702         if(this.valueField){
17703             var r = this.findRecord(this.valueField, v);
17704             if(r){
17705                 text = r.data[this.displayField];
17706             }else if(this.valueNotFoundText !== undefined){
17707                 text = this.valueNotFoundText;
17708             }
17709         }
17710         this.lastSelectionText = text;
17711         if(this.hiddenField){
17712             this.hiddenField.dom.value = v;
17713         }
17714         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17715         this.value = v;
17716         
17717         var close = this.closeTriggerEl();
17718         
17719         if(close){
17720             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17721         }
17722         
17723         this.validate();
17724     },
17725     /**
17726      * @property {Object} the last set data for the element
17727      */
17728     
17729     lastData : false,
17730     /**
17731      * Sets the value of the field based on a object which is related to the record format for the store.
17732      * @param {Object} value the value to set as. or false on reset?
17733      */
17734     setFromData : function(o){
17735         
17736         if(this.multiple){
17737             this.addItem(o);
17738             return;
17739         }
17740             
17741         var dv = ''; // display value
17742         var vv = ''; // value value..
17743         this.lastData = o;
17744         if (this.displayField) {
17745             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17746         } else {
17747             // this is an error condition!!!
17748             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17749         }
17750         
17751         if(this.valueField){
17752             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17753         }
17754         
17755         var close = this.closeTriggerEl();
17756         
17757         if(close){
17758             if(dv.length || vv * 1 > 0){
17759                 close.show() ;
17760                 this.blockFocus=true;
17761             } else {
17762                 close.hide();
17763             }             
17764         }
17765         
17766         if(this.hiddenField){
17767             this.hiddenField.dom.value = vv;
17768             
17769             this.lastSelectionText = dv;
17770             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17771             this.value = vv;
17772             return;
17773         }
17774         // no hidden field.. - we store the value in 'value', but still display
17775         // display field!!!!
17776         this.lastSelectionText = dv;
17777         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17778         this.value = vv;
17779         
17780         
17781         
17782     },
17783     // private
17784     reset : function(){
17785         // overridden so that last data is reset..
17786         
17787         if(this.multiple){
17788             this.clearItem();
17789             return;
17790         }
17791         
17792         this.setValue(this.originalValue);
17793         //this.clearInvalid();
17794         this.lastData = false;
17795         if (this.view) {
17796             this.view.clearSelections();
17797         }
17798         
17799         this.validate();
17800     },
17801     // private
17802     findRecord : function(prop, value){
17803         var record;
17804         if(this.store.getCount() > 0){
17805             this.store.each(function(r){
17806                 if(r.data[prop] == value){
17807                     record = r;
17808                     return false;
17809                 }
17810                 return true;
17811             });
17812         }
17813         return record;
17814     },
17815     
17816     getName: function()
17817     {
17818         // returns hidden if it's set..
17819         if (!this.rendered) {return ''};
17820         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17821         
17822     },
17823     // private
17824     onViewMove : function(e, t){
17825         this.inKeyMode = false;
17826     },
17827
17828     // private
17829     onViewOver : function(e, t){
17830         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17831             return;
17832         }
17833         var item = this.view.findItemFromChild(t);
17834         
17835         if(item){
17836             var index = this.view.indexOf(item);
17837             this.select(index, false);
17838         }
17839     },
17840
17841     // private
17842     onViewClick : function(view, doFocus, el, e)
17843     {
17844         var index = this.view.getSelectedIndexes()[0];
17845         
17846         var r = this.store.getAt(index);
17847         
17848         if(this.tickable){
17849             
17850             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17851                 return;
17852             }
17853             
17854             var rm = false;
17855             var _this = this;
17856             
17857             Roo.each(this.tickItems, function(v,k){
17858                 
17859                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17860                     Roo.log(v);
17861                     _this.tickItems.splice(k, 1);
17862                     
17863                     if(typeof(e) == 'undefined' && view == false){
17864                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17865                     }
17866                     
17867                     rm = true;
17868                     return;
17869                 }
17870             });
17871             
17872             if(rm){
17873                 return;
17874             }
17875             
17876             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17877                 this.tickItems.push(r.data);
17878             }
17879             
17880             if(typeof(e) == 'undefined' && view == false){
17881                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17882             }
17883                     
17884             return;
17885         }
17886         
17887         if(r){
17888             this.onSelect(r, index);
17889         }
17890         if(doFocus !== false && !this.blockFocus){
17891             this.inputEl().focus();
17892         }
17893     },
17894
17895     // private
17896     restrictHeight : function(){
17897         //this.innerList.dom.style.height = '';
17898         //var inner = this.innerList.dom;
17899         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17900         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17901         //this.list.beginUpdate();
17902         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17903         this.list.alignTo(this.inputEl(), this.listAlign);
17904         this.list.alignTo(this.inputEl(), this.listAlign);
17905         //this.list.endUpdate();
17906     },
17907
17908     // private
17909     onEmptyResults : function(){
17910         
17911         if(this.tickable && this.editable){
17912             this.hasFocus = false;
17913             this.restrictHeight();
17914             return;
17915         }
17916         
17917         this.collapse();
17918     },
17919
17920     /**
17921      * Returns true if the dropdown list is expanded, else false.
17922      */
17923     isExpanded : function(){
17924         return this.list.isVisible();
17925     },
17926
17927     /**
17928      * Select an item in the dropdown list by its data value. 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 {String} value The data value of the 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      * @return {Boolean} True if the value matched an item in the list, else false
17934      */
17935     selectByValue : function(v, scrollIntoView){
17936         if(v !== undefined && v !== null){
17937             var r = this.findRecord(this.valueField || this.displayField, v);
17938             if(r){
17939                 this.select(this.store.indexOf(r), scrollIntoView);
17940                 return true;
17941             }
17942         }
17943         return false;
17944     },
17945
17946     /**
17947      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17948      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17949      * @param {Number} index The zero-based index of the list item to select
17950      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17951      * selected item if it is not currently in view (defaults to true)
17952      */
17953     select : function(index, scrollIntoView){
17954         this.selectedIndex = index;
17955         this.view.select(index);
17956         if(scrollIntoView !== false){
17957             var el = this.view.getNode(index);
17958             /*
17959              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17960              */
17961             if(el){
17962                 this.list.scrollChildIntoView(el, false);
17963             }
17964         }
17965     },
17966
17967     // private
17968     selectNext : function(){
17969         var ct = this.store.getCount();
17970         if(ct > 0){
17971             if(this.selectedIndex == -1){
17972                 this.select(0);
17973             }else if(this.selectedIndex < ct-1){
17974                 this.select(this.selectedIndex+1);
17975             }
17976         }
17977     },
17978
17979     // private
17980     selectPrev : function(){
17981         var ct = this.store.getCount();
17982         if(ct > 0){
17983             if(this.selectedIndex == -1){
17984                 this.select(0);
17985             }else if(this.selectedIndex != 0){
17986                 this.select(this.selectedIndex-1);
17987             }
17988         }
17989     },
17990
17991     // private
17992     onKeyUp : function(e){
17993         if(this.editable !== false && !e.isSpecialKey()){
17994             this.lastKey = e.getKey();
17995             this.dqTask.delay(this.queryDelay);
17996         }
17997     },
17998
17999     // private
18000     validateBlur : function(){
18001         return !this.list || !this.list.isVisible();   
18002     },
18003
18004     // private
18005     initQuery : function(){
18006         
18007         var v = this.getRawValue();
18008         
18009         if(this.tickable && this.editable){
18010             v = this.tickableInputEl().getValue();
18011         }
18012         
18013         this.doQuery(v);
18014     },
18015
18016     // private
18017     doForce : function(){
18018         if(this.inputEl().dom.value.length > 0){
18019             this.inputEl().dom.value =
18020                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18021              
18022         }
18023     },
18024
18025     /**
18026      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18027      * query allowing the query action to be canceled if needed.
18028      * @param {String} query The SQL query to execute
18029      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18030      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18031      * saved in the current store (defaults to false)
18032      */
18033     doQuery : function(q, forceAll){
18034         
18035         if(q === undefined || q === null){
18036             q = '';
18037         }
18038         var qe = {
18039             query: q,
18040             forceAll: forceAll,
18041             combo: this,
18042             cancel:false
18043         };
18044         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18045             return false;
18046         }
18047         q = qe.query;
18048         
18049         forceAll = qe.forceAll;
18050         if(forceAll === true || (q.length >= this.minChars)){
18051             
18052             this.hasQuery = true;
18053             
18054             if(this.lastQuery != q || this.alwaysQuery){
18055                 this.lastQuery = q;
18056                 if(this.mode == 'local'){
18057                     this.selectedIndex = -1;
18058                     if(forceAll){
18059                         this.store.clearFilter();
18060                     }else{
18061                         
18062                         if(this.specialFilter){
18063                             this.fireEvent('specialfilter', this);
18064                             this.onLoad();
18065                             return;
18066                         }
18067                         
18068                         this.store.filter(this.displayField, q);
18069                     }
18070                     
18071                     this.store.fireEvent("datachanged", this.store);
18072                     
18073                     this.onLoad();
18074                     
18075                     
18076                 }else{
18077                     
18078                     this.store.baseParams[this.queryParam] = q;
18079                     
18080                     var options = {params : this.getParams(q)};
18081                     
18082                     if(this.loadNext){
18083                         options.add = true;
18084                         options.params.start = this.page * this.pageSize;
18085                     }
18086                     
18087                     this.store.load(options);
18088                     
18089                     /*
18090                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18091                      *  we should expand the list on onLoad
18092                      *  so command out it
18093                      */
18094 //                    this.expand();
18095                 }
18096             }else{
18097                 this.selectedIndex = -1;
18098                 this.onLoad();   
18099             }
18100         }
18101         
18102         this.loadNext = false;
18103     },
18104     
18105     // private
18106     getParams : function(q){
18107         var p = {};
18108         //p[this.queryParam] = q;
18109         
18110         if(this.pageSize){
18111             p.start = 0;
18112             p.limit = this.pageSize;
18113         }
18114         return p;
18115     },
18116
18117     /**
18118      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18119      */
18120     collapse : function(){
18121         if(!this.isExpanded()){
18122             return;
18123         }
18124         
18125         this.list.hide();
18126         
18127         this.hasFocus = false;
18128         
18129         if(this.tickable){
18130             this.okBtn.hide();
18131             this.cancelBtn.hide();
18132             this.trigger.show();
18133             
18134             if(this.editable){
18135                 this.tickableInputEl().dom.value = '';
18136                 this.tickableInputEl().blur();
18137             }
18138             
18139         }
18140         
18141         Roo.get(document).un('mousedown', this.collapseIf, this);
18142         Roo.get(document).un('mousewheel', this.collapseIf, this);
18143         if (!this.editable) {
18144             Roo.get(document).un('keydown', this.listKeyPress, this);
18145         }
18146         this.fireEvent('collapse', this);
18147         
18148         this.validate();
18149     },
18150
18151     // private
18152     collapseIf : function(e){
18153         var in_combo  = e.within(this.el);
18154         var in_list =  e.within(this.list);
18155         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18156         
18157         if (in_combo || in_list || is_list) {
18158             //e.stopPropagation();
18159             return;
18160         }
18161         
18162         if(this.tickable){
18163             this.onTickableFooterButtonClick(e, false, false);
18164         }
18165
18166         this.collapse();
18167         
18168     },
18169
18170     /**
18171      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18172      */
18173     expand : function(){
18174        
18175         if(this.isExpanded() || !this.hasFocus){
18176             return;
18177         }
18178         
18179         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18180         this.list.setWidth(lw);
18181         
18182         Roo.log('expand');
18183         
18184         this.list.show();
18185         
18186         this.restrictHeight();
18187         
18188         if(this.tickable){
18189             
18190             this.tickItems = Roo.apply([], this.item);
18191             
18192             this.okBtn.show();
18193             this.cancelBtn.show();
18194             this.trigger.hide();
18195             
18196             if(this.editable){
18197                 this.tickableInputEl().focus();
18198             }
18199             
18200         }
18201         
18202         Roo.get(document).on('mousedown', this.collapseIf, this);
18203         Roo.get(document).on('mousewheel', this.collapseIf, this);
18204         if (!this.editable) {
18205             Roo.get(document).on('keydown', this.listKeyPress, this);
18206         }
18207         
18208         this.fireEvent('expand', this);
18209     },
18210
18211     // private
18212     // Implements the default empty TriggerField.onTriggerClick function
18213     onTriggerClick : function(e)
18214     {
18215         Roo.log('trigger click');
18216         
18217         if(this.disabled || !this.triggerList){
18218             return;
18219         }
18220         
18221         this.page = 0;
18222         this.loadNext = false;
18223         
18224         if(this.isExpanded()){
18225             this.collapse();
18226             if (!this.blockFocus) {
18227                 this.inputEl().focus();
18228             }
18229             
18230         }else {
18231             this.hasFocus = true;
18232             if(this.triggerAction == 'all') {
18233                 this.doQuery(this.allQuery, true);
18234             } else {
18235                 this.doQuery(this.getRawValue());
18236             }
18237             if (!this.blockFocus) {
18238                 this.inputEl().focus();
18239             }
18240         }
18241     },
18242     
18243     onTickableTriggerClick : function(e)
18244     {
18245         if(this.disabled){
18246             return;
18247         }
18248         
18249         this.page = 0;
18250         this.loadNext = false;
18251         this.hasFocus = true;
18252         
18253         if(this.triggerAction == 'all') {
18254             this.doQuery(this.allQuery, true);
18255         } else {
18256             this.doQuery(this.getRawValue());
18257         }
18258     },
18259     
18260     onSearchFieldClick : function(e)
18261     {
18262         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18263             this.onTickableFooterButtonClick(e, false, false);
18264             return;
18265         }
18266         
18267         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18268             return;
18269         }
18270         
18271         this.page = 0;
18272         this.loadNext = false;
18273         this.hasFocus = true;
18274         
18275         if(this.triggerAction == 'all') {
18276             this.doQuery(this.allQuery, true);
18277         } else {
18278             this.doQuery(this.getRawValue());
18279         }
18280     },
18281     
18282     listKeyPress : function(e)
18283     {
18284         //Roo.log('listkeypress');
18285         // scroll to first matching element based on key pres..
18286         if (e.isSpecialKey()) {
18287             return false;
18288         }
18289         var k = String.fromCharCode(e.getKey()).toUpperCase();
18290         //Roo.log(k);
18291         var match  = false;
18292         var csel = this.view.getSelectedNodes();
18293         var cselitem = false;
18294         if (csel.length) {
18295             var ix = this.view.indexOf(csel[0]);
18296             cselitem  = this.store.getAt(ix);
18297             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18298                 cselitem = false;
18299             }
18300             
18301         }
18302         
18303         this.store.each(function(v) { 
18304             if (cselitem) {
18305                 // start at existing selection.
18306                 if (cselitem.id == v.id) {
18307                     cselitem = false;
18308                 }
18309                 return true;
18310             }
18311                 
18312             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18313                 match = this.store.indexOf(v);
18314                 return false;
18315             }
18316             return true;
18317         }, this);
18318         
18319         if (match === false) {
18320             return true; // no more action?
18321         }
18322         // scroll to?
18323         this.view.select(match);
18324         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18325         sn.scrollIntoView(sn.dom.parentNode, false);
18326     },
18327     
18328     onViewScroll : function(e, t){
18329         
18330         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){
18331             return;
18332         }
18333         
18334         this.hasQuery = true;
18335         
18336         this.loading = this.list.select('.loading', true).first();
18337         
18338         if(this.loading === null){
18339             this.list.createChild({
18340                 tag: 'div',
18341                 cls: 'loading roo-select2-more-results roo-select2-active',
18342                 html: 'Loading more results...'
18343             });
18344             
18345             this.loading = this.list.select('.loading', true).first();
18346             
18347             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18348             
18349             this.loading.hide();
18350         }
18351         
18352         this.loading.show();
18353         
18354         var _combo = this;
18355         
18356         this.page++;
18357         this.loadNext = true;
18358         
18359         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18360         
18361         return;
18362     },
18363     
18364     addItem : function(o)
18365     {   
18366         var dv = ''; // display value
18367         
18368         if (this.displayField) {
18369             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18370         } else {
18371             // this is an error condition!!!
18372             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18373         }
18374         
18375         if(!dv.length){
18376             return;
18377         }
18378         
18379         var choice = this.choices.createChild({
18380             tag: 'li',
18381             cls: 'roo-select2-search-choice',
18382             cn: [
18383                 {
18384                     tag: 'div',
18385                     html: dv
18386                 },
18387                 {
18388                     tag: 'a',
18389                     href: '#',
18390                     cls: 'roo-select2-search-choice-close fa fa-times',
18391                     tabindex: '-1'
18392                 }
18393             ]
18394             
18395         }, this.searchField);
18396         
18397         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18398         
18399         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18400         
18401         this.item.push(o);
18402         
18403         this.lastData = o;
18404         
18405         this.syncValue();
18406         
18407         this.inputEl().dom.value = '';
18408         
18409         this.validate();
18410     },
18411     
18412     onRemoveItem : function(e, _self, o)
18413     {
18414         e.preventDefault();
18415         
18416         this.lastItem = Roo.apply([], this.item);
18417         
18418         var index = this.item.indexOf(o.data) * 1;
18419         
18420         if( index < 0){
18421             Roo.log('not this item?!');
18422             return;
18423         }
18424         
18425         this.item.splice(index, 1);
18426         o.item.remove();
18427         
18428         this.syncValue();
18429         
18430         this.fireEvent('remove', this, e);
18431         
18432         this.validate();
18433         
18434     },
18435     
18436     syncValue : function()
18437     {
18438         if(!this.item.length){
18439             this.clearValue();
18440             return;
18441         }
18442             
18443         var value = [];
18444         var _this = this;
18445         Roo.each(this.item, function(i){
18446             if(_this.valueField){
18447                 value.push(i[_this.valueField]);
18448                 return;
18449             }
18450
18451             value.push(i);
18452         });
18453
18454         this.value = value.join(',');
18455
18456         if(this.hiddenField){
18457             this.hiddenField.dom.value = this.value;
18458         }
18459         
18460         this.store.fireEvent("datachanged", this.store);
18461         
18462         this.validate();
18463     },
18464     
18465     clearItem : function()
18466     {
18467         if(!this.multiple){
18468             return;
18469         }
18470         
18471         this.item = [];
18472         
18473         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18474            c.remove();
18475         });
18476         
18477         this.syncValue();
18478         
18479         this.validate();
18480         
18481         if(this.tickable && !Roo.isTouch){
18482             this.view.refresh();
18483         }
18484     },
18485     
18486     inputEl: function ()
18487     {
18488         if(Roo.isIOS && this.useNativeIOS){
18489             return this.el.select('select.roo-ios-select', true).first();
18490         }
18491         
18492         if(Roo.isTouch && this.mobileTouchView){
18493             return this.el.select('input.form-control',true).first();
18494         }
18495         
18496         if(this.tickable){
18497             return this.searchField;
18498         }
18499         
18500         return this.el.select('input.form-control',true).first();
18501     },
18502     
18503     onTickableFooterButtonClick : function(e, btn, el)
18504     {
18505         e.preventDefault();
18506         
18507         this.lastItem = Roo.apply([], this.item);
18508         
18509         if(btn && btn.name == 'cancel'){
18510             this.tickItems = Roo.apply([], this.item);
18511             this.collapse();
18512             return;
18513         }
18514         
18515         this.clearItem();
18516         
18517         var _this = this;
18518         
18519         Roo.each(this.tickItems, function(o){
18520             _this.addItem(o);
18521         });
18522         
18523         this.collapse();
18524         
18525     },
18526     
18527     validate : function()
18528     {
18529         if(this.getVisibilityEl().hasClass('hidden')){
18530             return true;
18531         }
18532         
18533         var v = this.getRawValue();
18534         
18535         if(this.multiple){
18536             v = this.getValue();
18537         }
18538         
18539         if(this.disabled || this.allowBlank || v.length){
18540             this.markValid();
18541             return true;
18542         }
18543         
18544         this.markInvalid();
18545         return false;
18546     },
18547     
18548     tickableInputEl : function()
18549     {
18550         if(!this.tickable || !this.editable){
18551             return this.inputEl();
18552         }
18553         
18554         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18555     },
18556     
18557     
18558     getAutoCreateTouchView : function()
18559     {
18560         var id = Roo.id();
18561         
18562         var cfg = {
18563             cls: 'form-group' //input-group
18564         };
18565         
18566         var input =  {
18567             tag: 'input',
18568             id : id,
18569             type : this.inputType,
18570             cls : 'form-control x-combo-noedit',
18571             autocomplete: 'new-password',
18572             placeholder : this.placeholder || '',
18573             readonly : true
18574         };
18575         
18576         if (this.name) {
18577             input.name = this.name;
18578         }
18579         
18580         if (this.size) {
18581             input.cls += ' input-' + this.size;
18582         }
18583         
18584         if (this.disabled) {
18585             input.disabled = true;
18586         }
18587         
18588         var inputblock = {
18589             cls : 'roo-combobox-wrap',
18590             cn : [
18591                 input
18592             ]
18593         };
18594         
18595         if(this.before){
18596             inputblock.cls += ' input-group';
18597             
18598             inputblock.cn.unshift({
18599                 tag :'span',
18600                 cls : 'input-group-addon input-group-prepend input-group-text',
18601                 html : this.before
18602             });
18603         }
18604         
18605         if(this.removable && !this.multiple){
18606             inputblock.cls += ' roo-removable';
18607             
18608             inputblock.cn.push({
18609                 tag: 'button',
18610                 html : 'x',
18611                 cls : 'roo-combo-removable-btn close'
18612             });
18613         }
18614
18615         if(this.hasFeedback && !this.allowBlank){
18616             
18617             inputblock.cls += ' has-feedback';
18618             
18619             inputblock.cn.push({
18620                 tag: 'span',
18621                 cls: 'glyphicon form-control-feedback'
18622             });
18623             
18624         }
18625         
18626         if (this.after) {
18627             
18628             inputblock.cls += (this.before) ? '' : ' input-group';
18629             
18630             inputblock.cn.push({
18631                 tag :'span',
18632                 cls : 'input-group-addon input-group-append input-group-text',
18633                 html : this.after
18634             });
18635         }
18636
18637         
18638         var ibwrap = inputblock;
18639         
18640         if(this.multiple){
18641             ibwrap = {
18642                 tag: 'ul',
18643                 cls: 'roo-select2-choices',
18644                 cn:[
18645                     {
18646                         tag: 'li',
18647                         cls: 'roo-select2-search-field',
18648                         cn: [
18649
18650                             inputblock
18651                         ]
18652                     }
18653                 ]
18654             };
18655         
18656             
18657         }
18658         
18659         var combobox = {
18660             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18661             cn: [
18662                 {
18663                     tag: 'input',
18664                     type : 'hidden',
18665                     cls: 'form-hidden-field'
18666                 },
18667                 ibwrap
18668             ]
18669         };
18670         
18671         if(!this.multiple && this.showToggleBtn){
18672             
18673             var caret = {
18674                 cls: 'caret'
18675             };
18676             
18677             if (this.caret != false) {
18678                 caret = {
18679                      tag: 'i',
18680                      cls: 'fa fa-' + this.caret
18681                 };
18682                 
18683             }
18684             
18685             combobox.cn.push({
18686                 tag :'span',
18687                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18688                 cn : [
18689                     Roo.bootstrap.version == 3 ? caret : '',
18690                     {
18691                         tag: 'span',
18692                         cls: 'combobox-clear',
18693                         cn  : [
18694                             {
18695                                 tag : 'i',
18696                                 cls: 'icon-remove'
18697                             }
18698                         ]
18699                     }
18700                 ]
18701
18702             })
18703         }
18704         
18705         if(this.multiple){
18706             combobox.cls += ' roo-select2-container-multi';
18707         }
18708         
18709         var required =  this.allowBlank ?  {
18710                     tag : 'i',
18711                     style: 'display: none'
18712                 } : {
18713                    tag : 'i',
18714                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18715                    tooltip : 'This field is required'
18716                 };
18717         
18718         var align = this.labelAlign || this.parentLabelAlign();
18719         
18720         if (align ==='left' && this.fieldLabel.length) {
18721
18722             cfg.cn = [
18723                 required,
18724                 {
18725                     tag: 'label',
18726                     cls : 'control-label col-form-label',
18727                     html : this.fieldLabel
18728
18729                 },
18730                 {
18731                     cls : 'roo-combobox-wrap ', 
18732                     cn: [
18733                         combobox
18734                     ]
18735                 }
18736             ];
18737             
18738             var labelCfg = cfg.cn[1];
18739             var contentCfg = cfg.cn[2];
18740             
18741
18742             if(this.indicatorpos == 'right'){
18743                 cfg.cn = [
18744                     {
18745                         tag: 'label',
18746                         'for' :  id,
18747                         cls : 'control-label col-form-label',
18748                         cn : [
18749                             {
18750                                 tag : 'span',
18751                                 html : this.fieldLabel
18752                             },
18753                             required
18754                         ]
18755                     },
18756                     {
18757                         cls : "roo-combobox-wrap ",
18758                         cn: [
18759                             combobox
18760                         ]
18761                     }
18762
18763                 ];
18764                 
18765                 labelCfg = cfg.cn[0];
18766                 contentCfg = cfg.cn[1];
18767             }
18768             
18769            
18770             
18771             if(this.labelWidth > 12){
18772                 labelCfg.style = "width: " + this.labelWidth + 'px';
18773             }
18774            
18775             if(this.labelWidth < 13 && this.labelmd == 0){
18776                 this.labelmd = this.labelWidth;
18777             }
18778             
18779             if(this.labellg > 0){
18780                 labelCfg.cls += ' col-lg-' + this.labellg;
18781                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18782             }
18783             
18784             if(this.labelmd > 0){
18785                 labelCfg.cls += ' col-md-' + this.labelmd;
18786                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18787             }
18788             
18789             if(this.labelsm > 0){
18790                 labelCfg.cls += ' col-sm-' + this.labelsm;
18791                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18792             }
18793             
18794             if(this.labelxs > 0){
18795                 labelCfg.cls += ' col-xs-' + this.labelxs;
18796                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18797             }
18798                 
18799                 
18800         } else if ( this.fieldLabel.length) {
18801             cfg.cn = [
18802                required,
18803                 {
18804                     tag: 'label',
18805                     cls : 'control-label',
18806                     html : this.fieldLabel
18807
18808                 },
18809                 {
18810                     cls : '', 
18811                     cn: [
18812                         combobox
18813                     ]
18814                 }
18815             ];
18816             
18817             if(this.indicatorpos == 'right'){
18818                 cfg.cn = [
18819                     {
18820                         tag: 'label',
18821                         cls : 'control-label',
18822                         html : this.fieldLabel,
18823                         cn : [
18824                             required
18825                         ]
18826                     },
18827                     {
18828                         cls : '', 
18829                         cn: [
18830                             combobox
18831                         ]
18832                     }
18833                 ];
18834             }
18835         } else {
18836             cfg.cn = combobox;    
18837         }
18838         
18839         
18840         var settings = this;
18841         
18842         ['xs','sm','md','lg'].map(function(size){
18843             if (settings[size]) {
18844                 cfg.cls += ' col-' + size + '-' + settings[size];
18845             }
18846         });
18847         
18848         return cfg;
18849     },
18850     
18851     initTouchView : function()
18852     {
18853         this.renderTouchView();
18854         
18855         this.touchViewEl.on('scroll', function(){
18856             this.el.dom.scrollTop = 0;
18857         }, this);
18858         
18859         this.originalValue = this.getValue();
18860         
18861         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18862         
18863         this.inputEl().on("click", this.showTouchView, this);
18864         if (this.triggerEl) {
18865             this.triggerEl.on("click", this.showTouchView, this);
18866         }
18867         
18868         
18869         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18870         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18871         
18872         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18873         
18874         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18875         this.store.on('load', this.onTouchViewLoad, this);
18876         this.store.on('loadexception', this.onTouchViewLoadException, this);
18877         
18878         if(this.hiddenName){
18879             
18880             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18881             
18882             this.hiddenField.dom.value =
18883                 this.hiddenValue !== undefined ? this.hiddenValue :
18884                 this.value !== undefined ? this.value : '';
18885         
18886             this.el.dom.removeAttribute('name');
18887             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18888         }
18889         
18890         if(this.multiple){
18891             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18892             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18893         }
18894         
18895         if(this.removable && !this.multiple){
18896             var close = this.closeTriggerEl();
18897             if(close){
18898                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18899                 close.on('click', this.removeBtnClick, this, close);
18900             }
18901         }
18902         /*
18903          * fix the bug in Safari iOS8
18904          */
18905         this.inputEl().on("focus", function(e){
18906             document.activeElement.blur();
18907         }, this);
18908         
18909         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18910         
18911         return;
18912         
18913         
18914     },
18915     
18916     renderTouchView : function()
18917     {
18918         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18919         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18920         
18921         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18922         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18923         
18924         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18925         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18926         this.touchViewBodyEl.setStyle('overflow', 'auto');
18927         
18928         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18929         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18930         
18931         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18932         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18933         
18934     },
18935     
18936     showTouchView : function()
18937     {
18938         if(this.disabled){
18939             return;
18940         }
18941         
18942         this.touchViewHeaderEl.hide();
18943
18944         if(this.modalTitle.length){
18945             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18946             this.touchViewHeaderEl.show();
18947         }
18948
18949         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18950         this.touchViewEl.show();
18951
18952         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18953         
18954         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18955         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18956
18957         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18958
18959         if(this.modalTitle.length){
18960             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18961         }
18962         
18963         this.touchViewBodyEl.setHeight(bodyHeight);
18964
18965         if(this.animate){
18966             var _this = this;
18967             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18968         }else{
18969             this.touchViewEl.addClass(['in','show']);
18970         }
18971         
18972         if(this._touchViewMask){
18973             Roo.get(document.body).addClass("x-body-masked");
18974             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18975             this._touchViewMask.setStyle('z-index', 10000);
18976             this._touchViewMask.addClass('show');
18977         }
18978         
18979         this.doTouchViewQuery();
18980         
18981     },
18982     
18983     hideTouchView : function()
18984     {
18985         this.touchViewEl.removeClass(['in','show']);
18986
18987         if(this.animate){
18988             var _this = this;
18989             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18990         }else{
18991             this.touchViewEl.setStyle('display', 'none');
18992         }
18993         
18994         if(this._touchViewMask){
18995             this._touchViewMask.removeClass('show');
18996             Roo.get(document.body).removeClass("x-body-masked");
18997         }
18998     },
18999     
19000     setTouchViewValue : function()
19001     {
19002         if(this.multiple){
19003             this.clearItem();
19004         
19005             var _this = this;
19006
19007             Roo.each(this.tickItems, function(o){
19008                 this.addItem(o);
19009             }, this);
19010         }
19011         
19012         this.hideTouchView();
19013     },
19014     
19015     doTouchViewQuery : function()
19016     {
19017         var qe = {
19018             query: '',
19019             forceAll: true,
19020             combo: this,
19021             cancel:false
19022         };
19023         
19024         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19025             return false;
19026         }
19027         
19028         if(!this.alwaysQuery || this.mode == 'local'){
19029             this.onTouchViewLoad();
19030             return;
19031         }
19032         
19033         this.store.load();
19034     },
19035     
19036     onTouchViewBeforeLoad : function(combo,opts)
19037     {
19038         return;
19039     },
19040
19041     // private
19042     onTouchViewLoad : function()
19043     {
19044         if(this.store.getCount() < 1){
19045             this.onTouchViewEmptyResults();
19046             return;
19047         }
19048         
19049         this.clearTouchView();
19050         
19051         var rawValue = this.getRawValue();
19052         
19053         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19054         
19055         this.tickItems = [];
19056         
19057         this.store.data.each(function(d, rowIndex){
19058             var row = this.touchViewListGroup.createChild(template);
19059             
19060             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19061                 row.addClass(d.data.cls);
19062             }
19063             
19064             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19065                 var cfg = {
19066                     data : d.data,
19067                     html : d.data[this.displayField]
19068                 };
19069                 
19070                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19071                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19072                 }
19073             }
19074             row.removeClass('selected');
19075             if(!this.multiple && this.valueField &&
19076                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19077             {
19078                 // radio buttons..
19079                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19080                 row.addClass('selected');
19081             }
19082             
19083             if(this.multiple && this.valueField &&
19084                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19085             {
19086                 
19087                 // checkboxes...
19088                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19089                 this.tickItems.push(d.data);
19090             }
19091             
19092             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19093             
19094         }, this);
19095         
19096         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19097         
19098         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19099
19100         if(this.modalTitle.length){
19101             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19102         }
19103
19104         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19105         
19106         if(this.mobile_restrict_height && listHeight < bodyHeight){
19107             this.touchViewBodyEl.setHeight(listHeight);
19108         }
19109         
19110         var _this = this;
19111         
19112         if(firstChecked && listHeight > bodyHeight){
19113             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19114         }
19115         
19116     },
19117     
19118     onTouchViewLoadException : function()
19119     {
19120         this.hideTouchView();
19121     },
19122     
19123     onTouchViewEmptyResults : function()
19124     {
19125         this.clearTouchView();
19126         
19127         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19128         
19129         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19130         
19131     },
19132     
19133     clearTouchView : function()
19134     {
19135         this.touchViewListGroup.dom.innerHTML = '';
19136     },
19137     
19138     onTouchViewClick : function(e, el, o)
19139     {
19140         e.preventDefault();
19141         
19142         var row = o.row;
19143         var rowIndex = o.rowIndex;
19144         
19145         var r = this.store.getAt(rowIndex);
19146         
19147         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19148             
19149             if(!this.multiple){
19150                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19151                     c.dom.removeAttribute('checked');
19152                 }, this);
19153
19154                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19155
19156                 this.setFromData(r.data);
19157
19158                 var close = this.closeTriggerEl();
19159
19160                 if(close){
19161                     close.show();
19162                 }
19163
19164                 this.hideTouchView();
19165
19166                 this.fireEvent('select', this, r, rowIndex);
19167
19168                 return;
19169             }
19170
19171             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19172                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19173                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19174                 return;
19175             }
19176
19177             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19178             this.addItem(r.data);
19179             this.tickItems.push(r.data);
19180         }
19181     },
19182     
19183     getAutoCreateNativeIOS : function()
19184     {
19185         var cfg = {
19186             cls: 'form-group' //input-group,
19187         };
19188         
19189         var combobox =  {
19190             tag: 'select',
19191             cls : 'roo-ios-select'
19192         };
19193         
19194         if (this.name) {
19195             combobox.name = this.name;
19196         }
19197         
19198         if (this.disabled) {
19199             combobox.disabled = true;
19200         }
19201         
19202         var settings = this;
19203         
19204         ['xs','sm','md','lg'].map(function(size){
19205             if (settings[size]) {
19206                 cfg.cls += ' col-' + size + '-' + settings[size];
19207             }
19208         });
19209         
19210         cfg.cn = combobox;
19211         
19212         return cfg;
19213         
19214     },
19215     
19216     initIOSView : function()
19217     {
19218         this.store.on('load', this.onIOSViewLoad, this);
19219         
19220         return;
19221     },
19222     
19223     onIOSViewLoad : function()
19224     {
19225         if(this.store.getCount() < 1){
19226             return;
19227         }
19228         
19229         this.clearIOSView();
19230         
19231         if(this.allowBlank) {
19232             
19233             var default_text = '-- SELECT --';
19234             
19235             if(this.placeholder.length){
19236                 default_text = this.placeholder;
19237             }
19238             
19239             if(this.emptyTitle.length){
19240                 default_text += ' - ' + this.emptyTitle + ' -';
19241             }
19242             
19243             var opt = this.inputEl().createChild({
19244                 tag: 'option',
19245                 value : 0,
19246                 html : default_text
19247             });
19248             
19249             var o = {};
19250             o[this.valueField] = 0;
19251             o[this.displayField] = default_text;
19252             
19253             this.ios_options.push({
19254                 data : o,
19255                 el : opt
19256             });
19257             
19258         }
19259         
19260         this.store.data.each(function(d, rowIndex){
19261             
19262             var html = '';
19263             
19264             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19265                 html = d.data[this.displayField];
19266             }
19267             
19268             var value = '';
19269             
19270             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19271                 value = d.data[this.valueField];
19272             }
19273             
19274             var option = {
19275                 tag: 'option',
19276                 value : value,
19277                 html : html
19278             };
19279             
19280             if(this.value == d.data[this.valueField]){
19281                 option['selected'] = true;
19282             }
19283             
19284             var opt = this.inputEl().createChild(option);
19285             
19286             this.ios_options.push({
19287                 data : d.data,
19288                 el : opt
19289             });
19290             
19291         }, this);
19292         
19293         this.inputEl().on('change', function(){
19294            this.fireEvent('select', this);
19295         }, this);
19296         
19297     },
19298     
19299     clearIOSView: function()
19300     {
19301         this.inputEl().dom.innerHTML = '';
19302         
19303         this.ios_options = [];
19304     },
19305     
19306     setIOSValue: function(v)
19307     {
19308         this.value = v;
19309         
19310         if(!this.ios_options){
19311             return;
19312         }
19313         
19314         Roo.each(this.ios_options, function(opts){
19315            
19316            opts.el.dom.removeAttribute('selected');
19317            
19318            if(opts.data[this.valueField] != v){
19319                return;
19320            }
19321            
19322            opts.el.dom.setAttribute('selected', true);
19323            
19324         }, this);
19325     }
19326
19327     /** 
19328     * @cfg {Boolean} grow 
19329     * @hide 
19330     */
19331     /** 
19332     * @cfg {Number} growMin 
19333     * @hide 
19334     */
19335     /** 
19336     * @cfg {Number} growMax 
19337     * @hide 
19338     */
19339     /**
19340      * @hide
19341      * @method autoSize
19342      */
19343 });
19344
19345 Roo.apply(Roo.bootstrap.ComboBox,  {
19346     
19347     header : {
19348         tag: 'div',
19349         cls: 'modal-header',
19350         cn: [
19351             {
19352                 tag: 'h4',
19353                 cls: 'modal-title'
19354             }
19355         ]
19356     },
19357     
19358     body : {
19359         tag: 'div',
19360         cls: 'modal-body',
19361         cn: [
19362             {
19363                 tag: 'ul',
19364                 cls: 'list-group'
19365             }
19366         ]
19367     },
19368     
19369     listItemRadio : {
19370         tag: 'li',
19371         cls: 'list-group-item',
19372         cn: [
19373             {
19374                 tag: 'span',
19375                 cls: 'roo-combobox-list-group-item-value'
19376             },
19377             {
19378                 tag: 'div',
19379                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19380                 cn: [
19381                     {
19382                         tag: 'input',
19383                         type: 'radio'
19384                     },
19385                     {
19386                         tag: 'label'
19387                     }
19388                 ]
19389             }
19390         ]
19391     },
19392     
19393     listItemCheckbox : {
19394         tag: 'li',
19395         cls: 'list-group-item',
19396         cn: [
19397             {
19398                 tag: 'span',
19399                 cls: 'roo-combobox-list-group-item-value'
19400             },
19401             {
19402                 tag: 'div',
19403                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19404                 cn: [
19405                     {
19406                         tag: 'input',
19407                         type: 'checkbox'
19408                     },
19409                     {
19410                         tag: 'label'
19411                     }
19412                 ]
19413             }
19414         ]
19415     },
19416     
19417     emptyResult : {
19418         tag: 'div',
19419         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19420     },
19421     
19422     footer : {
19423         tag: 'div',
19424         cls: 'modal-footer',
19425         cn: [
19426             {
19427                 tag: 'div',
19428                 cls: 'row',
19429                 cn: [
19430                     {
19431                         tag: 'div',
19432                         cls: 'col-xs-6 text-left',
19433                         cn: {
19434                             tag: 'button',
19435                             cls: 'btn btn-danger roo-touch-view-cancel',
19436                             html: 'Cancel'
19437                         }
19438                     },
19439                     {
19440                         tag: 'div',
19441                         cls: 'col-xs-6 text-right',
19442                         cn: {
19443                             tag: 'button',
19444                             cls: 'btn btn-success roo-touch-view-ok',
19445                             html: 'OK'
19446                         }
19447                     }
19448                 ]
19449             }
19450         ]
19451         
19452     }
19453 });
19454
19455 Roo.apply(Roo.bootstrap.ComboBox,  {
19456     
19457     touchViewTemplate : {
19458         tag: 'div',
19459         cls: 'modal fade roo-combobox-touch-view',
19460         cn: [
19461             {
19462                 tag: 'div',
19463                 cls: 'modal-dialog',
19464                 style : 'position:fixed', // we have to fix position....
19465                 cn: [
19466                     {
19467                         tag: 'div',
19468                         cls: 'modal-content',
19469                         cn: [
19470                             Roo.bootstrap.ComboBox.header,
19471                             Roo.bootstrap.ComboBox.body,
19472                             Roo.bootstrap.ComboBox.footer
19473                         ]
19474                     }
19475                 ]
19476             }
19477         ]
19478     }
19479 });/*
19480  * Based on:
19481  * Ext JS Library 1.1.1
19482  * Copyright(c) 2006-2007, Ext JS, LLC.
19483  *
19484  * Originally Released Under LGPL - original licence link has changed is not relivant.
19485  *
19486  * Fork - LGPL
19487  * <script type="text/javascript">
19488  */
19489
19490 /**
19491  * @class Roo.View
19492  * @extends Roo.util.Observable
19493  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19494  * This class also supports single and multi selection modes. <br>
19495  * Create a data model bound view:
19496  <pre><code>
19497  var store = new Roo.data.Store(...);
19498
19499  var view = new Roo.View({
19500     el : "my-element",
19501     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19502  
19503     singleSelect: true,
19504     selectedClass: "ydataview-selected",
19505     store: store
19506  });
19507
19508  // listen for node click?
19509  view.on("click", function(vw, index, node, e){
19510  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19511  });
19512
19513  // load XML data
19514  dataModel.load("foobar.xml");
19515  </code></pre>
19516  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19517  * <br><br>
19518  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19519  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19520  * 
19521  * Note: old style constructor is still suported (container, template, config)
19522  * 
19523  * @constructor
19524  * Create a new View
19525  * @param {Object} config The config object
19526  * 
19527  */
19528 Roo.View = function(config, depreciated_tpl, depreciated_config){
19529     
19530     this.parent = false;
19531     
19532     if (typeof(depreciated_tpl) == 'undefined') {
19533         // new way.. - universal constructor.
19534         Roo.apply(this, config);
19535         this.el  = Roo.get(this.el);
19536     } else {
19537         // old format..
19538         this.el  = Roo.get(config);
19539         this.tpl = depreciated_tpl;
19540         Roo.apply(this, depreciated_config);
19541     }
19542     this.wrapEl  = this.el.wrap().wrap();
19543     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19544     
19545     
19546     if(typeof(this.tpl) == "string"){
19547         this.tpl = new Roo.Template(this.tpl);
19548     } else {
19549         // support xtype ctors..
19550         this.tpl = new Roo.factory(this.tpl, Roo);
19551     }
19552     
19553     
19554     this.tpl.compile();
19555     
19556     /** @private */
19557     this.addEvents({
19558         /**
19559          * @event beforeclick
19560          * Fires before a click is processed. Returns false to cancel the default action.
19561          * @param {Roo.View} this
19562          * @param {Number} index The index of the target node
19563          * @param {HTMLElement} node The target node
19564          * @param {Roo.EventObject} e The raw event object
19565          */
19566             "beforeclick" : true,
19567         /**
19568          * @event click
19569          * Fires when a template node is clicked.
19570          * @param {Roo.View} this
19571          * @param {Number} index The index of the target node
19572          * @param {HTMLElement} node The target node
19573          * @param {Roo.EventObject} e The raw event object
19574          */
19575             "click" : true,
19576         /**
19577          * @event dblclick
19578          * Fires when a template node is double clicked.
19579          * @param {Roo.View} this
19580          * @param {Number} index The index of the target node
19581          * @param {HTMLElement} node The target node
19582          * @param {Roo.EventObject} e The raw event object
19583          */
19584             "dblclick" : true,
19585         /**
19586          * @event contextmenu
19587          * Fires when a template node is right clicked.
19588          * @param {Roo.View} this
19589          * @param {Number} index The index of the target node
19590          * @param {HTMLElement} node The target node
19591          * @param {Roo.EventObject} e The raw event object
19592          */
19593             "contextmenu" : true,
19594         /**
19595          * @event selectionchange
19596          * Fires when the selected nodes change.
19597          * @param {Roo.View} this
19598          * @param {Array} selections Array of the selected nodes
19599          */
19600             "selectionchange" : true,
19601     
19602         /**
19603          * @event beforeselect
19604          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19605          * @param {Roo.View} this
19606          * @param {HTMLElement} node The node to be selected
19607          * @param {Array} selections Array of currently selected nodes
19608          */
19609             "beforeselect" : true,
19610         /**
19611          * @event preparedata
19612          * Fires on every row to render, to allow you to change the data.
19613          * @param {Roo.View} this
19614          * @param {Object} data to be rendered (change this)
19615          */
19616           "preparedata" : true
19617           
19618           
19619         });
19620
19621
19622
19623     this.el.on({
19624         "click": this.onClick,
19625         "dblclick": this.onDblClick,
19626         "contextmenu": this.onContextMenu,
19627         scope:this
19628     });
19629
19630     this.selections = [];
19631     this.nodes = [];
19632     this.cmp = new Roo.CompositeElementLite([]);
19633     if(this.store){
19634         this.store = Roo.factory(this.store, Roo.data);
19635         this.setStore(this.store, true);
19636     }
19637     
19638     if ( this.footer && this.footer.xtype) {
19639            
19640          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19641         
19642         this.footer.dataSource = this.store;
19643         this.footer.container = fctr;
19644         this.footer = Roo.factory(this.footer, Roo);
19645         fctr.insertFirst(this.el);
19646         
19647         // this is a bit insane - as the paging toolbar seems to detach the el..
19648 //        dom.parentNode.parentNode.parentNode
19649          // they get detached?
19650     }
19651     
19652     
19653     Roo.View.superclass.constructor.call(this);
19654     
19655     
19656 };
19657
19658 Roo.extend(Roo.View, Roo.util.Observable, {
19659     
19660      /**
19661      * @cfg {Roo.data.Store} store Data store to load data from.
19662      */
19663     store : false,
19664     
19665     /**
19666      * @cfg {String|Roo.Element} el The container element.
19667      */
19668     el : '',
19669     
19670     /**
19671      * @cfg {String|Roo.Template} tpl The template used by this View 
19672      */
19673     tpl : false,
19674     /**
19675      * @cfg {String} dataName the named area of the template to use as the data area
19676      *                          Works with domtemplates roo-name="name"
19677      */
19678     dataName: false,
19679     /**
19680      * @cfg {String} selectedClass The css class to add to selected nodes
19681      */
19682     selectedClass : "x-view-selected",
19683      /**
19684      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19685      */
19686     emptyText : "",
19687     
19688     /**
19689      * @cfg {String} text to display on mask (default Loading)
19690      */
19691     mask : false,
19692     /**
19693      * @cfg {Boolean} multiSelect Allow multiple selection
19694      */
19695     multiSelect : false,
19696     /**
19697      * @cfg {Boolean} singleSelect Allow single selection
19698      */
19699     singleSelect:  false,
19700     
19701     /**
19702      * @cfg {Boolean} toggleSelect - selecting 
19703      */
19704     toggleSelect : false,
19705     
19706     /**
19707      * @cfg {Boolean} tickable - selecting 
19708      */
19709     tickable : false,
19710     
19711     /**
19712      * Returns the element this view is bound to.
19713      * @return {Roo.Element}
19714      */
19715     getEl : function(){
19716         return this.wrapEl;
19717     },
19718     
19719     
19720
19721     /**
19722      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19723      */
19724     refresh : function(){
19725         //Roo.log('refresh');
19726         var t = this.tpl;
19727         
19728         // if we are using something like 'domtemplate', then
19729         // the what gets used is:
19730         // t.applySubtemplate(NAME, data, wrapping data..)
19731         // the outer template then get' applied with
19732         //     the store 'extra data'
19733         // and the body get's added to the
19734         //      roo-name="data" node?
19735         //      <span class='roo-tpl-{name}'></span> ?????
19736         
19737         
19738         
19739         this.clearSelections();
19740         this.el.update("");
19741         var html = [];
19742         var records = this.store.getRange();
19743         if(records.length < 1) {
19744             
19745             // is this valid??  = should it render a template??
19746             
19747             this.el.update(this.emptyText);
19748             return;
19749         }
19750         var el = this.el;
19751         if (this.dataName) {
19752             this.el.update(t.apply(this.store.meta)); //????
19753             el = this.el.child('.roo-tpl-' + this.dataName);
19754         }
19755         
19756         for(var i = 0, len = records.length; i < len; i++){
19757             var data = this.prepareData(records[i].data, i, records[i]);
19758             this.fireEvent("preparedata", this, data, i, records[i]);
19759             
19760             var d = Roo.apply({}, data);
19761             
19762             if(this.tickable){
19763                 Roo.apply(d, {'roo-id' : Roo.id()});
19764                 
19765                 var _this = this;
19766             
19767                 Roo.each(this.parent.item, function(item){
19768                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19769                         return;
19770                     }
19771                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19772                 });
19773             }
19774             
19775             html[html.length] = Roo.util.Format.trim(
19776                 this.dataName ?
19777                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19778                     t.apply(d)
19779             );
19780         }
19781         
19782         
19783         
19784         el.update(html.join(""));
19785         this.nodes = el.dom.childNodes;
19786         this.updateIndexes(0);
19787     },
19788     
19789
19790     /**
19791      * Function to override to reformat the data that is sent to
19792      * the template for each node.
19793      * DEPRICATED - use the preparedata event handler.
19794      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19795      * a JSON object for an UpdateManager bound view).
19796      */
19797     prepareData : function(data, index, record)
19798     {
19799         this.fireEvent("preparedata", this, data, index, record);
19800         return data;
19801     },
19802
19803     onUpdate : function(ds, record){
19804         // Roo.log('on update');   
19805         this.clearSelections();
19806         var index = this.store.indexOf(record);
19807         var n = this.nodes[index];
19808         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19809         n.parentNode.removeChild(n);
19810         this.updateIndexes(index, index);
19811     },
19812
19813     
19814     
19815 // --------- FIXME     
19816     onAdd : function(ds, records, index)
19817     {
19818         //Roo.log(['on Add', ds, records, index] );        
19819         this.clearSelections();
19820         if(this.nodes.length == 0){
19821             this.refresh();
19822             return;
19823         }
19824         var n = this.nodes[index];
19825         for(var i = 0, len = records.length; i < len; i++){
19826             var d = this.prepareData(records[i].data, i, records[i]);
19827             if(n){
19828                 this.tpl.insertBefore(n, d);
19829             }else{
19830                 
19831                 this.tpl.append(this.el, d);
19832             }
19833         }
19834         this.updateIndexes(index);
19835     },
19836
19837     onRemove : function(ds, record, index){
19838        // Roo.log('onRemove');
19839         this.clearSelections();
19840         var el = this.dataName  ?
19841             this.el.child('.roo-tpl-' + this.dataName) :
19842             this.el; 
19843         
19844         el.dom.removeChild(this.nodes[index]);
19845         this.updateIndexes(index);
19846     },
19847
19848     /**
19849      * Refresh an individual node.
19850      * @param {Number} index
19851      */
19852     refreshNode : function(index){
19853         this.onUpdate(this.store, this.store.getAt(index));
19854     },
19855
19856     updateIndexes : function(startIndex, endIndex){
19857         var ns = this.nodes;
19858         startIndex = startIndex || 0;
19859         endIndex = endIndex || ns.length - 1;
19860         for(var i = startIndex; i <= endIndex; i++){
19861             ns[i].nodeIndex = i;
19862         }
19863     },
19864
19865     /**
19866      * Changes the data store this view uses and refresh the view.
19867      * @param {Store} store
19868      */
19869     setStore : function(store, initial){
19870         if(!initial && this.store){
19871             this.store.un("datachanged", this.refresh);
19872             this.store.un("add", this.onAdd);
19873             this.store.un("remove", this.onRemove);
19874             this.store.un("update", this.onUpdate);
19875             this.store.un("clear", this.refresh);
19876             this.store.un("beforeload", this.onBeforeLoad);
19877             this.store.un("load", this.onLoad);
19878             this.store.un("loadexception", this.onLoad);
19879         }
19880         if(store){
19881           
19882             store.on("datachanged", this.refresh, this);
19883             store.on("add", this.onAdd, this);
19884             store.on("remove", this.onRemove, this);
19885             store.on("update", this.onUpdate, this);
19886             store.on("clear", this.refresh, this);
19887             store.on("beforeload", this.onBeforeLoad, this);
19888             store.on("load", this.onLoad, this);
19889             store.on("loadexception", this.onLoad, this);
19890         }
19891         
19892         if(store){
19893             this.refresh();
19894         }
19895     },
19896     /**
19897      * onbeforeLoad - masks the loading area.
19898      *
19899      */
19900     onBeforeLoad : function(store,opts)
19901     {
19902          //Roo.log('onBeforeLoad');   
19903         if (!opts.add) {
19904             this.el.update("");
19905         }
19906         this.el.mask(this.mask ? this.mask : "Loading" ); 
19907     },
19908     onLoad : function ()
19909     {
19910         this.el.unmask();
19911     },
19912     
19913
19914     /**
19915      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19916      * @param {HTMLElement} node
19917      * @return {HTMLElement} The template node
19918      */
19919     findItemFromChild : function(node){
19920         var el = this.dataName  ?
19921             this.el.child('.roo-tpl-' + this.dataName,true) :
19922             this.el.dom; 
19923         
19924         if(!node || node.parentNode == el){
19925                     return node;
19926             }
19927             var p = node.parentNode;
19928             while(p && p != el){
19929             if(p.parentNode == el){
19930                 return p;
19931             }
19932             p = p.parentNode;
19933         }
19934             return null;
19935     },
19936
19937     /** @ignore */
19938     onClick : function(e){
19939         var item = this.findItemFromChild(e.getTarget());
19940         if(item){
19941             var index = this.indexOf(item);
19942             if(this.onItemClick(item, index, e) !== false){
19943                 this.fireEvent("click", this, index, item, e);
19944             }
19945         }else{
19946             this.clearSelections();
19947         }
19948     },
19949
19950     /** @ignore */
19951     onContextMenu : function(e){
19952         var item = this.findItemFromChild(e.getTarget());
19953         if(item){
19954             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19955         }
19956     },
19957
19958     /** @ignore */
19959     onDblClick : function(e){
19960         var item = this.findItemFromChild(e.getTarget());
19961         if(item){
19962             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19963         }
19964     },
19965
19966     onItemClick : function(item, index, e)
19967     {
19968         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19969             return false;
19970         }
19971         if (this.toggleSelect) {
19972             var m = this.isSelected(item) ? 'unselect' : 'select';
19973             //Roo.log(m);
19974             var _t = this;
19975             _t[m](item, true, false);
19976             return true;
19977         }
19978         if(this.multiSelect || this.singleSelect){
19979             if(this.multiSelect && e.shiftKey && this.lastSelection){
19980                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19981             }else{
19982                 this.select(item, this.multiSelect && e.ctrlKey);
19983                 this.lastSelection = item;
19984             }
19985             
19986             if(!this.tickable){
19987                 e.preventDefault();
19988             }
19989             
19990         }
19991         return true;
19992     },
19993
19994     /**
19995      * Get the number of selected nodes.
19996      * @return {Number}
19997      */
19998     getSelectionCount : function(){
19999         return this.selections.length;
20000     },
20001
20002     /**
20003      * Get the currently selected nodes.
20004      * @return {Array} An array of HTMLElements
20005      */
20006     getSelectedNodes : function(){
20007         return this.selections;
20008     },
20009
20010     /**
20011      * Get the indexes of the selected nodes.
20012      * @return {Array}
20013      */
20014     getSelectedIndexes : function(){
20015         var indexes = [], s = this.selections;
20016         for(var i = 0, len = s.length; i < len; i++){
20017             indexes.push(s[i].nodeIndex);
20018         }
20019         return indexes;
20020     },
20021
20022     /**
20023      * Clear all selections
20024      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20025      */
20026     clearSelections : function(suppressEvent){
20027         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20028             this.cmp.elements = this.selections;
20029             this.cmp.removeClass(this.selectedClass);
20030             this.selections = [];
20031             if(!suppressEvent){
20032                 this.fireEvent("selectionchange", this, this.selections);
20033             }
20034         }
20035     },
20036
20037     /**
20038      * Returns true if the passed node is selected
20039      * @param {HTMLElement/Number} node The node or node index
20040      * @return {Boolean}
20041      */
20042     isSelected : function(node){
20043         var s = this.selections;
20044         if(s.length < 1){
20045             return false;
20046         }
20047         node = this.getNode(node);
20048         return s.indexOf(node) !== -1;
20049     },
20050
20051     /**
20052      * Selects nodes.
20053      * @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
20054      * @param {Boolean} keepExisting (optional) true to keep existing selections
20055      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20056      */
20057     select : function(nodeInfo, keepExisting, suppressEvent){
20058         if(nodeInfo instanceof Array){
20059             if(!keepExisting){
20060                 this.clearSelections(true);
20061             }
20062             for(var i = 0, len = nodeInfo.length; i < len; i++){
20063                 this.select(nodeInfo[i], true, true);
20064             }
20065             return;
20066         } 
20067         var node = this.getNode(nodeInfo);
20068         if(!node || this.isSelected(node)){
20069             return; // already selected.
20070         }
20071         if(!keepExisting){
20072             this.clearSelections(true);
20073         }
20074         
20075         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20076             Roo.fly(node).addClass(this.selectedClass);
20077             this.selections.push(node);
20078             if(!suppressEvent){
20079                 this.fireEvent("selectionchange", this, this.selections);
20080             }
20081         }
20082         
20083         
20084     },
20085       /**
20086      * Unselects nodes.
20087      * @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
20088      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20089      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20090      */
20091     unselect : function(nodeInfo, keepExisting, suppressEvent)
20092     {
20093         if(nodeInfo instanceof Array){
20094             Roo.each(this.selections, function(s) {
20095                 this.unselect(s, nodeInfo);
20096             }, this);
20097             return;
20098         }
20099         var node = this.getNode(nodeInfo);
20100         if(!node || !this.isSelected(node)){
20101             //Roo.log("not selected");
20102             return; // not selected.
20103         }
20104         // fireevent???
20105         var ns = [];
20106         Roo.each(this.selections, function(s) {
20107             if (s == node ) {
20108                 Roo.fly(node).removeClass(this.selectedClass);
20109
20110                 return;
20111             }
20112             ns.push(s);
20113         },this);
20114         
20115         this.selections= ns;
20116         this.fireEvent("selectionchange", this, this.selections);
20117     },
20118
20119     /**
20120      * Gets a template node.
20121      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20122      * @return {HTMLElement} The node or null if it wasn't found
20123      */
20124     getNode : function(nodeInfo){
20125         if(typeof nodeInfo == "string"){
20126             return document.getElementById(nodeInfo);
20127         }else if(typeof nodeInfo == "number"){
20128             return this.nodes[nodeInfo];
20129         }
20130         return nodeInfo;
20131     },
20132
20133     /**
20134      * Gets a range template nodes.
20135      * @param {Number} startIndex
20136      * @param {Number} endIndex
20137      * @return {Array} An array of nodes
20138      */
20139     getNodes : function(start, end){
20140         var ns = this.nodes;
20141         start = start || 0;
20142         end = typeof end == "undefined" ? ns.length - 1 : end;
20143         var nodes = [];
20144         if(start <= end){
20145             for(var i = start; i <= end; i++){
20146                 nodes.push(ns[i]);
20147             }
20148         } else{
20149             for(var i = start; i >= end; i--){
20150                 nodes.push(ns[i]);
20151             }
20152         }
20153         return nodes;
20154     },
20155
20156     /**
20157      * Finds the index of the passed node
20158      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20159      * @return {Number} The index of the node or -1
20160      */
20161     indexOf : function(node){
20162         node = this.getNode(node);
20163         if(typeof node.nodeIndex == "number"){
20164             return node.nodeIndex;
20165         }
20166         var ns = this.nodes;
20167         for(var i = 0, len = ns.length; i < len; i++){
20168             if(ns[i] == node){
20169                 return i;
20170             }
20171         }
20172         return -1;
20173     }
20174 });
20175 /*
20176  * - LGPL
20177  *
20178  * based on jquery fullcalendar
20179  * 
20180  */
20181
20182 Roo.bootstrap = Roo.bootstrap || {};
20183 /**
20184  * @class Roo.bootstrap.Calendar
20185  * @extends Roo.bootstrap.Component
20186  * Bootstrap Calendar class
20187  * @cfg {Boolean} loadMask (true|false) default false
20188  * @cfg {Object} header generate the user specific header of the calendar, default false
20189
20190  * @constructor
20191  * Create a new Container
20192  * @param {Object} config The config object
20193  */
20194
20195
20196
20197 Roo.bootstrap.Calendar = function(config){
20198     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20199      this.addEvents({
20200         /**
20201              * @event select
20202              * Fires when a date is selected
20203              * @param {DatePicker} this
20204              * @param {Date} date The selected date
20205              */
20206         'select': true,
20207         /**
20208              * @event monthchange
20209              * Fires when the displayed month changes 
20210              * @param {DatePicker} this
20211              * @param {Date} date The selected month
20212              */
20213         'monthchange': true,
20214         /**
20215              * @event evententer
20216              * Fires when mouse over an event
20217              * @param {Calendar} this
20218              * @param {event} Event
20219              */
20220         'evententer': true,
20221         /**
20222              * @event eventleave
20223              * Fires when the mouse leaves an
20224              * @param {Calendar} this
20225              * @param {event}
20226              */
20227         'eventleave': true,
20228         /**
20229              * @event eventclick
20230              * Fires when the mouse click an
20231              * @param {Calendar} this
20232              * @param {event}
20233              */
20234         'eventclick': true
20235         
20236     });
20237
20238 };
20239
20240 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20241     
20242           /**
20243      * @cfg {Roo.data.Store} store
20244      * The data source for the calendar
20245      */
20246         store : false,
20247      /**
20248      * @cfg {Number} startDay
20249      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20250      */
20251     startDay : 0,
20252     
20253     loadMask : false,
20254     
20255     header : false,
20256       
20257     getAutoCreate : function(){
20258         
20259         
20260         var fc_button = function(name, corner, style, content ) {
20261             return Roo.apply({},{
20262                 tag : 'span',
20263                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20264                          (corner.length ?
20265                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20266                             ''
20267                         ),
20268                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20269                 unselectable: 'on'
20270             });
20271         };
20272         
20273         var header = {};
20274         
20275         if(!this.header){
20276             header = {
20277                 tag : 'table',
20278                 cls : 'fc-header',
20279                 style : 'width:100%',
20280                 cn : [
20281                     {
20282                         tag: 'tr',
20283                         cn : [
20284                             {
20285                                 tag : 'td',
20286                                 cls : 'fc-header-left',
20287                                 cn : [
20288                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20289                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20290                                     { tag: 'span', cls: 'fc-header-space' },
20291                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20292
20293
20294                                 ]
20295                             },
20296
20297                             {
20298                                 tag : 'td',
20299                                 cls : 'fc-header-center',
20300                                 cn : [
20301                                     {
20302                                         tag: 'span',
20303                                         cls: 'fc-header-title',
20304                                         cn : {
20305                                             tag: 'H2',
20306                                             html : 'month / year'
20307                                         }
20308                                     }
20309
20310                                 ]
20311                             },
20312                             {
20313                                 tag : 'td',
20314                                 cls : 'fc-header-right',
20315                                 cn : [
20316                               /*      fc_button('month', 'left', '', 'month' ),
20317                                     fc_button('week', '', '', 'week' ),
20318                                     fc_button('day', 'right', '', 'day' )
20319                                 */    
20320
20321                                 ]
20322                             }
20323
20324                         ]
20325                     }
20326                 ]
20327             };
20328         }
20329         
20330         header = this.header;
20331         
20332        
20333         var cal_heads = function() {
20334             var ret = [];
20335             // fixme - handle this.
20336             
20337             for (var i =0; i < Date.dayNames.length; i++) {
20338                 var d = Date.dayNames[i];
20339                 ret.push({
20340                     tag: 'th',
20341                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20342                     html : d.substring(0,3)
20343                 });
20344                 
20345             }
20346             ret[0].cls += ' fc-first';
20347             ret[6].cls += ' fc-last';
20348             return ret;
20349         };
20350         var cal_cell = function(n) {
20351             return  {
20352                 tag: 'td',
20353                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20354                 cn : [
20355                     {
20356                         cn : [
20357                             {
20358                                 cls: 'fc-day-number',
20359                                 html: 'D'
20360                             },
20361                             {
20362                                 cls: 'fc-day-content',
20363                              
20364                                 cn : [
20365                                      {
20366                                         style: 'position: relative;' // height: 17px;
20367                                     }
20368                                 ]
20369                             }
20370                             
20371                             
20372                         ]
20373                     }
20374                 ]
20375                 
20376             }
20377         };
20378         var cal_rows = function() {
20379             
20380             var ret = [];
20381             for (var r = 0; r < 6; r++) {
20382                 var row= {
20383                     tag : 'tr',
20384                     cls : 'fc-week',
20385                     cn : []
20386                 };
20387                 
20388                 for (var i =0; i < Date.dayNames.length; i++) {
20389                     var d = Date.dayNames[i];
20390                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20391
20392                 }
20393                 row.cn[0].cls+=' fc-first';
20394                 row.cn[0].cn[0].style = 'min-height:90px';
20395                 row.cn[6].cls+=' fc-last';
20396                 ret.push(row);
20397                 
20398             }
20399             ret[0].cls += ' fc-first';
20400             ret[4].cls += ' fc-prev-last';
20401             ret[5].cls += ' fc-last';
20402             return ret;
20403             
20404         };
20405         
20406         var cal_table = {
20407             tag: 'table',
20408             cls: 'fc-border-separate',
20409             style : 'width:100%',
20410             cellspacing  : 0,
20411             cn : [
20412                 { 
20413                     tag: 'thead',
20414                     cn : [
20415                         { 
20416                             tag: 'tr',
20417                             cls : 'fc-first fc-last',
20418                             cn : cal_heads()
20419                         }
20420                     ]
20421                 },
20422                 { 
20423                     tag: 'tbody',
20424                     cn : cal_rows()
20425                 }
20426                   
20427             ]
20428         };
20429          
20430          var cfg = {
20431             cls : 'fc fc-ltr',
20432             cn : [
20433                 header,
20434                 {
20435                     cls : 'fc-content',
20436                     style : "position: relative;",
20437                     cn : [
20438                         {
20439                             cls : 'fc-view fc-view-month fc-grid',
20440                             style : 'position: relative',
20441                             unselectable : 'on',
20442                             cn : [
20443                                 {
20444                                     cls : 'fc-event-container',
20445                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20446                                 },
20447                                 cal_table
20448                             ]
20449                         }
20450                     ]
20451     
20452                 }
20453            ] 
20454             
20455         };
20456         
20457          
20458         
20459         return cfg;
20460     },
20461     
20462     
20463     initEvents : function()
20464     {
20465         if(!this.store){
20466             throw "can not find store for calendar";
20467         }
20468         
20469         var mark = {
20470             tag: "div",
20471             cls:"x-dlg-mask",
20472             style: "text-align:center",
20473             cn: [
20474                 {
20475                     tag: "div",
20476                     style: "background-color:white;width:50%;margin:250 auto",
20477                     cn: [
20478                         {
20479                             tag: "img",
20480                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20481                         },
20482                         {
20483                             tag: "span",
20484                             html: "Loading"
20485                         }
20486                         
20487                     ]
20488                 }
20489             ]
20490         };
20491         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20492         
20493         var size = this.el.select('.fc-content', true).first().getSize();
20494         this.maskEl.setSize(size.width, size.height);
20495         this.maskEl.enableDisplayMode("block");
20496         if(!this.loadMask){
20497             this.maskEl.hide();
20498         }
20499         
20500         this.store = Roo.factory(this.store, Roo.data);
20501         this.store.on('load', this.onLoad, this);
20502         this.store.on('beforeload', this.onBeforeLoad, this);
20503         
20504         this.resize();
20505         
20506         this.cells = this.el.select('.fc-day',true);
20507         //Roo.log(this.cells);
20508         this.textNodes = this.el.query('.fc-day-number');
20509         this.cells.addClassOnOver('fc-state-hover');
20510         
20511         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20512         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20513         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20514         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20515         
20516         this.on('monthchange', this.onMonthChange, this);
20517         
20518         this.update(new Date().clearTime());
20519     },
20520     
20521     resize : function() {
20522         var sz  = this.el.getSize();
20523         
20524         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20525         this.el.select('.fc-day-content div',true).setHeight(34);
20526     },
20527     
20528     
20529     // private
20530     showPrevMonth : function(e){
20531         this.update(this.activeDate.add("mo", -1));
20532     },
20533     showToday : function(e){
20534         this.update(new Date().clearTime());
20535     },
20536     // private
20537     showNextMonth : function(e){
20538         this.update(this.activeDate.add("mo", 1));
20539     },
20540
20541     // private
20542     showPrevYear : function(){
20543         this.update(this.activeDate.add("y", -1));
20544     },
20545
20546     // private
20547     showNextYear : function(){
20548         this.update(this.activeDate.add("y", 1));
20549     },
20550
20551     
20552    // private
20553     update : function(date)
20554     {
20555         var vd = this.activeDate;
20556         this.activeDate = date;
20557 //        if(vd && this.el){
20558 //            var t = date.getTime();
20559 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20560 //                Roo.log('using add remove');
20561 //                
20562 //                this.fireEvent('monthchange', this, date);
20563 //                
20564 //                this.cells.removeClass("fc-state-highlight");
20565 //                this.cells.each(function(c){
20566 //                   if(c.dateValue == t){
20567 //                       c.addClass("fc-state-highlight");
20568 //                       setTimeout(function(){
20569 //                            try{c.dom.firstChild.focus();}catch(e){}
20570 //                       }, 50);
20571 //                       return false;
20572 //                   }
20573 //                   return true;
20574 //                });
20575 //                return;
20576 //            }
20577 //        }
20578         
20579         var days = date.getDaysInMonth();
20580         
20581         var firstOfMonth = date.getFirstDateOfMonth();
20582         var startingPos = firstOfMonth.getDay()-this.startDay;
20583         
20584         if(startingPos < this.startDay){
20585             startingPos += 7;
20586         }
20587         
20588         var pm = date.add(Date.MONTH, -1);
20589         var prevStart = pm.getDaysInMonth()-startingPos;
20590 //        
20591         this.cells = this.el.select('.fc-day',true);
20592         this.textNodes = this.el.query('.fc-day-number');
20593         this.cells.addClassOnOver('fc-state-hover');
20594         
20595         var cells = this.cells.elements;
20596         var textEls = this.textNodes;
20597         
20598         Roo.each(cells, function(cell){
20599             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20600         });
20601         
20602         days += startingPos;
20603
20604         // convert everything to numbers so it's fast
20605         var day = 86400000;
20606         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20607         //Roo.log(d);
20608         //Roo.log(pm);
20609         //Roo.log(prevStart);
20610         
20611         var today = new Date().clearTime().getTime();
20612         var sel = date.clearTime().getTime();
20613         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20614         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20615         var ddMatch = this.disabledDatesRE;
20616         var ddText = this.disabledDatesText;
20617         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20618         var ddaysText = this.disabledDaysText;
20619         var format = this.format;
20620         
20621         var setCellClass = function(cal, cell){
20622             cell.row = 0;
20623             cell.events = [];
20624             cell.more = [];
20625             //Roo.log('set Cell Class');
20626             cell.title = "";
20627             var t = d.getTime();
20628             
20629             //Roo.log(d);
20630             
20631             cell.dateValue = t;
20632             if(t == today){
20633                 cell.className += " fc-today";
20634                 cell.className += " fc-state-highlight";
20635                 cell.title = cal.todayText;
20636             }
20637             if(t == sel){
20638                 // disable highlight in other month..
20639                 //cell.className += " fc-state-highlight";
20640                 
20641             }
20642             // disabling
20643             if(t < min) {
20644                 cell.className = " fc-state-disabled";
20645                 cell.title = cal.minText;
20646                 return;
20647             }
20648             if(t > max) {
20649                 cell.className = " fc-state-disabled";
20650                 cell.title = cal.maxText;
20651                 return;
20652             }
20653             if(ddays){
20654                 if(ddays.indexOf(d.getDay()) != -1){
20655                     cell.title = ddaysText;
20656                     cell.className = " fc-state-disabled";
20657                 }
20658             }
20659             if(ddMatch && format){
20660                 var fvalue = d.dateFormat(format);
20661                 if(ddMatch.test(fvalue)){
20662                     cell.title = ddText.replace("%0", fvalue);
20663                     cell.className = " fc-state-disabled";
20664                 }
20665             }
20666             
20667             if (!cell.initialClassName) {
20668                 cell.initialClassName = cell.dom.className;
20669             }
20670             
20671             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20672         };
20673
20674         var i = 0;
20675         
20676         for(; i < startingPos; i++) {
20677             textEls[i].innerHTML = (++prevStart);
20678             d.setDate(d.getDate()+1);
20679             
20680             cells[i].className = "fc-past fc-other-month";
20681             setCellClass(this, cells[i]);
20682         }
20683         
20684         var intDay = 0;
20685         
20686         for(; i < days; i++){
20687             intDay = i - startingPos + 1;
20688             textEls[i].innerHTML = (intDay);
20689             d.setDate(d.getDate()+1);
20690             
20691             cells[i].className = ''; // "x-date-active";
20692             setCellClass(this, cells[i]);
20693         }
20694         var extraDays = 0;
20695         
20696         for(; i < 42; i++) {
20697             textEls[i].innerHTML = (++extraDays);
20698             d.setDate(d.getDate()+1);
20699             
20700             cells[i].className = "fc-future fc-other-month";
20701             setCellClass(this, cells[i]);
20702         }
20703         
20704         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20705         
20706         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20707         
20708         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20709         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20710         
20711         if(totalRows != 6){
20712             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20713             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20714         }
20715         
20716         this.fireEvent('monthchange', this, date);
20717         
20718         
20719         /*
20720         if(!this.internalRender){
20721             var main = this.el.dom.firstChild;
20722             var w = main.offsetWidth;
20723             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20724             Roo.fly(main).setWidth(w);
20725             this.internalRender = true;
20726             // opera does not respect the auto grow header center column
20727             // then, after it gets a width opera refuses to recalculate
20728             // without a second pass
20729             if(Roo.isOpera && !this.secondPass){
20730                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20731                 this.secondPass = true;
20732                 this.update.defer(10, this, [date]);
20733             }
20734         }
20735         */
20736         
20737     },
20738     
20739     findCell : function(dt) {
20740         dt = dt.clearTime().getTime();
20741         var ret = false;
20742         this.cells.each(function(c){
20743             //Roo.log("check " +c.dateValue + '?=' + dt);
20744             if(c.dateValue == dt){
20745                 ret = c;
20746                 return false;
20747             }
20748             return true;
20749         });
20750         
20751         return ret;
20752     },
20753     
20754     findCells : function(ev) {
20755         var s = ev.start.clone().clearTime().getTime();
20756        // Roo.log(s);
20757         var e= ev.end.clone().clearTime().getTime();
20758        // Roo.log(e);
20759         var ret = [];
20760         this.cells.each(function(c){
20761              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20762             
20763             if(c.dateValue > e){
20764                 return ;
20765             }
20766             if(c.dateValue < s){
20767                 return ;
20768             }
20769             ret.push(c);
20770         });
20771         
20772         return ret;    
20773     },
20774     
20775 //    findBestRow: function(cells)
20776 //    {
20777 //        var ret = 0;
20778 //        
20779 //        for (var i =0 ; i < cells.length;i++) {
20780 //            ret  = Math.max(cells[i].rows || 0,ret);
20781 //        }
20782 //        return ret;
20783 //        
20784 //    },
20785     
20786     
20787     addItem : function(ev)
20788     {
20789         // look for vertical location slot in
20790         var cells = this.findCells(ev);
20791         
20792 //        ev.row = this.findBestRow(cells);
20793         
20794         // work out the location.
20795         
20796         var crow = false;
20797         var rows = [];
20798         for(var i =0; i < cells.length; i++) {
20799             
20800             cells[i].row = cells[0].row;
20801             
20802             if(i == 0){
20803                 cells[i].row = cells[i].row + 1;
20804             }
20805             
20806             if (!crow) {
20807                 crow = {
20808                     start : cells[i],
20809                     end :  cells[i]
20810                 };
20811                 continue;
20812             }
20813             if (crow.start.getY() == cells[i].getY()) {
20814                 // on same row.
20815                 crow.end = cells[i];
20816                 continue;
20817             }
20818             // different row.
20819             rows.push(crow);
20820             crow = {
20821                 start: cells[i],
20822                 end : cells[i]
20823             };
20824             
20825         }
20826         
20827         rows.push(crow);
20828         ev.els = [];
20829         ev.rows = rows;
20830         ev.cells = cells;
20831         
20832         cells[0].events.push(ev);
20833         
20834         this.calevents.push(ev);
20835     },
20836     
20837     clearEvents: function() {
20838         
20839         if(!this.calevents){
20840             return;
20841         }
20842         
20843         Roo.each(this.cells.elements, function(c){
20844             c.row = 0;
20845             c.events = [];
20846             c.more = [];
20847         });
20848         
20849         Roo.each(this.calevents, function(e) {
20850             Roo.each(e.els, function(el) {
20851                 el.un('mouseenter' ,this.onEventEnter, this);
20852                 el.un('mouseleave' ,this.onEventLeave, this);
20853                 el.remove();
20854             },this);
20855         },this);
20856         
20857         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20858             e.remove();
20859         });
20860         
20861     },
20862     
20863     renderEvents: function()
20864     {   
20865         var _this = this;
20866         
20867         this.cells.each(function(c) {
20868             
20869             if(c.row < 5){
20870                 return;
20871             }
20872             
20873             var ev = c.events;
20874             
20875             var r = 4;
20876             if(c.row != c.events.length){
20877                 r = 4 - (4 - (c.row - c.events.length));
20878             }
20879             
20880             c.events = ev.slice(0, r);
20881             c.more = ev.slice(r);
20882             
20883             if(c.more.length && c.more.length == 1){
20884                 c.events.push(c.more.pop());
20885             }
20886             
20887             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20888             
20889         });
20890             
20891         this.cells.each(function(c) {
20892             
20893             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20894             
20895             
20896             for (var e = 0; e < c.events.length; e++){
20897                 var ev = c.events[e];
20898                 var rows = ev.rows;
20899                 
20900                 for(var i = 0; i < rows.length; i++) {
20901                 
20902                     // how many rows should it span..
20903
20904                     var  cfg = {
20905                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20906                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20907
20908                         unselectable : "on",
20909                         cn : [
20910                             {
20911                                 cls: 'fc-event-inner',
20912                                 cn : [
20913     //                                {
20914     //                                  tag:'span',
20915     //                                  cls: 'fc-event-time',
20916     //                                  html : cells.length > 1 ? '' : ev.time
20917     //                                },
20918                                     {
20919                                       tag:'span',
20920                                       cls: 'fc-event-title',
20921                                       html : String.format('{0}', ev.title)
20922                                     }
20923
20924
20925                                 ]
20926                             },
20927                             {
20928                                 cls: 'ui-resizable-handle ui-resizable-e',
20929                                 html : '&nbsp;&nbsp;&nbsp'
20930                             }
20931
20932                         ]
20933                     };
20934
20935                     if (i == 0) {
20936                         cfg.cls += ' fc-event-start';
20937                     }
20938                     if ((i+1) == rows.length) {
20939                         cfg.cls += ' fc-event-end';
20940                     }
20941
20942                     var ctr = _this.el.select('.fc-event-container',true).first();
20943                     var cg = ctr.createChild(cfg);
20944
20945                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20946                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20947
20948                     var r = (c.more.length) ? 1 : 0;
20949                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20950                     cg.setWidth(ebox.right - sbox.x -2);
20951
20952                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20953                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20954                     cg.on('click', _this.onEventClick, _this, ev);
20955
20956                     ev.els.push(cg);
20957                     
20958                 }
20959                 
20960             }
20961             
20962             
20963             if(c.more.length){
20964                 var  cfg = {
20965                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20966                     style : 'position: absolute',
20967                     unselectable : "on",
20968                     cn : [
20969                         {
20970                             cls: 'fc-event-inner',
20971                             cn : [
20972                                 {
20973                                   tag:'span',
20974                                   cls: 'fc-event-title',
20975                                   html : 'More'
20976                                 }
20977
20978
20979                             ]
20980                         },
20981                         {
20982                             cls: 'ui-resizable-handle ui-resizable-e',
20983                             html : '&nbsp;&nbsp;&nbsp'
20984                         }
20985
20986                     ]
20987                 };
20988
20989                 var ctr = _this.el.select('.fc-event-container',true).first();
20990                 var cg = ctr.createChild(cfg);
20991
20992                 var sbox = c.select('.fc-day-content',true).first().getBox();
20993                 var ebox = c.select('.fc-day-content',true).first().getBox();
20994                 //Roo.log(cg);
20995                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20996                 cg.setWidth(ebox.right - sbox.x -2);
20997
20998                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20999                 
21000             }
21001             
21002         });
21003         
21004         
21005         
21006     },
21007     
21008     onEventEnter: function (e, el,event,d) {
21009         this.fireEvent('evententer', this, el, event);
21010     },
21011     
21012     onEventLeave: function (e, el,event,d) {
21013         this.fireEvent('eventleave', this, el, event);
21014     },
21015     
21016     onEventClick: function (e, el,event,d) {
21017         this.fireEvent('eventclick', this, el, event);
21018     },
21019     
21020     onMonthChange: function () {
21021         this.store.load();
21022     },
21023     
21024     onMoreEventClick: function(e, el, more)
21025     {
21026         var _this = this;
21027         
21028         this.calpopover.placement = 'right';
21029         this.calpopover.setTitle('More');
21030         
21031         this.calpopover.setContent('');
21032         
21033         var ctr = this.calpopover.el.select('.popover-content', true).first();
21034         
21035         Roo.each(more, function(m){
21036             var cfg = {
21037                 cls : 'fc-event-hori fc-event-draggable',
21038                 html : m.title
21039             };
21040             var cg = ctr.createChild(cfg);
21041             
21042             cg.on('click', _this.onEventClick, _this, m);
21043         });
21044         
21045         this.calpopover.show(el);
21046         
21047         
21048     },
21049     
21050     onLoad: function () 
21051     {   
21052         this.calevents = [];
21053         var cal = this;
21054         
21055         if(this.store.getCount() > 0){
21056             this.store.data.each(function(d){
21057                cal.addItem({
21058                     id : d.data.id,
21059                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21060                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21061                     time : d.data.start_time,
21062                     title : d.data.title,
21063                     description : d.data.description,
21064                     venue : d.data.venue
21065                 });
21066             });
21067         }
21068         
21069         this.renderEvents();
21070         
21071         if(this.calevents.length && this.loadMask){
21072             this.maskEl.hide();
21073         }
21074     },
21075     
21076     onBeforeLoad: function()
21077     {
21078         this.clearEvents();
21079         if(this.loadMask){
21080             this.maskEl.show();
21081         }
21082     }
21083 });
21084
21085  
21086  /*
21087  * - LGPL
21088  *
21089  * element
21090  * 
21091  */
21092
21093 /**
21094  * @class Roo.bootstrap.Popover
21095  * @extends Roo.bootstrap.Component
21096  * @builder-top
21097  * @children Roo.bootstrap.Component
21098  * Bootstrap Popover class
21099  * @cfg {String} html contents of the popover   (or false to use children..)
21100  * @cfg {String} title of popover (or false to hide)
21101  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21102  * @cfg {String} trigger click || hover (or false to trigger manually)
21103  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21104  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21105  *      - if false and it has a 'parent' then it will be automatically added to that element
21106  *      - if string - Roo.get  will be called 
21107  * @cfg {Number} delay - delay before showing
21108  
21109  * @constructor
21110  * Create a new Popover
21111  * @param {Object} config The config object
21112  */
21113
21114 Roo.bootstrap.Popover = function(config){
21115     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21116     
21117     this.addEvents({
21118         // raw events
21119          /**
21120          * @event show
21121          * After the popover show
21122          * 
21123          * @param {Roo.bootstrap.Popover} this
21124          */
21125         "show" : true,
21126         /**
21127          * @event hide
21128          * After the popover hide
21129          * 
21130          * @param {Roo.bootstrap.Popover} this
21131          */
21132         "hide" : true
21133     });
21134 };
21135
21136 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21137     
21138     title: false,
21139     html: false,
21140     
21141     placement : 'right',
21142     trigger : 'hover', // hover
21143     modal : false,
21144     delay : 0,
21145     
21146     over: false,
21147     
21148     can_build_overlaid : false,
21149     
21150     maskEl : false, // the mask element
21151     headerEl : false,
21152     contentEl : false,
21153     alignEl : false, // when show is called with an element - this get's stored.
21154     
21155     getChildContainer : function()
21156     {
21157         return this.contentEl;
21158         
21159     },
21160     getPopoverHeader : function()
21161     {
21162         this.title = true; // flag not to hide it..
21163         this.headerEl.addClass('p-0');
21164         return this.headerEl
21165     },
21166     
21167     
21168     getAutoCreate : function(){
21169          
21170         var cfg = {
21171            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21172            style: 'display:block',
21173            cn : [
21174                 {
21175                     cls : 'arrow'
21176                 },
21177                 {
21178                     cls : 'popover-inner ',
21179                     cn : [
21180                         {
21181                             tag: 'h3',
21182                             cls: 'popover-title popover-header',
21183                             html : this.title === false ? '' : this.title
21184                         },
21185                         {
21186                             cls : 'popover-content popover-body '  + (this.cls || ''),
21187                             html : this.html || ''
21188                         }
21189                     ]
21190                     
21191                 }
21192            ]
21193         };
21194         
21195         return cfg;
21196     },
21197     /**
21198      * @param {string} the title
21199      */
21200     setTitle: function(str)
21201     {
21202         this.title = str;
21203         if (this.el) {
21204             this.headerEl.dom.innerHTML = str;
21205         }
21206         
21207     },
21208     /**
21209      * @param {string} the body content
21210      */
21211     setContent: function(str)
21212     {
21213         this.html = str;
21214         if (this.contentEl) {
21215             this.contentEl.dom.innerHTML = str;
21216         }
21217         
21218     },
21219     // as it get's added to the bottom of the page.
21220     onRender : function(ct, position)
21221     {
21222         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21223         
21224         
21225         
21226         if(!this.el){
21227             var cfg = Roo.apply({},  this.getAutoCreate());
21228             cfg.id = Roo.id();
21229             
21230             if (this.cls) {
21231                 cfg.cls += ' ' + this.cls;
21232             }
21233             if (this.style) {
21234                 cfg.style = this.style;
21235             }
21236             //Roo.log("adding to ");
21237             this.el = Roo.get(document.body).createChild(cfg, position);
21238 //            Roo.log(this.el);
21239         }
21240         
21241         this.contentEl = this.el.select('.popover-content',true).first();
21242         this.headerEl =  this.el.select('.popover-title',true).first();
21243         
21244         var nitems = [];
21245         if(typeof(this.items) != 'undefined'){
21246             var items = this.items;
21247             delete this.items;
21248
21249             for(var i =0;i < items.length;i++) {
21250                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21251             }
21252         }
21253
21254         this.items = nitems;
21255         
21256         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21257         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21258         
21259         
21260         
21261         this.initEvents();
21262     },
21263     
21264     resizeMask : function()
21265     {
21266         this.maskEl.setSize(
21267             Roo.lib.Dom.getViewWidth(true),
21268             Roo.lib.Dom.getViewHeight(true)
21269         );
21270     },
21271     
21272     initEvents : function()
21273     {
21274         
21275         if (!this.modal) { 
21276             Roo.bootstrap.Popover.register(this);
21277         }
21278          
21279         this.arrowEl = this.el.select('.arrow',true).first();
21280         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21281         this.el.enableDisplayMode('block');
21282         this.el.hide();
21283  
21284         
21285         if (this.over === false && !this.parent()) {
21286             return; 
21287         }
21288         if (this.triggers === false) {
21289             return;
21290         }
21291          
21292         // support parent
21293         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21294         var triggers = this.trigger ? this.trigger.split(' ') : [];
21295         Roo.each(triggers, function(trigger) {
21296         
21297             if (trigger == 'click') {
21298                 on_el.on('click', this.toggle, this);
21299             } else if (trigger != 'manual') {
21300                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21301                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21302       
21303                 on_el.on(eventIn  ,this.enter, this);
21304                 on_el.on(eventOut, this.leave, this);
21305             }
21306         }, this);
21307     },
21308     
21309     
21310     // private
21311     timeout : null,
21312     hoverState : null,
21313     
21314     toggle : function () {
21315         this.hoverState == 'in' ? this.leave() : this.enter();
21316     },
21317     
21318     enter : function () {
21319         
21320         clearTimeout(this.timeout);
21321     
21322         this.hoverState = 'in';
21323     
21324         if (!this.delay || !this.delay.show) {
21325             this.show();
21326             return;
21327         }
21328         var _t = this;
21329         this.timeout = setTimeout(function () {
21330             if (_t.hoverState == 'in') {
21331                 _t.show();
21332             }
21333         }, this.delay.show)
21334     },
21335     
21336     leave : function() {
21337         clearTimeout(this.timeout);
21338     
21339         this.hoverState = 'out';
21340     
21341         if (!this.delay || !this.delay.hide) {
21342             this.hide();
21343             return;
21344         }
21345         var _t = this;
21346         this.timeout = setTimeout(function () {
21347             if (_t.hoverState == 'out') {
21348                 _t.hide();
21349             }
21350         }, this.delay.hide)
21351     },
21352     /**
21353      * Show the popover
21354      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21355      * @param {string} (left|right|top|bottom) position
21356      */
21357     show : function (on_el, placement)
21358     {
21359         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21360         on_el = on_el || false; // default to false
21361          
21362         if (!on_el) {
21363             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21364                 on_el = this.parent().el;
21365             } else if (this.over) {
21366                 on_el = Roo.get(this.over);
21367             }
21368             
21369         }
21370         
21371         this.alignEl = Roo.get( on_el );
21372
21373         if (!this.el) {
21374             this.render(document.body);
21375         }
21376         
21377         
21378          
21379         
21380         if (this.title === false) {
21381             this.headerEl.hide();
21382         }
21383         
21384        
21385         this.el.show();
21386         this.el.dom.style.display = 'block';
21387          
21388  
21389         if (this.alignEl) {
21390             this.updatePosition(this.placement, true);
21391              
21392         } else {
21393             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21394             var es = this.el.getSize();
21395             var x = Roo.lib.Dom.getViewWidth()/2;
21396             var y = Roo.lib.Dom.getViewHeight()/2;
21397             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21398             
21399         }
21400
21401         
21402         //var arrow = this.el.select('.arrow',true).first();
21403         //arrow.set(align[2], 
21404         
21405         this.el.addClass('in');
21406         
21407          
21408         
21409         this.hoverState = 'in';
21410         
21411         if (this.modal) {
21412             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21413             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21414             this.maskEl.dom.style.display = 'block';
21415             this.maskEl.addClass('show');
21416         }
21417         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21418  
21419         this.fireEvent('show', this);
21420         
21421     },
21422     /**
21423      * fire this manually after loading a grid in the table for example
21424      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21425      * @param {Boolean} try and move it if we cant get right position.
21426      */
21427     updatePosition : function(placement, try_move)
21428     {
21429         // allow for calling with no parameters
21430         placement = placement   ? placement :  this.placement;
21431         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21432         
21433         this.el.removeClass([
21434             'fade','top','bottom', 'left', 'right','in',
21435             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21436         ]);
21437         this.el.addClass(placement + ' bs-popover-' + placement);
21438         
21439         if (!this.alignEl ) {
21440             return false;
21441         }
21442         
21443         switch (placement) {
21444             case 'right':
21445                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21446                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21447                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21448                     //normal display... or moved up/down.
21449                     this.el.setXY(offset);
21450                     var xy = this.alignEl.getAnchorXY('tr', false);
21451                     xy[0]+=2;xy[1]+=5;
21452                     this.arrowEl.setXY(xy);
21453                     return true;
21454                 }
21455                 // continue through...
21456                 return this.updatePosition('left', false);
21457                 
21458             
21459             case 'left':
21460                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21461                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21462                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21463                     //normal display... or moved up/down.
21464                     this.el.setXY(offset);
21465                     var xy = this.alignEl.getAnchorXY('tl', false);
21466                     xy[0]-=10;xy[1]+=5; // << fix me
21467                     this.arrowEl.setXY(xy);
21468                     return true;
21469                 }
21470                 // call self...
21471                 return this.updatePosition('right', false);
21472             
21473             case 'top':
21474                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21475                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21476                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21477                     //normal display... or moved up/down.
21478                     this.el.setXY(offset);
21479                     var xy = this.alignEl.getAnchorXY('t', false);
21480                     xy[1]-=10; // << fix me
21481                     this.arrowEl.setXY(xy);
21482                     return true;
21483                 }
21484                 // fall through
21485                return this.updatePosition('bottom', false);
21486             
21487             case 'bottom':
21488                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21489                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21490                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21491                     //normal display... or moved up/down.
21492                     this.el.setXY(offset);
21493                     var xy = this.alignEl.getAnchorXY('b', false);
21494                      xy[1]+=2; // << fix me
21495                     this.arrowEl.setXY(xy);
21496                     return true;
21497                 }
21498                 // fall through
21499                 return this.updatePosition('top', false);
21500                 
21501             
21502         }
21503         
21504         
21505         return false;
21506     },
21507     
21508     hide : function()
21509     {
21510         this.el.setXY([0,0]);
21511         this.el.removeClass('in');
21512         this.el.hide();
21513         this.hoverState = null;
21514         this.maskEl.hide(); // always..
21515         this.fireEvent('hide', this);
21516     }
21517     
21518 });
21519
21520
21521 Roo.apply(Roo.bootstrap.Popover, {
21522
21523     alignment : {
21524         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21525         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21526         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21527         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21528     },
21529     
21530     zIndex : 20001,
21531
21532     clickHander : false,
21533     
21534     
21535
21536     onMouseDown : function(e)
21537     {
21538         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21539             /// what is nothing is showing..
21540             this.hideAll();
21541         }
21542          
21543     },
21544     
21545     
21546     popups : [],
21547     
21548     register : function(popup)
21549     {
21550         if (!Roo.bootstrap.Popover.clickHandler) {
21551             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21552         }
21553         // hide other popups.
21554         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21555         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21556         this.hideAll(); //<< why?
21557         //this.popups.push(popup);
21558     },
21559     hideAll : function()
21560     {
21561         this.popups.forEach(function(p) {
21562             p.hide();
21563         });
21564     },
21565     onShow : function() {
21566         Roo.bootstrap.Popover.popups.push(this);
21567     },
21568     onHide : function() {
21569         Roo.bootstrap.Popover.popups.remove(this);
21570     } 
21571
21572 });/*
21573  * - LGPL
21574  *
21575  * Card header - holder for the card header elements.
21576  * 
21577  */
21578
21579 /**
21580  * @class Roo.bootstrap.PopoverNav
21581  * @extends Roo.bootstrap.NavGroup
21582  * Bootstrap Popover header navigation class
21583  * @constructor
21584  * Create a new Popover Header Navigation 
21585  * @param {Object} config The config object
21586  */
21587
21588 Roo.bootstrap.PopoverNav = function(config){
21589     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21590 };
21591
21592 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21593     
21594     
21595     container_method : 'getPopoverHeader' 
21596     
21597      
21598     
21599     
21600    
21601 });
21602
21603  
21604
21605  /*
21606  * - LGPL
21607  *
21608  * Progress
21609  * 
21610  */
21611
21612 /**
21613  * @class Roo.bootstrap.Progress
21614  * @extends Roo.bootstrap.Component
21615  * @children Roo.bootstrap.ProgressBar
21616  * Bootstrap Progress class
21617  * @cfg {Boolean} striped striped of the progress bar
21618  * @cfg {Boolean} active animated of the progress bar
21619  * 
21620  * 
21621  * @constructor
21622  * Create a new Progress
21623  * @param {Object} config The config object
21624  */
21625
21626 Roo.bootstrap.Progress = function(config){
21627     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21628 };
21629
21630 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21631     
21632     striped : false,
21633     active: false,
21634     
21635     getAutoCreate : function(){
21636         var cfg = {
21637             tag: 'div',
21638             cls: 'progress'
21639         };
21640         
21641         
21642         if(this.striped){
21643             cfg.cls += ' progress-striped';
21644         }
21645       
21646         if(this.active){
21647             cfg.cls += ' active';
21648         }
21649         
21650         
21651         return cfg;
21652     }
21653    
21654 });
21655
21656  
21657
21658  /*
21659  * - LGPL
21660  *
21661  * ProgressBar
21662  * 
21663  */
21664
21665 /**
21666  * @class Roo.bootstrap.ProgressBar
21667  * @extends Roo.bootstrap.Component
21668  * Bootstrap ProgressBar class
21669  * @cfg {Number} aria_valuenow aria-value now
21670  * @cfg {Number} aria_valuemin aria-value min
21671  * @cfg {Number} aria_valuemax aria-value max
21672  * @cfg {String} label label for the progress bar
21673  * @cfg {String} panel (success | info | warning | danger )
21674  * @cfg {String} role role of the progress bar
21675  * @cfg {String} sr_only text
21676  * 
21677  * 
21678  * @constructor
21679  * Create a new ProgressBar
21680  * @param {Object} config The config object
21681  */
21682
21683 Roo.bootstrap.ProgressBar = function(config){
21684     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21685 };
21686
21687 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21688     
21689     aria_valuenow : 0,
21690     aria_valuemin : 0,
21691     aria_valuemax : 100,
21692     label : false,
21693     panel : false,
21694     role : false,
21695     sr_only: false,
21696     
21697     getAutoCreate : function()
21698     {
21699         
21700         var cfg = {
21701             tag: 'div',
21702             cls: 'progress-bar',
21703             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21704         };
21705         
21706         if(this.sr_only){
21707             cfg.cn = {
21708                 tag: 'span',
21709                 cls: 'sr-only',
21710                 html: this.sr_only
21711             }
21712         }
21713         
21714         if(this.role){
21715             cfg.role = this.role;
21716         }
21717         
21718         if(this.aria_valuenow){
21719             cfg['aria-valuenow'] = this.aria_valuenow;
21720         }
21721         
21722         if(this.aria_valuemin){
21723             cfg['aria-valuemin'] = this.aria_valuemin;
21724         }
21725         
21726         if(this.aria_valuemax){
21727             cfg['aria-valuemax'] = this.aria_valuemax;
21728         }
21729         
21730         if(this.label && !this.sr_only){
21731             cfg.html = this.label;
21732         }
21733         
21734         if(this.panel){
21735             cfg.cls += ' progress-bar-' + this.panel;
21736         }
21737         
21738         return cfg;
21739     },
21740     
21741     update : function(aria_valuenow)
21742     {
21743         this.aria_valuenow = aria_valuenow;
21744         
21745         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21746     }
21747    
21748 });
21749
21750  
21751
21752  /**
21753  * @class Roo.bootstrap.TabGroup
21754  * @extends Roo.bootstrap.Column
21755  * @children Roo.bootstrap.TabPanel
21756  * Bootstrap Column class
21757  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21758  * @cfg {Boolean} carousel true to make the group behave like a carousel
21759  * @cfg {Boolean} bullets show bullets for the panels
21760  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21761  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21762  * @cfg {Boolean} showarrow (true|false) show arrow default true
21763  * 
21764  * @constructor
21765  * Create a new TabGroup
21766  * @param {Object} config The config object
21767  */
21768
21769 Roo.bootstrap.TabGroup = function(config){
21770     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21771     if (!this.navId) {
21772         this.navId = Roo.id();
21773     }
21774     this.tabs = [];
21775     Roo.bootstrap.TabGroup.register(this);
21776     
21777 };
21778
21779 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21780     
21781     carousel : false,
21782     transition : false,
21783     bullets : 0,
21784     timer : 0,
21785     autoslide : false,
21786     slideFn : false,
21787     slideOnTouch : false,
21788     showarrow : true,
21789     
21790     getAutoCreate : function()
21791     {
21792         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21793         
21794         cfg.cls += ' tab-content';
21795         
21796         if (this.carousel) {
21797             cfg.cls += ' carousel slide';
21798             
21799             cfg.cn = [{
21800                cls : 'carousel-inner',
21801                cn : []
21802             }];
21803         
21804             if(this.bullets  && !Roo.isTouch){
21805                 
21806                 var bullets = {
21807                     cls : 'carousel-bullets',
21808                     cn : []
21809                 };
21810                
21811                 if(this.bullets_cls){
21812                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21813                 }
21814                 
21815                 bullets.cn.push({
21816                     cls : 'clear'
21817                 });
21818                 
21819                 cfg.cn[0].cn.push(bullets);
21820             }
21821             
21822             if(this.showarrow){
21823                 cfg.cn[0].cn.push({
21824                     tag : 'div',
21825                     class : 'carousel-arrow',
21826                     cn : [
21827                         {
21828                             tag : 'div',
21829                             class : 'carousel-prev',
21830                             cn : [
21831                                 {
21832                                     tag : 'i',
21833                                     class : 'fa fa-chevron-left'
21834                                 }
21835                             ]
21836                         },
21837                         {
21838                             tag : 'div',
21839                             class : 'carousel-next',
21840                             cn : [
21841                                 {
21842                                     tag : 'i',
21843                                     class : 'fa fa-chevron-right'
21844                                 }
21845                             ]
21846                         }
21847                     ]
21848                 });
21849             }
21850             
21851         }
21852         
21853         return cfg;
21854     },
21855     
21856     initEvents:  function()
21857     {
21858 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21859 //            this.el.on("touchstart", this.onTouchStart, this);
21860 //        }
21861         
21862         if(this.autoslide){
21863             var _this = this;
21864             
21865             this.slideFn = window.setInterval(function() {
21866                 _this.showPanelNext();
21867             }, this.timer);
21868         }
21869         
21870         if(this.showarrow){
21871             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21872             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21873         }
21874         
21875         
21876     },
21877     
21878 //    onTouchStart : function(e, el, o)
21879 //    {
21880 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21881 //            return;
21882 //        }
21883 //        
21884 //        this.showPanelNext();
21885 //    },
21886     
21887     
21888     getChildContainer : function()
21889     {
21890         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21891     },
21892     
21893     /**
21894     * register a Navigation item
21895     * @param {Roo.bootstrap.NavItem} the navitem to add
21896     */
21897     register : function(item)
21898     {
21899         this.tabs.push( item);
21900         item.navId = this.navId; // not really needed..
21901         this.addBullet();
21902     
21903     },
21904     
21905     getActivePanel : function()
21906     {
21907         var r = false;
21908         Roo.each(this.tabs, function(t) {
21909             if (t.active) {
21910                 r = t;
21911                 return false;
21912             }
21913             return null;
21914         });
21915         return r;
21916         
21917     },
21918     getPanelByName : function(n)
21919     {
21920         var r = false;
21921         Roo.each(this.tabs, function(t) {
21922             if (t.tabId == n) {
21923                 r = t;
21924                 return false;
21925             }
21926             return null;
21927         });
21928         return r;
21929     },
21930     indexOfPanel : function(p)
21931     {
21932         var r = false;
21933         Roo.each(this.tabs, function(t,i) {
21934             if (t.tabId == p.tabId) {
21935                 r = i;
21936                 return false;
21937             }
21938             return null;
21939         });
21940         return r;
21941     },
21942     /**
21943      * show a specific panel
21944      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21945      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21946      */
21947     showPanel : function (pan)
21948     {
21949         if(this.transition || typeof(pan) == 'undefined'){
21950             Roo.log("waiting for the transitionend");
21951             return false;
21952         }
21953         
21954         if (typeof(pan) == 'number') {
21955             pan = this.tabs[pan];
21956         }
21957         
21958         if (typeof(pan) == 'string') {
21959             pan = this.getPanelByName(pan);
21960         }
21961         
21962         var cur = this.getActivePanel();
21963         
21964         if(!pan || !cur){
21965             Roo.log('pan or acitve pan is undefined');
21966             return false;
21967         }
21968         
21969         if (pan.tabId == this.getActivePanel().tabId) {
21970             return true;
21971         }
21972         
21973         if (false === cur.fireEvent('beforedeactivate')) {
21974             return false;
21975         }
21976         
21977         if(this.bullets > 0 && !Roo.isTouch){
21978             this.setActiveBullet(this.indexOfPanel(pan));
21979         }
21980         
21981         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21982             
21983             //class="carousel-item carousel-item-next carousel-item-left"
21984             
21985             this.transition = true;
21986             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21987             var lr = dir == 'next' ? 'left' : 'right';
21988             pan.el.addClass(dir); // or prev
21989             pan.el.addClass('carousel-item-' + dir); // or prev
21990             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21991             cur.el.addClass(lr); // or right
21992             pan.el.addClass(lr);
21993             cur.el.addClass('carousel-item-' +lr); // or right
21994             pan.el.addClass('carousel-item-' +lr);
21995             
21996             
21997             var _this = this;
21998             cur.el.on('transitionend', function() {
21999                 Roo.log("trans end?");
22000                 
22001                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22002                 pan.setActive(true);
22003                 
22004                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22005                 cur.setActive(false);
22006                 
22007                 _this.transition = false;
22008                 
22009             }, this, { single:  true } );
22010             
22011             return true;
22012         }
22013         
22014         cur.setActive(false);
22015         pan.setActive(true);
22016         
22017         return true;
22018         
22019     },
22020     showPanelNext : function()
22021     {
22022         var i = this.indexOfPanel(this.getActivePanel());
22023         
22024         if (i >= this.tabs.length - 1 && !this.autoslide) {
22025             return;
22026         }
22027         
22028         if (i >= this.tabs.length - 1 && this.autoslide) {
22029             i = -1;
22030         }
22031         
22032         this.showPanel(this.tabs[i+1]);
22033     },
22034     
22035     showPanelPrev : function()
22036     {
22037         var i = this.indexOfPanel(this.getActivePanel());
22038         
22039         if (i  < 1 && !this.autoslide) {
22040             return;
22041         }
22042         
22043         if (i < 1 && this.autoslide) {
22044             i = this.tabs.length;
22045         }
22046         
22047         this.showPanel(this.tabs[i-1]);
22048     },
22049     
22050     
22051     addBullet: function()
22052     {
22053         if(!this.bullets || Roo.isTouch){
22054             return;
22055         }
22056         var ctr = this.el.select('.carousel-bullets',true).first();
22057         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22058         var bullet = ctr.createChild({
22059             cls : 'bullet bullet-' + i
22060         },ctr.dom.lastChild);
22061         
22062         
22063         var _this = this;
22064         
22065         bullet.on('click', (function(e, el, o, ii, t){
22066
22067             e.preventDefault();
22068
22069             this.showPanel(ii);
22070
22071             if(this.autoslide && this.slideFn){
22072                 clearInterval(this.slideFn);
22073                 this.slideFn = window.setInterval(function() {
22074                     _this.showPanelNext();
22075                 }, this.timer);
22076             }
22077
22078         }).createDelegate(this, [i, bullet], true));
22079                 
22080         
22081     },
22082      
22083     setActiveBullet : function(i)
22084     {
22085         if(Roo.isTouch){
22086             return;
22087         }
22088         
22089         Roo.each(this.el.select('.bullet', true).elements, function(el){
22090             el.removeClass('selected');
22091         });
22092
22093         var bullet = this.el.select('.bullet-' + i, true).first();
22094         
22095         if(!bullet){
22096             return;
22097         }
22098         
22099         bullet.addClass('selected');
22100     }
22101     
22102     
22103   
22104 });
22105
22106  
22107
22108  
22109  
22110 Roo.apply(Roo.bootstrap.TabGroup, {
22111     
22112     groups: {},
22113      /**
22114     * register a Navigation Group
22115     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22116     */
22117     register : function(navgrp)
22118     {
22119         this.groups[navgrp.navId] = navgrp;
22120         
22121     },
22122     /**
22123     * fetch a Navigation Group based on the navigation ID
22124     * if one does not exist , it will get created.
22125     * @param {string} the navgroup to add
22126     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22127     */
22128     get: function(navId) {
22129         if (typeof(this.groups[navId]) == 'undefined') {
22130             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22131         }
22132         return this.groups[navId] ;
22133     }
22134     
22135     
22136     
22137 });
22138
22139  /*
22140  * - LGPL
22141  *
22142  * TabPanel
22143  * 
22144  */
22145
22146 /**
22147  * @class Roo.bootstrap.TabPanel
22148  * @extends Roo.bootstrap.Component
22149  * @children Roo.bootstrap.Component
22150  * Bootstrap TabPanel class
22151  * @cfg {Boolean} active panel active
22152  * @cfg {String} html panel content
22153  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22154  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22155  * @cfg {String} href click to link..
22156  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22157  * 
22158  * 
22159  * @constructor
22160  * Create a new TabPanel
22161  * @param {Object} config The config object
22162  */
22163
22164 Roo.bootstrap.TabPanel = function(config){
22165     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22166     this.addEvents({
22167         /**
22168              * @event changed
22169              * Fires when the active status changes
22170              * @param {Roo.bootstrap.TabPanel} this
22171              * @param {Boolean} state the new state
22172             
22173          */
22174         'changed': true,
22175         /**
22176              * @event beforedeactivate
22177              * Fires before a tab is de-activated - can be used to do validation on a form.
22178              * @param {Roo.bootstrap.TabPanel} this
22179              * @return {Boolean} false if there is an error
22180             
22181          */
22182         'beforedeactivate': true
22183      });
22184     
22185     this.tabId = this.tabId || Roo.id();
22186   
22187 };
22188
22189 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22190     
22191     active: false,
22192     html: false,
22193     tabId: false,
22194     navId : false,
22195     href : '',
22196     touchSlide : false,
22197     getAutoCreate : function(){
22198         
22199         
22200         var cfg = {
22201             tag: 'div',
22202             // item is needed for carousel - not sure if it has any effect otherwise
22203             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22204             html: this.html || ''
22205         };
22206         
22207         if(this.active){
22208             cfg.cls += ' active';
22209         }
22210         
22211         if(this.tabId){
22212             cfg.tabId = this.tabId;
22213         }
22214         
22215         
22216         
22217         return cfg;
22218     },
22219     
22220     initEvents:  function()
22221     {
22222         var p = this.parent();
22223         
22224         this.navId = this.navId || p.navId;
22225         
22226         if (typeof(this.navId) != 'undefined') {
22227             // not really needed.. but just in case.. parent should be a NavGroup.
22228             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22229             
22230             tg.register(this);
22231             
22232             var i = tg.tabs.length - 1;
22233             
22234             if(this.active && tg.bullets > 0 && i < tg.bullets){
22235                 tg.setActiveBullet(i);
22236             }
22237         }
22238         
22239         this.el.on('click', this.onClick, this);
22240         
22241         if(Roo.isTouch && this.touchSlide){
22242             this.el.on("touchstart", this.onTouchStart, this);
22243             this.el.on("touchmove", this.onTouchMove, this);
22244             this.el.on("touchend", this.onTouchEnd, this);
22245         }
22246         
22247     },
22248     
22249     onRender : function(ct, position)
22250     {
22251         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22252     },
22253     
22254     setActive : function(state)
22255     {
22256         Roo.log("panel - set active " + this.tabId + "=" + state);
22257         
22258         this.active = state;
22259         if (!state) {
22260             this.el.removeClass('active');
22261             
22262         } else  if (!this.el.hasClass('active')) {
22263             this.el.addClass('active');
22264         }
22265         
22266         this.fireEvent('changed', this, state);
22267     },
22268     
22269     onClick : function(e)
22270     {
22271         e.preventDefault();
22272         
22273         if(!this.href.length){
22274             return;
22275         }
22276         
22277         window.location.href = this.href;
22278     },
22279     
22280     startX : 0,
22281     startY : 0,
22282     endX : 0,
22283     endY : 0,
22284     swiping : false,
22285     
22286     onTouchStart : function(e)
22287     {
22288         this.swiping = false;
22289         
22290         this.startX = e.browserEvent.touches[0].clientX;
22291         this.startY = e.browserEvent.touches[0].clientY;
22292     },
22293     
22294     onTouchMove : function(e)
22295     {
22296         this.swiping = true;
22297         
22298         this.endX = e.browserEvent.touches[0].clientX;
22299         this.endY = e.browserEvent.touches[0].clientY;
22300     },
22301     
22302     onTouchEnd : function(e)
22303     {
22304         if(!this.swiping){
22305             this.onClick(e);
22306             return;
22307         }
22308         
22309         var tabGroup = this.parent();
22310         
22311         if(this.endX > this.startX){ // swiping right
22312             tabGroup.showPanelPrev();
22313             return;
22314         }
22315         
22316         if(this.startX > this.endX){ // swiping left
22317             tabGroup.showPanelNext();
22318             return;
22319         }
22320     }
22321     
22322     
22323 });
22324  
22325
22326  
22327
22328  /*
22329  * - LGPL
22330  *
22331  * DateField
22332  * 
22333  */
22334
22335 /**
22336  * @class Roo.bootstrap.DateField
22337  * @extends Roo.bootstrap.Input
22338  * Bootstrap DateField class
22339  * @cfg {Number} weekStart default 0
22340  * @cfg {String} viewMode default empty, (months|years)
22341  * @cfg {String} minViewMode default empty, (months|years)
22342  * @cfg {Number} startDate default -Infinity
22343  * @cfg {Number} endDate default Infinity
22344  * @cfg {Boolean} todayHighlight default false
22345  * @cfg {Boolean} todayBtn default false
22346  * @cfg {Boolean} calendarWeeks default false
22347  * @cfg {Object} daysOfWeekDisabled default empty
22348  * @cfg {Boolean} singleMode default false (true | false)
22349  * 
22350  * @cfg {Boolean} keyboardNavigation default true
22351  * @cfg {String} language default en
22352  * 
22353  * @constructor
22354  * Create a new DateField
22355  * @param {Object} config The config object
22356  */
22357
22358 Roo.bootstrap.DateField = function(config){
22359     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22360      this.addEvents({
22361             /**
22362              * @event show
22363              * Fires when this field show.
22364              * @param {Roo.bootstrap.DateField} this
22365              * @param {Mixed} date The date value
22366              */
22367             show : true,
22368             /**
22369              * @event show
22370              * Fires when this field hide.
22371              * @param {Roo.bootstrap.DateField} this
22372              * @param {Mixed} date The date value
22373              */
22374             hide : true,
22375             /**
22376              * @event select
22377              * Fires when select a date.
22378              * @param {Roo.bootstrap.DateField} this
22379              * @param {Mixed} date The date value
22380              */
22381             select : true,
22382             /**
22383              * @event beforeselect
22384              * Fires when before select a date.
22385              * @param {Roo.bootstrap.DateField} this
22386              * @param {Mixed} date The date value
22387              */
22388             beforeselect : true
22389         });
22390 };
22391
22392 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22393     
22394     /**
22395      * @cfg {String} format
22396      * The default date format string which can be overriden for localization support.  The format must be
22397      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22398      */
22399     format : "m/d/y",
22400     /**
22401      * @cfg {String} altFormats
22402      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22403      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22404      */
22405     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22406     
22407     weekStart : 0,
22408     
22409     viewMode : '',
22410     
22411     minViewMode : '',
22412     
22413     todayHighlight : false,
22414     
22415     todayBtn: false,
22416     
22417     language: 'en',
22418     
22419     keyboardNavigation: true,
22420     
22421     calendarWeeks: false,
22422     
22423     startDate: -Infinity,
22424     
22425     endDate: Infinity,
22426     
22427     daysOfWeekDisabled: [],
22428     
22429     _events: [],
22430     
22431     singleMode : false,
22432     
22433     UTCDate: function()
22434     {
22435         return new Date(Date.UTC.apply(Date, arguments));
22436     },
22437     
22438     UTCToday: function()
22439     {
22440         var today = new Date();
22441         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22442     },
22443     
22444     getDate: function() {
22445             var d = this.getUTCDate();
22446             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22447     },
22448     
22449     getUTCDate: function() {
22450             return this.date;
22451     },
22452     
22453     setDate: function(d) {
22454             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22455     },
22456     
22457     setUTCDate: function(d) {
22458             this.date = d;
22459             this.setValue(this.formatDate(this.date));
22460     },
22461         
22462     onRender: function(ct, position)
22463     {
22464         
22465         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22466         
22467         this.language = this.language || 'en';
22468         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22469         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22470         
22471         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22472         this.format = this.format || 'm/d/y';
22473         this.isInline = false;
22474         this.isInput = true;
22475         this.component = this.el.select('.add-on', true).first() || false;
22476         this.component = (this.component && this.component.length === 0) ? false : this.component;
22477         this.hasInput = this.component && this.inputEl().length;
22478         
22479         if (typeof(this.minViewMode === 'string')) {
22480             switch (this.minViewMode) {
22481                 case 'months':
22482                     this.minViewMode = 1;
22483                     break;
22484                 case 'years':
22485                     this.minViewMode = 2;
22486                     break;
22487                 default:
22488                     this.minViewMode = 0;
22489                     break;
22490             }
22491         }
22492         
22493         if (typeof(this.viewMode === 'string')) {
22494             switch (this.viewMode) {
22495                 case 'months':
22496                     this.viewMode = 1;
22497                     break;
22498                 case 'years':
22499                     this.viewMode = 2;
22500                     break;
22501                 default:
22502                     this.viewMode = 0;
22503                     break;
22504             }
22505         }
22506                 
22507         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22508         
22509 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22510         
22511         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22512         
22513         this.picker().on('mousedown', this.onMousedown, this);
22514         this.picker().on('click', this.onClick, this);
22515         
22516         this.picker().addClass('datepicker-dropdown');
22517         
22518         this.startViewMode = this.viewMode;
22519         
22520         if(this.singleMode){
22521             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22522                 v.setVisibilityMode(Roo.Element.DISPLAY);
22523                 v.hide();
22524             });
22525             
22526             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22527                 v.setStyle('width', '189px');
22528             });
22529         }
22530         
22531         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22532             if(!this.calendarWeeks){
22533                 v.remove();
22534                 return;
22535             }
22536             
22537             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22538             v.attr('colspan', function(i, val){
22539                 return parseInt(val) + 1;
22540             });
22541         });
22542                         
22543         
22544         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22545         
22546         this.setStartDate(this.startDate);
22547         this.setEndDate(this.endDate);
22548         
22549         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22550         
22551         this.fillDow();
22552         this.fillMonths();
22553         this.update();
22554         this.showMode();
22555         
22556         if(this.isInline) {
22557             this.showPopup();
22558         }
22559     },
22560     
22561     picker : function()
22562     {
22563         return this.pickerEl;
22564 //        return this.el.select('.datepicker', true).first();
22565     },
22566     
22567     fillDow: function()
22568     {
22569         var dowCnt = this.weekStart;
22570         
22571         var dow = {
22572             tag: 'tr',
22573             cn: [
22574                 
22575             ]
22576         };
22577         
22578         if(this.calendarWeeks){
22579             dow.cn.push({
22580                 tag: 'th',
22581                 cls: 'cw',
22582                 html: '&nbsp;'
22583             })
22584         }
22585         
22586         while (dowCnt < this.weekStart + 7) {
22587             dow.cn.push({
22588                 tag: 'th',
22589                 cls: 'dow',
22590                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22591             });
22592         }
22593         
22594         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22595     },
22596     
22597     fillMonths: function()
22598     {    
22599         var i = 0;
22600         var months = this.picker().select('>.datepicker-months td', true).first();
22601         
22602         months.dom.innerHTML = '';
22603         
22604         while (i < 12) {
22605             var month = {
22606                 tag: 'span',
22607                 cls: 'month',
22608                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22609             };
22610             
22611             months.createChild(month);
22612         }
22613         
22614     },
22615     
22616     update: function()
22617     {
22618         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;
22619         
22620         if (this.date < this.startDate) {
22621             this.viewDate = new Date(this.startDate);
22622         } else if (this.date > this.endDate) {
22623             this.viewDate = new Date(this.endDate);
22624         } else {
22625             this.viewDate = new Date(this.date);
22626         }
22627         
22628         this.fill();
22629     },
22630     
22631     fill: function() 
22632     {
22633         var d = new Date(this.viewDate),
22634                 year = d.getUTCFullYear(),
22635                 month = d.getUTCMonth(),
22636                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22637                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22638                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22639                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22640                 currentDate = this.date && this.date.valueOf(),
22641                 today = this.UTCToday();
22642         
22643         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22644         
22645 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22646         
22647 //        this.picker.select('>tfoot th.today').
22648 //                                              .text(dates[this.language].today)
22649 //                                              .toggle(this.todayBtn !== false);
22650     
22651         this.updateNavArrows();
22652         this.fillMonths();
22653                                                 
22654         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22655         
22656         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22657          
22658         prevMonth.setUTCDate(day);
22659         
22660         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22661         
22662         var nextMonth = new Date(prevMonth);
22663         
22664         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22665         
22666         nextMonth = nextMonth.valueOf();
22667         
22668         var fillMonths = false;
22669         
22670         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22671         
22672         while(prevMonth.valueOf() <= nextMonth) {
22673             var clsName = '';
22674             
22675             if (prevMonth.getUTCDay() === this.weekStart) {
22676                 if(fillMonths){
22677                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22678                 }
22679                     
22680                 fillMonths = {
22681                     tag: 'tr',
22682                     cn: []
22683                 };
22684                 
22685                 if(this.calendarWeeks){
22686                     // ISO 8601: First week contains first thursday.
22687                     // ISO also states week starts on Monday, but we can be more abstract here.
22688                     var
22689                     // Start of current week: based on weekstart/current date
22690                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22691                     // Thursday of this week
22692                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22693                     // First Thursday of year, year from thursday
22694                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22695                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22696                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22697                     
22698                     fillMonths.cn.push({
22699                         tag: 'td',
22700                         cls: 'cw',
22701                         html: calWeek
22702                     });
22703                 }
22704             }
22705             
22706             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22707                 clsName += ' old';
22708             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22709                 clsName += ' new';
22710             }
22711             if (this.todayHighlight &&
22712                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22713                 prevMonth.getUTCMonth() == today.getMonth() &&
22714                 prevMonth.getUTCDate() == today.getDate()) {
22715                 clsName += ' today';
22716             }
22717             
22718             if (currentDate && prevMonth.valueOf() === currentDate) {
22719                 clsName += ' active';
22720             }
22721             
22722             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22723                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22724                     clsName += ' disabled';
22725             }
22726             
22727             fillMonths.cn.push({
22728                 tag: 'td',
22729                 cls: 'day ' + clsName,
22730                 html: prevMonth.getDate()
22731             });
22732             
22733             prevMonth.setDate(prevMonth.getDate()+1);
22734         }
22735           
22736         var currentYear = this.date && this.date.getUTCFullYear();
22737         var currentMonth = this.date && this.date.getUTCMonth();
22738         
22739         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22740         
22741         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22742             v.removeClass('active');
22743             
22744             if(currentYear === year && k === currentMonth){
22745                 v.addClass('active');
22746             }
22747             
22748             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22749                 v.addClass('disabled');
22750             }
22751             
22752         });
22753         
22754         
22755         year = parseInt(year/10, 10) * 10;
22756         
22757         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22758         
22759         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22760         
22761         year -= 1;
22762         for (var i = -1; i < 11; i++) {
22763             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22764                 tag: 'span',
22765                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22766                 html: year
22767             });
22768             
22769             year += 1;
22770         }
22771     },
22772     
22773     showMode: function(dir) 
22774     {
22775         if (dir) {
22776             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22777         }
22778         
22779         Roo.each(this.picker().select('>div',true).elements, function(v){
22780             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22781             v.hide();
22782         });
22783         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22784     },
22785     
22786     place: function()
22787     {
22788         if(this.isInline) {
22789             return;
22790         }
22791         
22792         this.picker().removeClass(['bottom', 'top']);
22793         
22794         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22795             /*
22796              * place to the top of element!
22797              *
22798              */
22799             
22800             this.picker().addClass('top');
22801             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22802             
22803             return;
22804         }
22805         
22806         this.picker().addClass('bottom');
22807         
22808         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22809     },
22810     
22811     parseDate : function(value)
22812     {
22813         if(!value || value instanceof Date){
22814             return value;
22815         }
22816         var v = Date.parseDate(value, this.format);
22817         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22818             v = Date.parseDate(value, 'Y-m-d');
22819         }
22820         if(!v && this.altFormats){
22821             if(!this.altFormatsArray){
22822                 this.altFormatsArray = this.altFormats.split("|");
22823             }
22824             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22825                 v = Date.parseDate(value, this.altFormatsArray[i]);
22826             }
22827         }
22828         return v;
22829     },
22830     
22831     formatDate : function(date, fmt)
22832     {   
22833         return (!date || !(date instanceof Date)) ?
22834         date : date.dateFormat(fmt || this.format);
22835     },
22836     
22837     onFocus : function()
22838     {
22839         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22840         this.showPopup();
22841     },
22842     
22843     onBlur : function()
22844     {
22845         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22846         
22847         var d = this.inputEl().getValue();
22848         
22849         this.setValue(d);
22850                 
22851         this.hidePopup();
22852     },
22853     
22854     showPopup : function()
22855     {
22856         this.picker().show();
22857         this.update();
22858         this.place();
22859         
22860         this.fireEvent('showpopup', this, this.date);
22861     },
22862     
22863     hidePopup : function()
22864     {
22865         if(this.isInline) {
22866             return;
22867         }
22868         this.picker().hide();
22869         this.viewMode = this.startViewMode;
22870         this.showMode();
22871         
22872         this.fireEvent('hidepopup', this, this.date);
22873         
22874     },
22875     
22876     onMousedown: function(e)
22877     {
22878         e.stopPropagation();
22879         e.preventDefault();
22880     },
22881     
22882     keyup: function(e)
22883     {
22884         Roo.bootstrap.DateField.superclass.keyup.call(this);
22885         this.update();
22886     },
22887
22888     setValue: function(v)
22889     {
22890         if(this.fireEvent('beforeselect', this, v) !== false){
22891             var d = new Date(this.parseDate(v) ).clearTime();
22892         
22893             if(isNaN(d.getTime())){
22894                 this.date = this.viewDate = '';
22895                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22896                 return;
22897             }
22898
22899             v = this.formatDate(d);
22900
22901             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22902
22903             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22904
22905             this.update();
22906
22907             this.fireEvent('select', this, this.date);
22908         }
22909     },
22910     
22911     getValue: function()
22912     {
22913         return this.formatDate(this.date);
22914     },
22915     
22916     fireKey: function(e)
22917     {
22918         if (!this.picker().isVisible()){
22919             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22920                 this.showPopup();
22921             }
22922             return;
22923         }
22924         
22925         var dateChanged = false,
22926         dir, day, month,
22927         newDate, newViewDate;
22928         
22929         switch(e.keyCode){
22930             case 27: // escape
22931                 this.hidePopup();
22932                 e.preventDefault();
22933                 break;
22934             case 37: // left
22935             case 39: // right
22936                 if (!this.keyboardNavigation) {
22937                     break;
22938                 }
22939                 dir = e.keyCode == 37 ? -1 : 1;
22940                 
22941                 if (e.ctrlKey){
22942                     newDate = this.moveYear(this.date, dir);
22943                     newViewDate = this.moveYear(this.viewDate, dir);
22944                 } else if (e.shiftKey){
22945                     newDate = this.moveMonth(this.date, dir);
22946                     newViewDate = this.moveMonth(this.viewDate, dir);
22947                 } else {
22948                     newDate = new Date(this.date);
22949                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22950                     newViewDate = new Date(this.viewDate);
22951                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22952                 }
22953                 if (this.dateWithinRange(newDate)){
22954                     this.date = newDate;
22955                     this.viewDate = newViewDate;
22956                     this.setValue(this.formatDate(this.date));
22957 //                    this.update();
22958                     e.preventDefault();
22959                     dateChanged = true;
22960                 }
22961                 break;
22962             case 38: // up
22963             case 40: // down
22964                 if (!this.keyboardNavigation) {
22965                     break;
22966                 }
22967                 dir = e.keyCode == 38 ? -1 : 1;
22968                 if (e.ctrlKey){
22969                     newDate = this.moveYear(this.date, dir);
22970                     newViewDate = this.moveYear(this.viewDate, dir);
22971                 } else if (e.shiftKey){
22972                     newDate = this.moveMonth(this.date, dir);
22973                     newViewDate = this.moveMonth(this.viewDate, dir);
22974                 } else {
22975                     newDate = new Date(this.date);
22976                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22977                     newViewDate = new Date(this.viewDate);
22978                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22979                 }
22980                 if (this.dateWithinRange(newDate)){
22981                     this.date = newDate;
22982                     this.viewDate = newViewDate;
22983                     this.setValue(this.formatDate(this.date));
22984 //                    this.update();
22985                     e.preventDefault();
22986                     dateChanged = true;
22987                 }
22988                 break;
22989             case 13: // enter
22990                 this.setValue(this.formatDate(this.date));
22991                 this.hidePopup();
22992                 e.preventDefault();
22993                 break;
22994             case 9: // tab
22995                 this.setValue(this.formatDate(this.date));
22996                 this.hidePopup();
22997                 break;
22998             case 16: // shift
22999             case 17: // ctrl
23000             case 18: // alt
23001                 break;
23002             default :
23003                 this.hidePopup();
23004                 
23005         }
23006     },
23007     
23008     
23009     onClick: function(e) 
23010     {
23011         e.stopPropagation();
23012         e.preventDefault();
23013         
23014         var target = e.getTarget();
23015         
23016         if(target.nodeName.toLowerCase() === 'i'){
23017             target = Roo.get(target).dom.parentNode;
23018         }
23019         
23020         var nodeName = target.nodeName;
23021         var className = target.className;
23022         var html = target.innerHTML;
23023         //Roo.log(nodeName);
23024         
23025         switch(nodeName.toLowerCase()) {
23026             case 'th':
23027                 switch(className) {
23028                     case 'switch':
23029                         this.showMode(1);
23030                         break;
23031                     case 'prev':
23032                     case 'next':
23033                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23034                         switch(this.viewMode){
23035                                 case 0:
23036                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23037                                         break;
23038                                 case 1:
23039                                 case 2:
23040                                         this.viewDate = this.moveYear(this.viewDate, dir);
23041                                         break;
23042                         }
23043                         this.fill();
23044                         break;
23045                     case 'today':
23046                         var date = new Date();
23047                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23048 //                        this.fill()
23049                         this.setValue(this.formatDate(this.date));
23050                         
23051                         this.hidePopup();
23052                         break;
23053                 }
23054                 break;
23055             case 'span':
23056                 if (className.indexOf('disabled') < 0) {
23057                 if (!this.viewDate) {
23058                     this.viewDate = new Date();
23059                 }
23060                 this.viewDate.setUTCDate(1);
23061                     if (className.indexOf('month') > -1) {
23062                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23063                     } else {
23064                         var year = parseInt(html, 10) || 0;
23065                         this.viewDate.setUTCFullYear(year);
23066                         
23067                     }
23068                     
23069                     if(this.singleMode){
23070                         this.setValue(this.formatDate(this.viewDate));
23071                         this.hidePopup();
23072                         return;
23073                     }
23074                     
23075                     this.showMode(-1);
23076                     this.fill();
23077                 }
23078                 break;
23079                 
23080             case 'td':
23081                 //Roo.log(className);
23082                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23083                     var day = parseInt(html, 10) || 1;
23084                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23085                         month = (this.viewDate || new Date()).getUTCMonth();
23086
23087                     if (className.indexOf('old') > -1) {
23088                         if(month === 0 ){
23089                             month = 11;
23090                             year -= 1;
23091                         }else{
23092                             month -= 1;
23093                         }
23094                     } else if (className.indexOf('new') > -1) {
23095                         if (month == 11) {
23096                             month = 0;
23097                             year += 1;
23098                         } else {
23099                             month += 1;
23100                         }
23101                     }
23102                     //Roo.log([year,month,day]);
23103                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23104                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23105 //                    this.fill();
23106                     //Roo.log(this.formatDate(this.date));
23107                     this.setValue(this.formatDate(this.date));
23108                     this.hidePopup();
23109                 }
23110                 break;
23111         }
23112     },
23113     
23114     setStartDate: function(startDate)
23115     {
23116         this.startDate = startDate || -Infinity;
23117         if (this.startDate !== -Infinity) {
23118             this.startDate = this.parseDate(this.startDate);
23119         }
23120         this.update();
23121         this.updateNavArrows();
23122     },
23123
23124     setEndDate: function(endDate)
23125     {
23126         this.endDate = endDate || Infinity;
23127         if (this.endDate !== Infinity) {
23128             this.endDate = this.parseDate(this.endDate);
23129         }
23130         this.update();
23131         this.updateNavArrows();
23132     },
23133     
23134     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23135     {
23136         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23137         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23138             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23139         }
23140         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23141             return parseInt(d, 10);
23142         });
23143         this.update();
23144         this.updateNavArrows();
23145     },
23146     
23147     updateNavArrows: function() 
23148     {
23149         if(this.singleMode){
23150             return;
23151         }
23152         
23153         var d = new Date(this.viewDate),
23154         year = d.getUTCFullYear(),
23155         month = d.getUTCMonth();
23156         
23157         Roo.each(this.picker().select('.prev', true).elements, function(v){
23158             v.show();
23159             switch (this.viewMode) {
23160                 case 0:
23161
23162                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23163                         v.hide();
23164                     }
23165                     break;
23166                 case 1:
23167                 case 2:
23168                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23169                         v.hide();
23170                     }
23171                     break;
23172             }
23173         });
23174         
23175         Roo.each(this.picker().select('.next', true).elements, function(v){
23176             v.show();
23177             switch (this.viewMode) {
23178                 case 0:
23179
23180                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23181                         v.hide();
23182                     }
23183                     break;
23184                 case 1:
23185                 case 2:
23186                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23187                         v.hide();
23188                     }
23189                     break;
23190             }
23191         })
23192     },
23193     
23194     moveMonth: function(date, dir)
23195     {
23196         if (!dir) {
23197             return date;
23198         }
23199         var new_date = new Date(date.valueOf()),
23200         day = new_date.getUTCDate(),
23201         month = new_date.getUTCMonth(),
23202         mag = Math.abs(dir),
23203         new_month, test;
23204         dir = dir > 0 ? 1 : -1;
23205         if (mag == 1){
23206             test = dir == -1
23207             // If going back one month, make sure month is not current month
23208             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23209             ? function(){
23210                 return new_date.getUTCMonth() == month;
23211             }
23212             // If going forward one month, make sure month is as expected
23213             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23214             : function(){
23215                 return new_date.getUTCMonth() != new_month;
23216             };
23217             new_month = month + dir;
23218             new_date.setUTCMonth(new_month);
23219             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23220             if (new_month < 0 || new_month > 11) {
23221                 new_month = (new_month + 12) % 12;
23222             }
23223         } else {
23224             // For magnitudes >1, move one month at a time...
23225             for (var i=0; i<mag; i++) {
23226                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23227                 new_date = this.moveMonth(new_date, dir);
23228             }
23229             // ...then reset the day, keeping it in the new month
23230             new_month = new_date.getUTCMonth();
23231             new_date.setUTCDate(day);
23232             test = function(){
23233                 return new_month != new_date.getUTCMonth();
23234             };
23235         }
23236         // Common date-resetting loop -- if date is beyond end of month, make it
23237         // end of month
23238         while (test()){
23239             new_date.setUTCDate(--day);
23240             new_date.setUTCMonth(new_month);
23241         }
23242         return new_date;
23243     },
23244
23245     moveYear: function(date, dir)
23246     {
23247         return this.moveMonth(date, dir*12);
23248     },
23249
23250     dateWithinRange: function(date)
23251     {
23252         return date >= this.startDate && date <= this.endDate;
23253     },
23254
23255     
23256     remove: function() 
23257     {
23258         this.picker().remove();
23259     },
23260     
23261     validateValue : function(value)
23262     {
23263         if(this.getVisibilityEl().hasClass('hidden')){
23264             return true;
23265         }
23266         
23267         if(value.length < 1)  {
23268             if(this.allowBlank){
23269                 return true;
23270             }
23271             return false;
23272         }
23273         
23274         if(value.length < this.minLength){
23275             return false;
23276         }
23277         if(value.length > this.maxLength){
23278             return false;
23279         }
23280         if(this.vtype){
23281             var vt = Roo.form.VTypes;
23282             if(!vt[this.vtype](value, this)){
23283                 return false;
23284             }
23285         }
23286         if(typeof this.validator == "function"){
23287             var msg = this.validator(value);
23288             if(msg !== true){
23289                 return false;
23290             }
23291         }
23292         
23293         if(this.regex && !this.regex.test(value)){
23294             return false;
23295         }
23296         
23297         if(typeof(this.parseDate(value)) == 'undefined'){
23298             return false;
23299         }
23300         
23301         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23302             return false;
23303         }      
23304         
23305         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23306             return false;
23307         } 
23308         
23309         
23310         return true;
23311     },
23312     
23313     reset : function()
23314     {
23315         this.date = this.viewDate = '';
23316         
23317         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23318     }
23319    
23320 });
23321
23322 Roo.apply(Roo.bootstrap.DateField,  {
23323     
23324     head : {
23325         tag: 'thead',
23326         cn: [
23327         {
23328             tag: 'tr',
23329             cn: [
23330             {
23331                 tag: 'th',
23332                 cls: 'prev',
23333                 html: '<i class="fa fa-arrow-left"/>'
23334             },
23335             {
23336                 tag: 'th',
23337                 cls: 'switch',
23338                 colspan: '5'
23339             },
23340             {
23341                 tag: 'th',
23342                 cls: 'next',
23343                 html: '<i class="fa fa-arrow-right"/>'
23344             }
23345
23346             ]
23347         }
23348         ]
23349     },
23350     
23351     content : {
23352         tag: 'tbody',
23353         cn: [
23354         {
23355             tag: 'tr',
23356             cn: [
23357             {
23358                 tag: 'td',
23359                 colspan: '7'
23360             }
23361             ]
23362         }
23363         ]
23364     },
23365     
23366     footer : {
23367         tag: 'tfoot',
23368         cn: [
23369         {
23370             tag: 'tr',
23371             cn: [
23372             {
23373                 tag: 'th',
23374                 colspan: '7',
23375                 cls: 'today'
23376             }
23377                     
23378             ]
23379         }
23380         ]
23381     },
23382     
23383     dates:{
23384         en: {
23385             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23386             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23387             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23388             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23389             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23390             today: "Today"
23391         }
23392     },
23393     
23394     modes: [
23395     {
23396         clsName: 'days',
23397         navFnc: 'Month',
23398         navStep: 1
23399     },
23400     {
23401         clsName: 'months',
23402         navFnc: 'FullYear',
23403         navStep: 1
23404     },
23405     {
23406         clsName: 'years',
23407         navFnc: 'FullYear',
23408         navStep: 10
23409     }]
23410 });
23411
23412 Roo.apply(Roo.bootstrap.DateField,  {
23413   
23414     template : {
23415         tag: 'div',
23416         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23417         cn: [
23418         {
23419             tag: 'div',
23420             cls: 'datepicker-days',
23421             cn: [
23422             {
23423                 tag: 'table',
23424                 cls: 'table-condensed',
23425                 cn:[
23426                 Roo.bootstrap.DateField.head,
23427                 {
23428                     tag: 'tbody'
23429                 },
23430                 Roo.bootstrap.DateField.footer
23431                 ]
23432             }
23433             ]
23434         },
23435         {
23436             tag: 'div',
23437             cls: 'datepicker-months',
23438             cn: [
23439             {
23440                 tag: 'table',
23441                 cls: 'table-condensed',
23442                 cn:[
23443                 Roo.bootstrap.DateField.head,
23444                 Roo.bootstrap.DateField.content,
23445                 Roo.bootstrap.DateField.footer
23446                 ]
23447             }
23448             ]
23449         },
23450         {
23451             tag: 'div',
23452             cls: 'datepicker-years',
23453             cn: [
23454             {
23455                 tag: 'table',
23456                 cls: 'table-condensed',
23457                 cn:[
23458                 Roo.bootstrap.DateField.head,
23459                 Roo.bootstrap.DateField.content,
23460                 Roo.bootstrap.DateField.footer
23461                 ]
23462             }
23463             ]
23464         }
23465         ]
23466     }
23467 });
23468
23469  
23470
23471  /*
23472  * - LGPL
23473  *
23474  * TimeField
23475  * 
23476  */
23477
23478 /**
23479  * @class Roo.bootstrap.TimeField
23480  * @extends Roo.bootstrap.Input
23481  * Bootstrap DateField class
23482  * 
23483  * 
23484  * @constructor
23485  * Create a new TimeField
23486  * @param {Object} config The config object
23487  */
23488
23489 Roo.bootstrap.TimeField = function(config){
23490     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23491     this.addEvents({
23492             /**
23493              * @event show
23494              * Fires when this field show.
23495              * @param {Roo.bootstrap.DateField} thisthis
23496              * @param {Mixed} date The date value
23497              */
23498             show : true,
23499             /**
23500              * @event show
23501              * Fires when this field hide.
23502              * @param {Roo.bootstrap.DateField} this
23503              * @param {Mixed} date The date value
23504              */
23505             hide : true,
23506             /**
23507              * @event select
23508              * Fires when select a date.
23509              * @param {Roo.bootstrap.DateField} this
23510              * @param {Mixed} date The date value
23511              */
23512             select : true
23513         });
23514 };
23515
23516 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23517     
23518     /**
23519      * @cfg {String} format
23520      * The default time format string which can be overriden for localization support.  The format must be
23521      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23522      */
23523     format : "H:i",
23524
23525     getAutoCreate : function()
23526     {
23527         this.after = '<i class="fa far fa-clock"></i>';
23528         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23529         
23530          
23531     },
23532     onRender: function(ct, position)
23533     {
23534         
23535         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23536                 
23537         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23538         
23539         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23540         
23541         this.pop = this.picker().select('>.datepicker-time',true).first();
23542         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23543         
23544         this.picker().on('mousedown', this.onMousedown, this);
23545         this.picker().on('click', this.onClick, this);
23546         
23547         this.picker().addClass('datepicker-dropdown');
23548     
23549         this.fillTime();
23550         this.update();
23551             
23552         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23553         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23554         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23555         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23556         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23557         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23558
23559     },
23560     
23561     fireKey: function(e){
23562         if (!this.picker().isVisible()){
23563             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23564                 this.show();
23565             }
23566             return;
23567         }
23568
23569         e.preventDefault();
23570         
23571         switch(e.keyCode){
23572             case 27: // escape
23573                 this.hide();
23574                 break;
23575             case 37: // left
23576             case 39: // right
23577                 this.onTogglePeriod();
23578                 break;
23579             case 38: // up
23580                 this.onIncrementMinutes();
23581                 break;
23582             case 40: // down
23583                 this.onDecrementMinutes();
23584                 break;
23585             case 13: // enter
23586             case 9: // tab
23587                 this.setTime();
23588                 break;
23589         }
23590     },
23591     
23592     onClick: function(e) {
23593         e.stopPropagation();
23594         e.preventDefault();
23595     },
23596     
23597     picker : function()
23598     {
23599         return this.pickerEl;
23600     },
23601     
23602     fillTime: function()
23603     {    
23604         var time = this.pop.select('tbody', true).first();
23605         
23606         time.dom.innerHTML = '';
23607         
23608         time.createChild({
23609             tag: 'tr',
23610             cn: [
23611                 {
23612                     tag: 'td',
23613                     cn: [
23614                         {
23615                             tag: 'a',
23616                             href: '#',
23617                             cls: 'btn',
23618                             cn: [
23619                                 {
23620                                     tag: 'i',
23621                                     cls: 'hours-up fa fas fa-chevron-up'
23622                                 }
23623                             ]
23624                         } 
23625                     ]
23626                 },
23627                 {
23628                     tag: 'td',
23629                     cls: 'separator'
23630                 },
23631                 {
23632                     tag: 'td',
23633                     cn: [
23634                         {
23635                             tag: 'a',
23636                             href: '#',
23637                             cls: 'btn',
23638                             cn: [
23639                                 {
23640                                     tag: 'i',
23641                                     cls: 'minutes-up fa fas fa-chevron-up'
23642                                 }
23643                             ]
23644                         }
23645                     ]
23646                 },
23647                 {
23648                     tag: 'td',
23649                     cls: 'separator'
23650                 }
23651             ]
23652         });
23653         
23654         time.createChild({
23655             tag: 'tr',
23656             cn: [
23657                 {
23658                     tag: 'td',
23659                     cn: [
23660                         {
23661                             tag: 'span',
23662                             cls: 'timepicker-hour',
23663                             html: '00'
23664                         }  
23665                     ]
23666                 },
23667                 {
23668                     tag: 'td',
23669                     cls: 'separator',
23670                     html: ':'
23671                 },
23672                 {
23673                     tag: 'td',
23674                     cn: [
23675                         {
23676                             tag: 'span',
23677                             cls: 'timepicker-minute',
23678                             html: '00'
23679                         }  
23680                     ]
23681                 },
23682                 {
23683                     tag: 'td',
23684                     cls: 'separator'
23685                 },
23686                 {
23687                     tag: 'td',
23688                     cn: [
23689                         {
23690                             tag: 'button',
23691                             type: 'button',
23692                             cls: 'btn btn-primary period',
23693                             html: 'AM'
23694                             
23695                         }
23696                     ]
23697                 }
23698             ]
23699         });
23700         
23701         time.createChild({
23702             tag: 'tr',
23703             cn: [
23704                 {
23705                     tag: 'td',
23706                     cn: [
23707                         {
23708                             tag: 'a',
23709                             href: '#',
23710                             cls: 'btn',
23711                             cn: [
23712                                 {
23713                                     tag: 'span',
23714                                     cls: 'hours-down fa fas fa-chevron-down'
23715                                 }
23716                             ]
23717                         }
23718                     ]
23719                 },
23720                 {
23721                     tag: 'td',
23722                     cls: 'separator'
23723                 },
23724                 {
23725                     tag: 'td',
23726                     cn: [
23727                         {
23728                             tag: 'a',
23729                             href: '#',
23730                             cls: 'btn',
23731                             cn: [
23732                                 {
23733                                     tag: 'span',
23734                                     cls: 'minutes-down fa fas fa-chevron-down'
23735                                 }
23736                             ]
23737                         }
23738                     ]
23739                 },
23740                 {
23741                     tag: 'td',
23742                     cls: 'separator'
23743                 }
23744             ]
23745         });
23746         
23747     },
23748     
23749     update: function()
23750     {
23751         
23752         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23753         
23754         this.fill();
23755     },
23756     
23757     fill: function() 
23758     {
23759         var hours = this.time.getHours();
23760         var minutes = this.time.getMinutes();
23761         var period = 'AM';
23762         
23763         if(hours > 11){
23764             period = 'PM';
23765         }
23766         
23767         if(hours == 0){
23768             hours = 12;
23769         }
23770         
23771         
23772         if(hours > 12){
23773             hours = hours - 12;
23774         }
23775         
23776         if(hours < 10){
23777             hours = '0' + hours;
23778         }
23779         
23780         if(minutes < 10){
23781             minutes = '0' + minutes;
23782         }
23783         
23784         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23785         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23786         this.pop.select('button', true).first().dom.innerHTML = period;
23787         
23788     },
23789     
23790     place: function()
23791     {   
23792         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23793         
23794         var cls = ['bottom'];
23795         
23796         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23797             cls.pop();
23798             cls.push('top');
23799         }
23800         
23801         cls.push('right');
23802         
23803         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23804             cls.pop();
23805             cls.push('left');
23806         }
23807         //this.picker().setXY(20000,20000);
23808         this.picker().addClass(cls.join('-'));
23809         
23810         var _this = this;
23811         
23812         Roo.each(cls, function(c){
23813             if(c == 'bottom'){
23814                 (function() {
23815                  //  
23816                 }).defer(200);
23817                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23818                 //_this.picker().setTop(_this.inputEl().getHeight());
23819                 return;
23820             }
23821             if(c == 'top'){
23822                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23823                 
23824                 //_this.picker().setTop(0 - _this.picker().getHeight());
23825                 return;
23826             }
23827             /*
23828             if(c == 'left'){
23829                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23830                 return;
23831             }
23832             if(c == 'right'){
23833                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23834                 return;
23835             }
23836             */
23837         });
23838         
23839     },
23840   
23841     onFocus : function()
23842     {
23843         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23844         this.show();
23845     },
23846     
23847     onBlur : function()
23848     {
23849         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23850         this.hide();
23851     },
23852     
23853     show : function()
23854     {
23855         this.picker().show();
23856         this.pop.show();
23857         this.update();
23858         this.place();
23859         
23860         this.fireEvent('show', this, this.date);
23861     },
23862     
23863     hide : function()
23864     {
23865         this.picker().hide();
23866         this.pop.hide();
23867         
23868         this.fireEvent('hide', this, this.date);
23869     },
23870     
23871     setTime : function()
23872     {
23873         this.hide();
23874         this.setValue(this.time.format(this.format));
23875         
23876         this.fireEvent('select', this, this.date);
23877         
23878         
23879     },
23880     
23881     onMousedown: function(e){
23882         e.stopPropagation();
23883         e.preventDefault();
23884     },
23885     
23886     onIncrementHours: function()
23887     {
23888         Roo.log('onIncrementHours');
23889         this.time = this.time.add(Date.HOUR, 1);
23890         this.update();
23891         
23892     },
23893     
23894     onDecrementHours: function()
23895     {
23896         Roo.log('onDecrementHours');
23897         this.time = this.time.add(Date.HOUR, -1);
23898         this.update();
23899     },
23900     
23901     onIncrementMinutes: function()
23902     {
23903         Roo.log('onIncrementMinutes');
23904         this.time = this.time.add(Date.MINUTE, 1);
23905         this.update();
23906     },
23907     
23908     onDecrementMinutes: function()
23909     {
23910         Roo.log('onDecrementMinutes');
23911         this.time = this.time.add(Date.MINUTE, -1);
23912         this.update();
23913     },
23914     
23915     onTogglePeriod: function()
23916     {
23917         Roo.log('onTogglePeriod');
23918         this.time = this.time.add(Date.HOUR, 12);
23919         this.update();
23920     }
23921     
23922    
23923 });
23924  
23925
23926 Roo.apply(Roo.bootstrap.TimeField,  {
23927   
23928     template : {
23929         tag: 'div',
23930         cls: 'datepicker dropdown-menu',
23931         cn: [
23932             {
23933                 tag: 'div',
23934                 cls: 'datepicker-time',
23935                 cn: [
23936                 {
23937                     tag: 'table',
23938                     cls: 'table-condensed',
23939                     cn:[
23940                         {
23941                             tag: 'tbody',
23942                             cn: [
23943                                 {
23944                                     tag: 'tr',
23945                                     cn: [
23946                                     {
23947                                         tag: 'td',
23948                                         colspan: '7'
23949                                     }
23950                                     ]
23951                                 }
23952                             ]
23953                         },
23954                         {
23955                             tag: 'tfoot',
23956                             cn: [
23957                                 {
23958                                     tag: 'tr',
23959                                     cn: [
23960                                     {
23961                                         tag: 'th',
23962                                         colspan: '7',
23963                                         cls: '',
23964                                         cn: [
23965                                             {
23966                                                 tag: 'button',
23967                                                 cls: 'btn btn-info ok',
23968                                                 html: 'OK'
23969                                             }
23970                                         ]
23971                                     }
23972                     
23973                                     ]
23974                                 }
23975                             ]
23976                         }
23977                     ]
23978                 }
23979                 ]
23980             }
23981         ]
23982     }
23983 });
23984
23985  
23986
23987  /*
23988  * - LGPL
23989  *
23990  * MonthField
23991  * 
23992  */
23993
23994 /**
23995  * @class Roo.bootstrap.MonthField
23996  * @extends Roo.bootstrap.Input
23997  * Bootstrap MonthField class
23998  * 
23999  * @cfg {String} language default en
24000  * 
24001  * @constructor
24002  * Create a new MonthField
24003  * @param {Object} config The config object
24004  */
24005
24006 Roo.bootstrap.MonthField = function(config){
24007     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
24008     
24009     this.addEvents({
24010         /**
24011          * @event show
24012          * Fires when this field show.
24013          * @param {Roo.bootstrap.MonthField} this
24014          * @param {Mixed} date The date value
24015          */
24016         show : true,
24017         /**
24018          * @event show
24019          * Fires when this field hide.
24020          * @param {Roo.bootstrap.MonthField} this
24021          * @param {Mixed} date The date value
24022          */
24023         hide : true,
24024         /**
24025          * @event select
24026          * Fires when select a date.
24027          * @param {Roo.bootstrap.MonthField} this
24028          * @param {String} oldvalue The old value
24029          * @param {String} newvalue The new value
24030          */
24031         select : true
24032     });
24033 };
24034
24035 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24036     
24037     onRender: function(ct, position)
24038     {
24039         
24040         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24041         
24042         this.language = this.language || 'en';
24043         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24044         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24045         
24046         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24047         this.isInline = false;
24048         this.isInput = true;
24049         this.component = this.el.select('.add-on', true).first() || false;
24050         this.component = (this.component && this.component.length === 0) ? false : this.component;
24051         this.hasInput = this.component && this.inputEL().length;
24052         
24053         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24054         
24055         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24056         
24057         this.picker().on('mousedown', this.onMousedown, this);
24058         this.picker().on('click', this.onClick, this);
24059         
24060         this.picker().addClass('datepicker-dropdown');
24061         
24062         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24063             v.setStyle('width', '189px');
24064         });
24065         
24066         this.fillMonths();
24067         
24068         this.update();
24069         
24070         if(this.isInline) {
24071             this.show();
24072         }
24073         
24074     },
24075     
24076     setValue: function(v, suppressEvent)
24077     {   
24078         var o = this.getValue();
24079         
24080         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24081         
24082         this.update();
24083
24084         if(suppressEvent !== true){
24085             this.fireEvent('select', this, o, v);
24086         }
24087         
24088     },
24089     
24090     getValue: function()
24091     {
24092         return this.value;
24093     },
24094     
24095     onClick: function(e) 
24096     {
24097         e.stopPropagation();
24098         e.preventDefault();
24099         
24100         var target = e.getTarget();
24101         
24102         if(target.nodeName.toLowerCase() === 'i'){
24103             target = Roo.get(target).dom.parentNode;
24104         }
24105         
24106         var nodeName = target.nodeName;
24107         var className = target.className;
24108         var html = target.innerHTML;
24109         
24110         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24111             return;
24112         }
24113         
24114         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24115         
24116         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24117         
24118         this.hide();
24119                         
24120     },
24121     
24122     picker : function()
24123     {
24124         return this.pickerEl;
24125     },
24126     
24127     fillMonths: function()
24128     {    
24129         var i = 0;
24130         var months = this.picker().select('>.datepicker-months td', true).first();
24131         
24132         months.dom.innerHTML = '';
24133         
24134         while (i < 12) {
24135             var month = {
24136                 tag: 'span',
24137                 cls: 'month',
24138                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24139             };
24140             
24141             months.createChild(month);
24142         }
24143         
24144     },
24145     
24146     update: function()
24147     {
24148         var _this = this;
24149         
24150         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24151             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24152         }
24153         
24154         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24155             e.removeClass('active');
24156             
24157             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24158                 e.addClass('active');
24159             }
24160         })
24161     },
24162     
24163     place: function()
24164     {
24165         if(this.isInline) {
24166             return;
24167         }
24168         
24169         this.picker().removeClass(['bottom', 'top']);
24170         
24171         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24172             /*
24173              * place to the top of element!
24174              *
24175              */
24176             
24177             this.picker().addClass('top');
24178             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24179             
24180             return;
24181         }
24182         
24183         this.picker().addClass('bottom');
24184         
24185         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24186     },
24187     
24188     onFocus : function()
24189     {
24190         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24191         this.show();
24192     },
24193     
24194     onBlur : function()
24195     {
24196         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24197         
24198         var d = this.inputEl().getValue();
24199         
24200         this.setValue(d);
24201                 
24202         this.hide();
24203     },
24204     
24205     show : function()
24206     {
24207         this.picker().show();
24208         this.picker().select('>.datepicker-months', true).first().show();
24209         this.update();
24210         this.place();
24211         
24212         this.fireEvent('show', this, this.date);
24213     },
24214     
24215     hide : function()
24216     {
24217         if(this.isInline) {
24218             return;
24219         }
24220         this.picker().hide();
24221         this.fireEvent('hide', this, this.date);
24222         
24223     },
24224     
24225     onMousedown: function(e)
24226     {
24227         e.stopPropagation();
24228         e.preventDefault();
24229     },
24230     
24231     keyup: function(e)
24232     {
24233         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24234         this.update();
24235     },
24236
24237     fireKey: function(e)
24238     {
24239         if (!this.picker().isVisible()){
24240             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24241                 this.show();
24242             }
24243             return;
24244         }
24245         
24246         var dir;
24247         
24248         switch(e.keyCode){
24249             case 27: // escape
24250                 this.hide();
24251                 e.preventDefault();
24252                 break;
24253             case 37: // left
24254             case 39: // right
24255                 dir = e.keyCode == 37 ? -1 : 1;
24256                 
24257                 this.vIndex = this.vIndex + dir;
24258                 
24259                 if(this.vIndex < 0){
24260                     this.vIndex = 0;
24261                 }
24262                 
24263                 if(this.vIndex > 11){
24264                     this.vIndex = 11;
24265                 }
24266                 
24267                 if(isNaN(this.vIndex)){
24268                     this.vIndex = 0;
24269                 }
24270                 
24271                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24272                 
24273                 break;
24274             case 38: // up
24275             case 40: // down
24276                 
24277                 dir = e.keyCode == 38 ? -1 : 1;
24278                 
24279                 this.vIndex = this.vIndex + dir * 4;
24280                 
24281                 if(this.vIndex < 0){
24282                     this.vIndex = 0;
24283                 }
24284                 
24285                 if(this.vIndex > 11){
24286                     this.vIndex = 11;
24287                 }
24288                 
24289                 if(isNaN(this.vIndex)){
24290                     this.vIndex = 0;
24291                 }
24292                 
24293                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24294                 break;
24295                 
24296             case 13: // enter
24297                 
24298                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24299                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24300                 }
24301                 
24302                 this.hide();
24303                 e.preventDefault();
24304                 break;
24305             case 9: // tab
24306                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24307                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24308                 }
24309                 this.hide();
24310                 break;
24311             case 16: // shift
24312             case 17: // ctrl
24313             case 18: // alt
24314                 break;
24315             default :
24316                 this.hide();
24317                 
24318         }
24319     },
24320     
24321     remove: function() 
24322     {
24323         this.picker().remove();
24324     }
24325    
24326 });
24327
24328 Roo.apply(Roo.bootstrap.MonthField,  {
24329     
24330     content : {
24331         tag: 'tbody',
24332         cn: [
24333         {
24334             tag: 'tr',
24335             cn: [
24336             {
24337                 tag: 'td',
24338                 colspan: '7'
24339             }
24340             ]
24341         }
24342         ]
24343     },
24344     
24345     dates:{
24346         en: {
24347             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24348             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24349         }
24350     }
24351 });
24352
24353 Roo.apply(Roo.bootstrap.MonthField,  {
24354   
24355     template : {
24356         tag: 'div',
24357         cls: 'datepicker dropdown-menu roo-dynamic',
24358         cn: [
24359             {
24360                 tag: 'div',
24361                 cls: 'datepicker-months',
24362                 cn: [
24363                 {
24364                     tag: 'table',
24365                     cls: 'table-condensed',
24366                     cn:[
24367                         Roo.bootstrap.DateField.content
24368                     ]
24369                 }
24370                 ]
24371             }
24372         ]
24373     }
24374 });
24375
24376  
24377
24378  
24379  /*
24380  * - LGPL
24381  *
24382  * CheckBox
24383  * 
24384  */
24385
24386 /**
24387  * @class Roo.bootstrap.CheckBox
24388  * @extends Roo.bootstrap.Input
24389  * Bootstrap CheckBox class
24390  * 
24391  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24392  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24393  * @cfg {String} boxLabel The text that appears beside the checkbox
24394  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24395  * @cfg {Boolean} checked initnal the element
24396  * @cfg {Boolean} inline inline the element (default false)
24397  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24398  * @cfg {String} tooltip label tooltip
24399  * 
24400  * @constructor
24401  * Create a new CheckBox
24402  * @param {Object} config The config object
24403  */
24404
24405 Roo.bootstrap.CheckBox = function(config){
24406     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24407    
24408     this.addEvents({
24409         /**
24410         * @event check
24411         * Fires when the element is checked or unchecked.
24412         * @param {Roo.bootstrap.CheckBox} this This input
24413         * @param {Boolean} checked The new checked value
24414         */
24415        check : true,
24416        /**
24417         * @event click
24418         * Fires when the element is click.
24419         * @param {Roo.bootstrap.CheckBox} this This input
24420         */
24421        click : true
24422     });
24423     
24424 };
24425
24426 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24427   
24428     inputType: 'checkbox',
24429     inputValue: 1,
24430     valueOff: 0,
24431     boxLabel: false,
24432     checked: false,
24433     weight : false,
24434     inline: false,
24435     tooltip : '',
24436     
24437     // checkbox success does not make any sense really.. 
24438     invalidClass : "",
24439     validClass : "",
24440     
24441     
24442     getAutoCreate : function()
24443     {
24444         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24445         
24446         var id = Roo.id();
24447         
24448         var cfg = {};
24449         
24450         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24451         
24452         if(this.inline){
24453             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24454         }
24455         
24456         var input =  {
24457             tag: 'input',
24458             id : id,
24459             type : this.inputType,
24460             value : this.inputValue,
24461             cls : 'roo-' + this.inputType, //'form-box',
24462             placeholder : this.placeholder || ''
24463             
24464         };
24465         
24466         if(this.inputType != 'radio'){
24467             var hidden =  {
24468                 tag: 'input',
24469                 type : 'hidden',
24470                 cls : 'roo-hidden-value',
24471                 value : this.checked ? this.inputValue : this.valueOff
24472             };
24473         }
24474         
24475             
24476         if (this.weight) { // Validity check?
24477             cfg.cls += " " + this.inputType + "-" + this.weight;
24478         }
24479         
24480         if (this.disabled) {
24481             input.disabled=true;
24482         }
24483         
24484         if(this.checked){
24485             input.checked = this.checked;
24486         }
24487         
24488         if (this.name) {
24489             
24490             input.name = this.name;
24491             
24492             if(this.inputType != 'radio'){
24493                 hidden.name = this.name;
24494                 input.name = '_hidden_' + this.name;
24495             }
24496         }
24497         
24498         if (this.size) {
24499             input.cls += ' input-' + this.size;
24500         }
24501         
24502         var settings=this;
24503         
24504         ['xs','sm','md','lg'].map(function(size){
24505             if (settings[size]) {
24506                 cfg.cls += ' col-' + size + '-' + settings[size];
24507             }
24508         });
24509         
24510         var inputblock = input;
24511          
24512         if (this.before || this.after) {
24513             
24514             inputblock = {
24515                 cls : 'input-group',
24516                 cn :  [] 
24517             };
24518             
24519             if (this.before) {
24520                 inputblock.cn.push({
24521                     tag :'span',
24522                     cls : 'input-group-addon',
24523                     html : this.before
24524                 });
24525             }
24526             
24527             inputblock.cn.push(input);
24528             
24529             if(this.inputType != 'radio'){
24530                 inputblock.cn.push(hidden);
24531             }
24532             
24533             if (this.after) {
24534                 inputblock.cn.push({
24535                     tag :'span',
24536                     cls : 'input-group-addon',
24537                     html : this.after
24538                 });
24539             }
24540             
24541         }
24542         var boxLabelCfg = false;
24543         
24544         if(this.boxLabel){
24545            
24546             boxLabelCfg = {
24547                 tag: 'label',
24548                 //'for': id, // box label is handled by onclick - so no for...
24549                 cls: 'box-label',
24550                 html: this.boxLabel
24551             };
24552             if(this.tooltip){
24553                 boxLabelCfg.tooltip = this.tooltip;
24554             }
24555              
24556         }
24557         
24558         
24559         if (align ==='left' && this.fieldLabel.length) {
24560 //                Roo.log("left and has label");
24561             cfg.cn = [
24562                 {
24563                     tag: 'label',
24564                     'for' :  id,
24565                     cls : 'control-label',
24566                     html : this.fieldLabel
24567                 },
24568                 {
24569                     cls : "", 
24570                     cn: [
24571                         inputblock
24572                     ]
24573                 }
24574             ];
24575             
24576             if (boxLabelCfg) {
24577                 cfg.cn[1].cn.push(boxLabelCfg);
24578             }
24579             
24580             if(this.labelWidth > 12){
24581                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24582             }
24583             
24584             if(this.labelWidth < 13 && this.labelmd == 0){
24585                 this.labelmd = this.labelWidth;
24586             }
24587             
24588             if(this.labellg > 0){
24589                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24590                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24591             }
24592             
24593             if(this.labelmd > 0){
24594                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24595                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24596             }
24597             
24598             if(this.labelsm > 0){
24599                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24600                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24601             }
24602             
24603             if(this.labelxs > 0){
24604                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24605                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24606             }
24607             
24608         } else if ( this.fieldLabel.length) {
24609 //                Roo.log(" label");
24610                 cfg.cn = [
24611                    
24612                     {
24613                         tag: this.boxLabel ? 'span' : 'label',
24614                         'for': id,
24615                         cls: 'control-label box-input-label',
24616                         //cls : 'input-group-addon',
24617                         html : this.fieldLabel
24618                     },
24619                     
24620                     inputblock
24621                     
24622                 ];
24623                 if (boxLabelCfg) {
24624                     cfg.cn.push(boxLabelCfg);
24625                 }
24626
24627         } else {
24628             
24629 //                Roo.log(" no label && no align");
24630                 cfg.cn = [  inputblock ] ;
24631                 if (boxLabelCfg) {
24632                     cfg.cn.push(boxLabelCfg);
24633                 }
24634
24635                 
24636         }
24637         
24638        
24639         
24640         if(this.inputType != 'radio'){
24641             cfg.cn.push(hidden);
24642         }
24643         
24644         return cfg;
24645         
24646     },
24647     
24648     /**
24649      * return the real input element.
24650      */
24651     inputEl: function ()
24652     {
24653         return this.el.select('input.roo-' + this.inputType,true).first();
24654     },
24655     hiddenEl: function ()
24656     {
24657         return this.el.select('input.roo-hidden-value',true).first();
24658     },
24659     
24660     labelEl: function()
24661     {
24662         return this.el.select('label.control-label',true).first();
24663     },
24664     /* depricated... */
24665     
24666     label: function()
24667     {
24668         return this.labelEl();
24669     },
24670     
24671     boxLabelEl: function()
24672     {
24673         return this.el.select('label.box-label',true).first();
24674     },
24675     
24676     initEvents : function()
24677     {
24678 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24679         
24680         this.inputEl().on('click', this.onClick,  this);
24681         
24682         if (this.boxLabel) { 
24683             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24684         }
24685         
24686         this.startValue = this.getValue();
24687         
24688         if(this.groupId){
24689             Roo.bootstrap.CheckBox.register(this);
24690         }
24691     },
24692     
24693     onClick : function(e)
24694     {   
24695         if(this.fireEvent('click', this, e) !== false){
24696             this.setChecked(!this.checked);
24697         }
24698         
24699     },
24700     
24701     setChecked : function(state,suppressEvent)
24702     {
24703         this.startValue = this.getValue();
24704
24705         if(this.inputType == 'radio'){
24706             
24707             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24708                 e.dom.checked = false;
24709             });
24710             
24711             this.inputEl().dom.checked = true;
24712             
24713             this.inputEl().dom.value = this.inputValue;
24714             
24715             if(suppressEvent !== true){
24716                 this.fireEvent('check', this, true);
24717             }
24718             
24719             this.validate();
24720             
24721             return;
24722         }
24723         
24724         this.checked = state;
24725         
24726         this.inputEl().dom.checked = state;
24727         
24728         
24729         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24730         
24731         if(suppressEvent !== true){
24732             this.fireEvent('check', this, state);
24733         }
24734         
24735         this.validate();
24736     },
24737     
24738     getValue : function()
24739     {
24740         if(this.inputType == 'radio'){
24741             return this.getGroupValue();
24742         }
24743         
24744         return this.hiddenEl().dom.value;
24745         
24746     },
24747     
24748     getGroupValue : function()
24749     {
24750         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24751             return '';
24752         }
24753         
24754         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24755     },
24756     
24757     setValue : function(v,suppressEvent)
24758     {
24759         if(this.inputType == 'radio'){
24760             this.setGroupValue(v, suppressEvent);
24761             return;
24762         }
24763         
24764         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24765         
24766         this.validate();
24767     },
24768     
24769     setGroupValue : function(v, suppressEvent)
24770     {
24771         this.startValue = this.getValue();
24772         
24773         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24774             e.dom.checked = false;
24775             
24776             if(e.dom.value == v){
24777                 e.dom.checked = true;
24778             }
24779         });
24780         
24781         if(suppressEvent !== true){
24782             this.fireEvent('check', this, true);
24783         }
24784
24785         this.validate();
24786         
24787         return;
24788     },
24789     
24790     validate : function()
24791     {
24792         if(this.getVisibilityEl().hasClass('hidden')){
24793             return true;
24794         }
24795         
24796         if(
24797                 this.disabled || 
24798                 (this.inputType == 'radio' && this.validateRadio()) ||
24799                 (this.inputType == 'checkbox' && this.validateCheckbox())
24800         ){
24801             this.markValid();
24802             return true;
24803         }
24804         
24805         this.markInvalid();
24806         return false;
24807     },
24808     
24809     validateRadio : function()
24810     {
24811         if(this.getVisibilityEl().hasClass('hidden')){
24812             return true;
24813         }
24814         
24815         if(this.allowBlank){
24816             return true;
24817         }
24818         
24819         var valid = false;
24820         
24821         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24822             if(!e.dom.checked){
24823                 return;
24824             }
24825             
24826             valid = true;
24827             
24828             return false;
24829         });
24830         
24831         return valid;
24832     },
24833     
24834     validateCheckbox : function()
24835     {
24836         if(!this.groupId){
24837             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24838             //return (this.getValue() == this.inputValue) ? true : false;
24839         }
24840         
24841         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24842         
24843         if(!group){
24844             return false;
24845         }
24846         
24847         var r = false;
24848         
24849         for(var i in group){
24850             if(group[i].el.isVisible(true)){
24851                 r = false;
24852                 break;
24853             }
24854             
24855             r = true;
24856         }
24857         
24858         for(var i in group){
24859             if(r){
24860                 break;
24861             }
24862             
24863             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24864         }
24865         
24866         return r;
24867     },
24868     
24869     /**
24870      * Mark this field as valid
24871      */
24872     markValid : function()
24873     {
24874         var _this = this;
24875         
24876         this.fireEvent('valid', this);
24877         
24878         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24879         
24880         if(this.groupId){
24881             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24882         }
24883         
24884         if(label){
24885             label.markValid();
24886         }
24887
24888         if(this.inputType == 'radio'){
24889             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24890                 var fg = e.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             });
24899             
24900             return;
24901         }
24902
24903         if(!this.groupId){
24904             var fg = this.el.findParent('.form-group', false, true);
24905             if (Roo.bootstrap.version == 3) {
24906                 fg.removeClass([this.invalidClass, this.validClass]);
24907                 fg.addClass(this.validClass);
24908             } else {
24909                 fg.removeClass(['is-valid', 'is-invalid']);
24910                 fg.addClass('is-valid');
24911             }
24912             return;
24913         }
24914         
24915         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24916         
24917         if(!group){
24918             return;
24919         }
24920         
24921         for(var i in group){
24922             var fg = group[i].el.findParent('.form-group', false, true);
24923             if (Roo.bootstrap.version == 3) {
24924                 fg.removeClass([this.invalidClass, this.validClass]);
24925                 fg.addClass(this.validClass);
24926             } else {
24927                 fg.removeClass(['is-valid', 'is-invalid']);
24928                 fg.addClass('is-valid');
24929             }
24930         }
24931     },
24932     
24933      /**
24934      * Mark this field as invalid
24935      * @param {String} msg The validation message
24936      */
24937     markInvalid : function(msg)
24938     {
24939         if(this.allowBlank){
24940             return;
24941         }
24942         
24943         var _this = this;
24944         
24945         this.fireEvent('invalid', this, msg);
24946         
24947         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24948         
24949         if(this.groupId){
24950             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24951         }
24952         
24953         if(label){
24954             label.markInvalid();
24955         }
24956             
24957         if(this.inputType == 'radio'){
24958             
24959             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24960                 var fg = e.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             });
24969             
24970             return;
24971         }
24972         
24973         if(!this.groupId){
24974             var fg = this.el.findParent('.form-group', false, true);
24975             if (Roo.bootstrap.version == 3) {
24976                 fg.removeClass([_this.invalidClass, _this.validClass]);
24977                 fg.addClass(_this.invalidClass);
24978             } else {
24979                 fg.removeClass(['is-invalid', 'is-valid']);
24980                 fg.addClass('is-invalid');
24981             }
24982             return;
24983         }
24984         
24985         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24986         
24987         if(!group){
24988             return;
24989         }
24990         
24991         for(var i in group){
24992             var fg = group[i].el.findParent('.form-group', false, true);
24993             if (Roo.bootstrap.version == 3) {
24994                 fg.removeClass([_this.invalidClass, _this.validClass]);
24995                 fg.addClass(_this.invalidClass);
24996             } else {
24997                 fg.removeClass(['is-invalid', 'is-valid']);
24998                 fg.addClass('is-invalid');
24999             }
25000         }
25001         
25002     },
25003     
25004     clearInvalid : function()
25005     {
25006         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
25007         
25008         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25009         
25010         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
25011         
25012         if (label && label.iconEl) {
25013             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25014             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25015         }
25016     },
25017     
25018     disable : function()
25019     {
25020         if(this.inputType != 'radio'){
25021             Roo.bootstrap.CheckBox.superclass.disable.call(this);
25022             return;
25023         }
25024         
25025         var _this = this;
25026         
25027         if(this.rendered){
25028             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25029                 _this.getActionEl().addClass(this.disabledClass);
25030                 e.dom.disabled = true;
25031             });
25032         }
25033         
25034         this.disabled = true;
25035         this.fireEvent("disable", this);
25036         return this;
25037     },
25038
25039     enable : function()
25040     {
25041         if(this.inputType != 'radio'){
25042             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25043             return;
25044         }
25045         
25046         var _this = this;
25047         
25048         if(this.rendered){
25049             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25050                 _this.getActionEl().removeClass(this.disabledClass);
25051                 e.dom.disabled = false;
25052             });
25053         }
25054         
25055         this.disabled = false;
25056         this.fireEvent("enable", this);
25057         return this;
25058     },
25059     
25060     setBoxLabel : function(v)
25061     {
25062         this.boxLabel = v;
25063         
25064         if(this.rendered){
25065             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25066         }
25067     }
25068
25069 });
25070
25071 Roo.apply(Roo.bootstrap.CheckBox, {
25072     
25073     groups: {},
25074     
25075      /**
25076     * register a CheckBox Group
25077     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25078     */
25079     register : function(checkbox)
25080     {
25081         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25082             this.groups[checkbox.groupId] = {};
25083         }
25084         
25085         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25086             return;
25087         }
25088         
25089         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25090         
25091     },
25092     /**
25093     * fetch a CheckBox Group based on the group ID
25094     * @param {string} the group ID
25095     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25096     */
25097     get: function(groupId) {
25098         if (typeof(this.groups[groupId]) == 'undefined') {
25099             return false;
25100         }
25101         
25102         return this.groups[groupId] ;
25103     }
25104     
25105     
25106 });
25107 /*
25108  * - LGPL
25109  *
25110  * RadioItem
25111  * 
25112  */
25113
25114 /**
25115  * @class Roo.bootstrap.Radio
25116  * @extends Roo.bootstrap.Component
25117  * Bootstrap Radio class
25118  * @cfg {String} boxLabel - the label associated
25119  * @cfg {String} value - the value of radio
25120  * 
25121  * @constructor
25122  * Create a new Radio
25123  * @param {Object} config The config object
25124  */
25125 Roo.bootstrap.Radio = function(config){
25126     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25127     
25128 };
25129
25130 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25131     
25132     boxLabel : '',
25133     
25134     value : '',
25135     
25136     getAutoCreate : function()
25137     {
25138         var cfg = {
25139             tag : 'div',
25140             cls : 'form-group radio',
25141             cn : [
25142                 {
25143                     tag : 'label',
25144                     cls : 'box-label',
25145                     html : this.boxLabel
25146                 }
25147             ]
25148         };
25149         
25150         return cfg;
25151     },
25152     
25153     initEvents : function() 
25154     {
25155         this.parent().register(this);
25156         
25157         this.el.on('click', this.onClick, this);
25158         
25159     },
25160     
25161     onClick : function(e)
25162     {
25163         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25164             this.setChecked(true);
25165         }
25166     },
25167     
25168     setChecked : function(state, suppressEvent)
25169     {
25170         this.parent().setValue(this.value, suppressEvent);
25171         
25172     },
25173     
25174     setBoxLabel : function(v)
25175     {
25176         this.boxLabel = v;
25177         
25178         if(this.rendered){
25179             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25180         }
25181     }
25182     
25183 });
25184  
25185
25186  /*
25187  * - LGPL
25188  *
25189  * Input
25190  * 
25191  */
25192
25193 /**
25194  * @class Roo.bootstrap.SecurePass
25195  * @extends Roo.bootstrap.Input
25196  * Bootstrap SecurePass class
25197  *
25198  * 
25199  * @constructor
25200  * Create a new SecurePass
25201  * @param {Object} config The config object
25202  */
25203  
25204 Roo.bootstrap.SecurePass = function (config) {
25205     // these go here, so the translation tool can replace them..
25206     this.errors = {
25207         PwdEmpty: "Please type a password, and then retype it to confirm.",
25208         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25209         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25210         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25211         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25212         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25213         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25214         TooWeak: "Your password is Too Weak."
25215     },
25216     this.meterLabel = "Password strength:";
25217     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25218     this.meterClass = [
25219         "roo-password-meter-tooweak", 
25220         "roo-password-meter-weak", 
25221         "roo-password-meter-medium", 
25222         "roo-password-meter-strong", 
25223         "roo-password-meter-grey"
25224     ];
25225     
25226     this.errors = {};
25227     
25228     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25229 }
25230
25231 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25232     /**
25233      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25234      * {
25235      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25236      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25237      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25238      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25239      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25240      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25241      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25242      * })
25243      */
25244     // private
25245     
25246     meterWidth: 300,
25247     errorMsg :'',    
25248     errors: false,
25249     imageRoot: '/',
25250     /**
25251      * @cfg {String/Object} Label for the strength meter (defaults to
25252      * 'Password strength:')
25253      */
25254     // private
25255     meterLabel: '',
25256     /**
25257      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25258      * ['Weak', 'Medium', 'Strong'])
25259      */
25260     // private    
25261     pwdStrengths: false,    
25262     // private
25263     strength: 0,
25264     // private
25265     _lastPwd: null,
25266     // private
25267     kCapitalLetter: 0,
25268     kSmallLetter: 1,
25269     kDigit: 2,
25270     kPunctuation: 3,
25271     
25272     insecure: false,
25273     // private
25274     initEvents: function ()
25275     {
25276         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25277
25278         if (this.el.is('input[type=password]') && Roo.isSafari) {
25279             this.el.on('keydown', this.SafariOnKeyDown, this);
25280         }
25281
25282         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25283     },
25284     // private
25285     onRender: function (ct, position)
25286     {
25287         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25288         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25289         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25290
25291         this.trigger.createChild({
25292                    cn: [
25293                     {
25294                     //id: 'PwdMeter',
25295                     tag: 'div',
25296                     cls: 'roo-password-meter-grey col-xs-12',
25297                     style: {
25298                         //width: 0,
25299                         //width: this.meterWidth + 'px'                                                
25300                         }
25301                     },
25302                     {                            
25303                          cls: 'roo-password-meter-text'                          
25304                     }
25305                 ]            
25306         });
25307
25308          
25309         if (this.hideTrigger) {
25310             this.trigger.setDisplayed(false);
25311         }
25312         this.setSize(this.width || '', this.height || '');
25313     },
25314     // private
25315     onDestroy: function ()
25316     {
25317         if (this.trigger) {
25318             this.trigger.removeAllListeners();
25319             this.trigger.remove();
25320         }
25321         if (this.wrap) {
25322             this.wrap.remove();
25323         }
25324         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25325     },
25326     // private
25327     checkStrength: function ()
25328     {
25329         var pwd = this.inputEl().getValue();
25330         if (pwd == this._lastPwd) {
25331             return;
25332         }
25333
25334         var strength;
25335         if (this.ClientSideStrongPassword(pwd)) {
25336             strength = 3;
25337         } else if (this.ClientSideMediumPassword(pwd)) {
25338             strength = 2;
25339         } else if (this.ClientSideWeakPassword(pwd)) {
25340             strength = 1;
25341         } else {
25342             strength = 0;
25343         }
25344         
25345         Roo.log('strength1: ' + strength);
25346         
25347         //var pm = this.trigger.child('div/div/div').dom;
25348         var pm = this.trigger.child('div/div');
25349         pm.removeClass(this.meterClass);
25350         pm.addClass(this.meterClass[strength]);
25351                 
25352         
25353         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25354                 
25355         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25356         
25357         this._lastPwd = pwd;
25358     },
25359     reset: function ()
25360     {
25361         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25362         
25363         this._lastPwd = '';
25364         
25365         var pm = this.trigger.child('div/div');
25366         pm.removeClass(this.meterClass);
25367         pm.addClass('roo-password-meter-grey');        
25368         
25369         
25370         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25371         
25372         pt.innerHTML = '';
25373         this.inputEl().dom.type='password';
25374     },
25375     // private
25376     validateValue: function (value)
25377     {
25378         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25379             return false;
25380         }
25381         if (value.length == 0) {
25382             if (this.allowBlank) {
25383                 this.clearInvalid();
25384                 return true;
25385             }
25386
25387             this.markInvalid(this.errors.PwdEmpty);
25388             this.errorMsg = this.errors.PwdEmpty;
25389             return false;
25390         }
25391         
25392         if(this.insecure){
25393             return true;
25394         }
25395         
25396         if (!value.match(/[\x21-\x7e]+/)) {
25397             this.markInvalid(this.errors.PwdBadChar);
25398             this.errorMsg = this.errors.PwdBadChar;
25399             return false;
25400         }
25401         if (value.length < 6) {
25402             this.markInvalid(this.errors.PwdShort);
25403             this.errorMsg = this.errors.PwdShort;
25404             return false;
25405         }
25406         if (value.length > 16) {
25407             this.markInvalid(this.errors.PwdLong);
25408             this.errorMsg = this.errors.PwdLong;
25409             return false;
25410         }
25411         var strength;
25412         if (this.ClientSideStrongPassword(value)) {
25413             strength = 3;
25414         } else if (this.ClientSideMediumPassword(value)) {
25415             strength = 2;
25416         } else if (this.ClientSideWeakPassword(value)) {
25417             strength = 1;
25418         } else {
25419             strength = 0;
25420         }
25421
25422         
25423         if (strength < 2) {
25424             //this.markInvalid(this.errors.TooWeak);
25425             this.errorMsg = this.errors.TooWeak;
25426             //return false;
25427         }
25428         
25429         
25430         console.log('strength2: ' + strength);
25431         
25432         //var pm = this.trigger.child('div/div/div').dom;
25433         
25434         var pm = this.trigger.child('div/div');
25435         pm.removeClass(this.meterClass);
25436         pm.addClass(this.meterClass[strength]);
25437                 
25438         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25439                 
25440         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25441         
25442         this.errorMsg = ''; 
25443         return true;
25444     },
25445     // private
25446     CharacterSetChecks: function (type)
25447     {
25448         this.type = type;
25449         this.fResult = false;
25450     },
25451     // private
25452     isctype: function (character, type)
25453     {
25454         switch (type) {  
25455             case this.kCapitalLetter:
25456                 if (character >= 'A' && character <= 'Z') {
25457                     return true;
25458                 }
25459                 break;
25460             
25461             case this.kSmallLetter:
25462                 if (character >= 'a' && character <= 'z') {
25463                     return true;
25464                 }
25465                 break;
25466             
25467             case this.kDigit:
25468                 if (character >= '0' && character <= '9') {
25469                     return true;
25470                 }
25471                 break;
25472             
25473             case this.kPunctuation:
25474                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25475                     return true;
25476                 }
25477                 break;
25478             
25479             default:
25480                 return false;
25481         }
25482
25483     },
25484     // private
25485     IsLongEnough: function (pwd, size)
25486     {
25487         return !(pwd == null || isNaN(size) || pwd.length < size);
25488     },
25489     // private
25490     SpansEnoughCharacterSets: function (word, nb)
25491     {
25492         if (!this.IsLongEnough(word, nb))
25493         {
25494             return false;
25495         }
25496
25497         var characterSetChecks = new Array(
25498             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25499             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25500         );
25501         
25502         for (var index = 0; index < word.length; ++index) {
25503             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25504                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25505                     characterSetChecks[nCharSet].fResult = true;
25506                     break;
25507                 }
25508             }
25509         }
25510
25511         var nCharSets = 0;
25512         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25513             if (characterSetChecks[nCharSet].fResult) {
25514                 ++nCharSets;
25515             }
25516         }
25517
25518         if (nCharSets < nb) {
25519             return false;
25520         }
25521         return true;
25522     },
25523     // private
25524     ClientSideStrongPassword: function (pwd)
25525     {
25526         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25527     },
25528     // private
25529     ClientSideMediumPassword: function (pwd)
25530     {
25531         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25532     },
25533     // private
25534     ClientSideWeakPassword: function (pwd)
25535     {
25536         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25537     }
25538           
25539 })//<script type="text/javascript">
25540
25541 /*
25542  * Based  Ext JS Library 1.1.1
25543  * Copyright(c) 2006-2007, Ext JS, LLC.
25544  * LGPL
25545  *
25546  */
25547  
25548 /**
25549  * @class Roo.HtmlEditorCore
25550  * @extends Roo.Component
25551  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25552  *
25553  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25554  */
25555
25556 Roo.HtmlEditorCore = function(config){
25557     
25558     
25559     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25560     
25561     
25562     this.addEvents({
25563         /**
25564          * @event initialize
25565          * Fires when the editor is fully initialized (including the iframe)
25566          * @param {Roo.HtmlEditorCore} this
25567          */
25568         initialize: true,
25569         /**
25570          * @event activate
25571          * Fires when the editor is first receives the focus. Any insertion must wait
25572          * until after this event.
25573          * @param {Roo.HtmlEditorCore} this
25574          */
25575         activate: true,
25576          /**
25577          * @event beforesync
25578          * Fires before the textarea is updated with content from the editor iframe. Return false
25579          * to cancel the sync.
25580          * @param {Roo.HtmlEditorCore} this
25581          * @param {String} html
25582          */
25583         beforesync: true,
25584          /**
25585          * @event beforepush
25586          * Fires before the iframe editor is updated with content from the textarea. Return false
25587          * to cancel the push.
25588          * @param {Roo.HtmlEditorCore} this
25589          * @param {String} html
25590          */
25591         beforepush: true,
25592          /**
25593          * @event sync
25594          * Fires when the textarea is updated with content from the editor iframe.
25595          * @param {Roo.HtmlEditorCore} this
25596          * @param {String} html
25597          */
25598         sync: true,
25599          /**
25600          * @event push
25601          * Fires when the iframe editor is updated with content from the textarea.
25602          * @param {Roo.HtmlEditorCore} this
25603          * @param {String} html
25604          */
25605         push: true,
25606         
25607         /**
25608          * @event editorevent
25609          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25610          * @param {Roo.HtmlEditorCore} this
25611          */
25612         editorevent: true
25613         
25614     });
25615     
25616     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25617     
25618     // defaults : white / black...
25619     this.applyBlacklists();
25620     
25621     
25622     
25623 };
25624
25625
25626 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25627
25628
25629      /**
25630      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25631      */
25632     
25633     owner : false,
25634     
25635      /**
25636      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25637      *                        Roo.resizable.
25638      */
25639     resizable : false,
25640      /**
25641      * @cfg {Number} height (in pixels)
25642      */   
25643     height: 300,
25644    /**
25645      * @cfg {Number} width (in pixels)
25646      */   
25647     width: 500,
25648     
25649     /**
25650      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25651      * 
25652      */
25653     stylesheets: false,
25654     
25655     /**
25656      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25657      */
25658     allowComments: false,
25659     // id of frame..
25660     frameId: false,
25661     
25662     // private properties
25663     validationEvent : false,
25664     deferHeight: true,
25665     initialized : false,
25666     activated : false,
25667     sourceEditMode : false,
25668     onFocus : Roo.emptyFn,
25669     iframePad:3,
25670     hideMode:'offsets',
25671     
25672     clearUp: true,
25673     
25674     // blacklist + whitelisted elements..
25675     black: false,
25676     white: false,
25677      
25678     bodyCls : '',
25679
25680     /**
25681      * Protected method that will not generally be called directly. It
25682      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25683      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25684      */
25685     getDocMarkup : function(){
25686         // body styles..
25687         var st = '';
25688         
25689         // inherit styels from page...?? 
25690         if (this.stylesheets === false) {
25691             
25692             Roo.get(document.head).select('style').each(function(node) {
25693                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25694             });
25695             
25696             Roo.get(document.head).select('link').each(function(node) { 
25697                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25698             });
25699             
25700         } else if (!this.stylesheets.length) {
25701                 // simple..
25702                 st = '<style type="text/css">' +
25703                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25704                    '</style>';
25705         } else {
25706             for (var i in this.stylesheets) { 
25707                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25708             }
25709             
25710         }
25711         
25712         st +=  '<style type="text/css">' +
25713             'IMG { cursor: pointer } ' +
25714         '</style>';
25715
25716         var cls = 'roo-htmleditor-body';
25717         
25718         if(this.bodyCls.length){
25719             cls += ' ' + this.bodyCls;
25720         }
25721         
25722         return '<html><head>' + st  +
25723             //<style type="text/css">' +
25724             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25725             //'</style>' +
25726             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25727     },
25728
25729     // private
25730     onRender : function(ct, position)
25731     {
25732         var _t = this;
25733         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25734         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25735         
25736         
25737         this.el.dom.style.border = '0 none';
25738         this.el.dom.setAttribute('tabIndex', -1);
25739         this.el.addClass('x-hidden hide');
25740         
25741         
25742         
25743         if(Roo.isIE){ // fix IE 1px bogus margin
25744             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25745         }
25746        
25747         
25748         this.frameId = Roo.id();
25749         
25750          
25751         
25752         var iframe = this.owner.wrap.createChild({
25753             tag: 'iframe',
25754             cls: 'form-control', // bootstrap..
25755             id: this.frameId,
25756             name: this.frameId,
25757             frameBorder : 'no',
25758             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25759         }, this.el
25760         );
25761         
25762         
25763         this.iframe = iframe.dom;
25764
25765          this.assignDocWin();
25766         
25767         this.doc.designMode = 'on';
25768        
25769         this.doc.open();
25770         this.doc.write(this.getDocMarkup());
25771         this.doc.close();
25772
25773         
25774         var task = { // must defer to wait for browser to be ready
25775             run : function(){
25776                 //console.log("run task?" + this.doc.readyState);
25777                 this.assignDocWin();
25778                 if(this.doc.body || this.doc.readyState == 'complete'){
25779                     try {
25780                         this.doc.designMode="on";
25781                     } catch (e) {
25782                         return;
25783                     }
25784                     Roo.TaskMgr.stop(task);
25785                     this.initEditor.defer(10, this);
25786                 }
25787             },
25788             interval : 10,
25789             duration: 10000,
25790             scope: this
25791         };
25792         Roo.TaskMgr.start(task);
25793
25794     },
25795
25796     // private
25797     onResize : function(w, h)
25798     {
25799          Roo.log('resize: ' +w + ',' + h );
25800         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25801         if(!this.iframe){
25802             return;
25803         }
25804         if(typeof w == 'number'){
25805             
25806             this.iframe.style.width = w + 'px';
25807         }
25808         if(typeof h == 'number'){
25809             
25810             this.iframe.style.height = h + 'px';
25811             if(this.doc){
25812                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25813             }
25814         }
25815         
25816     },
25817
25818     /**
25819      * Toggles the editor between standard and source edit mode.
25820      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25821      */
25822     toggleSourceEdit : function(sourceEditMode){
25823         
25824         this.sourceEditMode = sourceEditMode === true;
25825         
25826         if(this.sourceEditMode){
25827  
25828             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25829             
25830         }else{
25831             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25832             //this.iframe.className = '';
25833             this.deferFocus();
25834         }
25835         //this.setSize(this.owner.wrap.getSize());
25836         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25837     },
25838
25839     
25840   
25841
25842     /**
25843      * Protected method that will not generally be called directly. If you need/want
25844      * custom HTML cleanup, this is the method you should override.
25845      * @param {String} html The HTML to be cleaned
25846      * return {String} The cleaned HTML
25847      */
25848     cleanHtml : function(html){
25849         html = String(html);
25850         if(html.length > 5){
25851             if(Roo.isSafari){ // strip safari nonsense
25852                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25853             }
25854         }
25855         if(html == '&nbsp;'){
25856             html = '';
25857         }
25858         return html;
25859     },
25860
25861     /**
25862      * HTML Editor -> Textarea
25863      * Protected method that will not generally be called directly. Syncs the contents
25864      * of the editor iframe with the textarea.
25865      */
25866     syncValue : function(){
25867         if(this.initialized){
25868             var bd = (this.doc.body || this.doc.documentElement);
25869             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25870             var html = bd.innerHTML;
25871             if(Roo.isSafari){
25872                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25873                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25874                 if(m && m[1]){
25875                     html = '<div style="'+m[0]+'">' + html + '</div>';
25876                 }
25877             }
25878             html = this.cleanHtml(html);
25879             // fix up the special chars.. normaly like back quotes in word...
25880             // however we do not want to do this with chinese..
25881             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25882                 
25883                 var cc = match.charCodeAt();
25884
25885                 // Get the character value, handling surrogate pairs
25886                 if (match.length == 2) {
25887                     // It's a surrogate pair, calculate the Unicode code point
25888                     var high = match.charCodeAt(0) - 0xD800;
25889                     var low  = match.charCodeAt(1) - 0xDC00;
25890                     cc = (high * 0x400) + low + 0x10000;
25891                 }  else if (
25892                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25893                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25894                     (cc >= 0xf900 && cc < 0xfb00 )
25895                 ) {
25896                         return match;
25897                 }  
25898          
25899                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25900                 return "&#" + cc + ";";
25901                 
25902                 
25903             });
25904             
25905             
25906              
25907             if(this.owner.fireEvent('beforesync', this, html) !== false){
25908                 this.el.dom.value = html;
25909                 this.owner.fireEvent('sync', this, html);
25910             }
25911         }
25912     },
25913
25914     /**
25915      * Protected method that will not generally be called directly. Pushes the value of the textarea
25916      * into the iframe editor.
25917      */
25918     pushValue : function(){
25919         if(this.initialized){
25920             var v = this.el.dom.value.trim();
25921             
25922 //            if(v.length < 1){
25923 //                v = '&#160;';
25924 //            }
25925             
25926             if(this.owner.fireEvent('beforepush', this, v) !== false){
25927                 var d = (this.doc.body || this.doc.documentElement);
25928                 d.innerHTML = v;
25929                 this.cleanUpPaste();
25930                 this.el.dom.value = d.innerHTML;
25931                 this.owner.fireEvent('push', this, v);
25932             }
25933         }
25934     },
25935
25936     // private
25937     deferFocus : function(){
25938         this.focus.defer(10, this);
25939     },
25940
25941     // doc'ed in Field
25942     focus : function(){
25943         if(this.win && !this.sourceEditMode){
25944             this.win.focus();
25945         }else{
25946             this.el.focus();
25947         }
25948     },
25949     
25950     assignDocWin: function()
25951     {
25952         var iframe = this.iframe;
25953         
25954          if(Roo.isIE){
25955             this.doc = iframe.contentWindow.document;
25956             this.win = iframe.contentWindow;
25957         } else {
25958 //            if (!Roo.get(this.frameId)) {
25959 //                return;
25960 //            }
25961 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25962 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25963             
25964             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25965                 return;
25966             }
25967             
25968             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25969             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25970         }
25971     },
25972     
25973     // private
25974     initEditor : function(){
25975         //console.log("INIT EDITOR");
25976         this.assignDocWin();
25977         
25978         
25979         
25980         this.doc.designMode="on";
25981         this.doc.open();
25982         this.doc.write(this.getDocMarkup());
25983         this.doc.close();
25984         
25985         var dbody = (this.doc.body || this.doc.documentElement);
25986         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25987         // this copies styles from the containing element into thsi one..
25988         // not sure why we need all of this..
25989         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25990         
25991         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25992         //ss['background-attachment'] = 'fixed'; // w3c
25993         dbody.bgProperties = 'fixed'; // ie
25994         //Roo.DomHelper.applyStyles(dbody, ss);
25995         Roo.EventManager.on(this.doc, {
25996             //'mousedown': this.onEditorEvent,
25997             'mouseup': this.onEditorEvent,
25998             'dblclick': this.onEditorEvent,
25999             'click': this.onEditorEvent,
26000             'keyup': this.onEditorEvent,
26001             buffer:100,
26002             scope: this
26003         });
26004         if(Roo.isGecko){
26005             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26006         }
26007         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26008             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26009         }
26010         this.initialized = true;
26011
26012         this.owner.fireEvent('initialize', this);
26013         this.pushValue();
26014     },
26015
26016     // private
26017     onDestroy : function(){
26018         
26019         
26020         
26021         if(this.rendered){
26022             
26023             //for (var i =0; i < this.toolbars.length;i++) {
26024             //    // fixme - ask toolbars for heights?
26025             //    this.toolbars[i].onDestroy();
26026            // }
26027             
26028             //this.wrap.dom.innerHTML = '';
26029             //this.wrap.remove();
26030         }
26031     },
26032
26033     // private
26034     onFirstFocus : function(){
26035         
26036         this.assignDocWin();
26037         
26038         
26039         this.activated = true;
26040          
26041     
26042         if(Roo.isGecko){ // prevent silly gecko errors
26043             this.win.focus();
26044             var s = this.win.getSelection();
26045             if(!s.focusNode || s.focusNode.nodeType != 3){
26046                 var r = s.getRangeAt(0);
26047                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26048                 r.collapse(true);
26049                 this.deferFocus();
26050             }
26051             try{
26052                 this.execCmd('useCSS', true);
26053                 this.execCmd('styleWithCSS', false);
26054             }catch(e){}
26055         }
26056         this.owner.fireEvent('activate', this);
26057     },
26058
26059     // private
26060     adjustFont: function(btn){
26061         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26062         //if(Roo.isSafari){ // safari
26063         //    adjust *= 2;
26064        // }
26065         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26066         if(Roo.isSafari){ // safari
26067             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26068             v =  (v < 10) ? 10 : v;
26069             v =  (v > 48) ? 48 : v;
26070             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26071             
26072         }
26073         
26074         
26075         v = Math.max(1, v+adjust);
26076         
26077         this.execCmd('FontSize', v  );
26078     },
26079
26080     onEditorEvent : function(e)
26081     {
26082         this.owner.fireEvent('editorevent', this, e);
26083       //  this.updateToolbar();
26084         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26085     },
26086
26087     insertTag : function(tg)
26088     {
26089         // could be a bit smarter... -> wrap the current selected tRoo..
26090         if (tg.toLowerCase() == 'span' ||
26091             tg.toLowerCase() == 'code' ||
26092             tg.toLowerCase() == 'sup' ||
26093             tg.toLowerCase() == 'sub' 
26094             ) {
26095             
26096             range = this.createRange(this.getSelection());
26097             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26098             wrappingNode.appendChild(range.extractContents());
26099             range.insertNode(wrappingNode);
26100
26101             return;
26102             
26103             
26104             
26105         }
26106         this.execCmd("formatblock",   tg);
26107         
26108     },
26109     
26110     insertText : function(txt)
26111     {
26112         
26113         
26114         var range = this.createRange();
26115         range.deleteContents();
26116                //alert(Sender.getAttribute('label'));
26117                
26118         range.insertNode(this.doc.createTextNode(txt));
26119     } ,
26120     
26121      
26122
26123     /**
26124      * Executes a Midas editor command on the editor document and performs necessary focus and
26125      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26126      * @param {String} cmd The Midas command
26127      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26128      */
26129     relayCmd : function(cmd, value){
26130         this.win.focus();
26131         this.execCmd(cmd, value);
26132         this.owner.fireEvent('editorevent', this);
26133         //this.updateToolbar();
26134         this.owner.deferFocus();
26135     },
26136
26137     /**
26138      * Executes a Midas editor command directly on the editor document.
26139      * For visual commands, you should use {@link #relayCmd} instead.
26140      * <b>This should only be called after the editor is initialized.</b>
26141      * @param {String} cmd The Midas command
26142      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26143      */
26144     execCmd : function(cmd, value){
26145         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26146         this.syncValue();
26147     },
26148  
26149  
26150    
26151     /**
26152      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26153      * to insert tRoo.
26154      * @param {String} text | dom node.. 
26155      */
26156     insertAtCursor : function(text)
26157     {
26158         
26159         if(!this.activated){
26160             return;
26161         }
26162         /*
26163         if(Roo.isIE){
26164             this.win.focus();
26165             var r = this.doc.selection.createRange();
26166             if(r){
26167                 r.collapse(true);
26168                 r.pasteHTML(text);
26169                 this.syncValue();
26170                 this.deferFocus();
26171             
26172             }
26173             return;
26174         }
26175         */
26176         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26177             this.win.focus();
26178             
26179             
26180             // from jquery ui (MIT licenced)
26181             var range, node;
26182             var win = this.win;
26183             
26184             if (win.getSelection && win.getSelection().getRangeAt) {
26185                 range = win.getSelection().getRangeAt(0);
26186                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26187                 range.insertNode(node);
26188             } else if (win.document.selection && win.document.selection.createRange) {
26189                 // no firefox support
26190                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26191                 win.document.selection.createRange().pasteHTML(txt);
26192             } else {
26193                 // no firefox support
26194                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26195                 this.execCmd('InsertHTML', txt);
26196             } 
26197             
26198             this.syncValue();
26199             
26200             this.deferFocus();
26201         }
26202     },
26203  // private
26204     mozKeyPress : function(e){
26205         if(e.ctrlKey){
26206             var c = e.getCharCode(), cmd;
26207           
26208             if(c > 0){
26209                 c = String.fromCharCode(c).toLowerCase();
26210                 switch(c){
26211                     case 'b':
26212                         cmd = 'bold';
26213                         break;
26214                     case 'i':
26215                         cmd = 'italic';
26216                         break;
26217                     
26218                     case 'u':
26219                         cmd = 'underline';
26220                         break;
26221                     
26222                     case 'v':
26223                         this.cleanUpPaste.defer(100, this);
26224                         return;
26225                         
26226                 }
26227                 if(cmd){
26228                     this.win.focus();
26229                     this.execCmd(cmd);
26230                     this.deferFocus();
26231                     e.preventDefault();
26232                 }
26233                 
26234             }
26235         }
26236     },
26237
26238     // private
26239     fixKeys : function(){ // load time branching for fastest keydown performance
26240         if(Roo.isIE){
26241             return function(e){
26242                 var k = e.getKey(), r;
26243                 if(k == e.TAB){
26244                     e.stopEvent();
26245                     r = this.doc.selection.createRange();
26246                     if(r){
26247                         r.collapse(true);
26248                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26249                         this.deferFocus();
26250                     }
26251                     return;
26252                 }
26253                 
26254                 if(k == e.ENTER){
26255                     r = this.doc.selection.createRange();
26256                     if(r){
26257                         var target = r.parentElement();
26258                         if(!target || target.tagName.toLowerCase() != 'li'){
26259                             e.stopEvent();
26260                             r.pasteHTML('<br />');
26261                             r.collapse(false);
26262                             r.select();
26263                         }
26264                     }
26265                 }
26266                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26267                     this.cleanUpPaste.defer(100, this);
26268                     return;
26269                 }
26270                 
26271                 
26272             };
26273         }else if(Roo.isOpera){
26274             return function(e){
26275                 var k = e.getKey();
26276                 if(k == e.TAB){
26277                     e.stopEvent();
26278                     this.win.focus();
26279                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26280                     this.deferFocus();
26281                 }
26282                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26283                     this.cleanUpPaste.defer(100, this);
26284                     return;
26285                 }
26286                 
26287             };
26288         }else if(Roo.isSafari){
26289             return function(e){
26290                 var k = e.getKey();
26291                 
26292                 if(k == e.TAB){
26293                     e.stopEvent();
26294                     this.execCmd('InsertText','\t');
26295                     this.deferFocus();
26296                     return;
26297                 }
26298                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26299                     this.cleanUpPaste.defer(100, this);
26300                     return;
26301                 }
26302                 
26303              };
26304         }
26305     }(),
26306     
26307     getAllAncestors: function()
26308     {
26309         var p = this.getSelectedNode();
26310         var a = [];
26311         if (!p) {
26312             a.push(p); // push blank onto stack..
26313             p = this.getParentElement();
26314         }
26315         
26316         
26317         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26318             a.push(p);
26319             p = p.parentNode;
26320         }
26321         a.push(this.doc.body);
26322         return a;
26323     },
26324     lastSel : false,
26325     lastSelNode : false,
26326     
26327     
26328     getSelection : function() 
26329     {
26330         this.assignDocWin();
26331         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26332     },
26333     
26334     getSelectedNode: function() 
26335     {
26336         // this may only work on Gecko!!!
26337         
26338         // should we cache this!!!!
26339         
26340         
26341         
26342          
26343         var range = this.createRange(this.getSelection()).cloneRange();
26344         
26345         if (Roo.isIE) {
26346             var parent = range.parentElement();
26347             while (true) {
26348                 var testRange = range.duplicate();
26349                 testRange.moveToElementText(parent);
26350                 if (testRange.inRange(range)) {
26351                     break;
26352                 }
26353                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26354                     break;
26355                 }
26356                 parent = parent.parentElement;
26357             }
26358             return parent;
26359         }
26360         
26361         // is ancestor a text element.
26362         var ac =  range.commonAncestorContainer;
26363         if (ac.nodeType == 3) {
26364             ac = ac.parentNode;
26365         }
26366         
26367         var ar = ac.childNodes;
26368          
26369         var nodes = [];
26370         var other_nodes = [];
26371         var has_other_nodes = false;
26372         for (var i=0;i<ar.length;i++) {
26373             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26374                 continue;
26375             }
26376             // fullly contained node.
26377             
26378             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26379                 nodes.push(ar[i]);
26380                 continue;
26381             }
26382             
26383             // probably selected..
26384             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26385                 other_nodes.push(ar[i]);
26386                 continue;
26387             }
26388             // outer..
26389             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26390                 continue;
26391             }
26392             
26393             
26394             has_other_nodes = true;
26395         }
26396         if (!nodes.length && other_nodes.length) {
26397             nodes= other_nodes;
26398         }
26399         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26400             return false;
26401         }
26402         
26403         return nodes[0];
26404     },
26405     createRange: function(sel)
26406     {
26407         // this has strange effects when using with 
26408         // top toolbar - not sure if it's a great idea.
26409         //this.editor.contentWindow.focus();
26410         if (typeof sel != "undefined") {
26411             try {
26412                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26413             } catch(e) {
26414                 return this.doc.createRange();
26415             }
26416         } else {
26417             return this.doc.createRange();
26418         }
26419     },
26420     getParentElement: function()
26421     {
26422         
26423         this.assignDocWin();
26424         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26425         
26426         var range = this.createRange(sel);
26427          
26428         try {
26429             var p = range.commonAncestorContainer;
26430             while (p.nodeType == 3) { // text node
26431                 p = p.parentNode;
26432             }
26433             return p;
26434         } catch (e) {
26435             return null;
26436         }
26437     
26438     },
26439     /***
26440      *
26441      * Range intersection.. the hard stuff...
26442      *  '-1' = before
26443      *  '0' = hits..
26444      *  '1' = after.
26445      *         [ -- selected range --- ]
26446      *   [fail]                        [fail]
26447      *
26448      *    basically..
26449      *      if end is before start or  hits it. fail.
26450      *      if start is after end or hits it fail.
26451      *
26452      *   if either hits (but other is outside. - then it's not 
26453      *   
26454      *    
26455      **/
26456     
26457     
26458     // @see http://www.thismuchiknow.co.uk/?p=64.
26459     rangeIntersectsNode : function(range, node)
26460     {
26461         var nodeRange = node.ownerDocument.createRange();
26462         try {
26463             nodeRange.selectNode(node);
26464         } catch (e) {
26465             nodeRange.selectNodeContents(node);
26466         }
26467     
26468         var rangeStartRange = range.cloneRange();
26469         rangeStartRange.collapse(true);
26470     
26471         var rangeEndRange = range.cloneRange();
26472         rangeEndRange.collapse(false);
26473     
26474         var nodeStartRange = nodeRange.cloneRange();
26475         nodeStartRange.collapse(true);
26476     
26477         var nodeEndRange = nodeRange.cloneRange();
26478         nodeEndRange.collapse(false);
26479     
26480         return rangeStartRange.compareBoundaryPoints(
26481                  Range.START_TO_START, nodeEndRange) == -1 &&
26482                rangeEndRange.compareBoundaryPoints(
26483                  Range.START_TO_START, nodeStartRange) == 1;
26484         
26485          
26486     },
26487     rangeCompareNode : function(range, node)
26488     {
26489         var nodeRange = node.ownerDocument.createRange();
26490         try {
26491             nodeRange.selectNode(node);
26492         } catch (e) {
26493             nodeRange.selectNodeContents(node);
26494         }
26495         
26496         
26497         range.collapse(true);
26498     
26499         nodeRange.collapse(true);
26500      
26501         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26502         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26503          
26504         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26505         
26506         var nodeIsBefore   =  ss == 1;
26507         var nodeIsAfter    = ee == -1;
26508         
26509         if (nodeIsBefore && nodeIsAfter) {
26510             return 0; // outer
26511         }
26512         if (!nodeIsBefore && nodeIsAfter) {
26513             return 1; //right trailed.
26514         }
26515         
26516         if (nodeIsBefore && !nodeIsAfter) {
26517             return 2;  // left trailed.
26518         }
26519         // fully contined.
26520         return 3;
26521     },
26522
26523     // private? - in a new class?
26524     cleanUpPaste :  function()
26525     {
26526         // cleans up the whole document..
26527         Roo.log('cleanuppaste');
26528         
26529         this.cleanUpChildren(this.doc.body);
26530         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26531         if (clean != this.doc.body.innerHTML) {
26532             this.doc.body.innerHTML = clean;
26533         }
26534         
26535     },
26536     
26537     cleanWordChars : function(input) {// change the chars to hex code
26538         var he = Roo.HtmlEditorCore;
26539         
26540         var output = input;
26541         Roo.each(he.swapCodes, function(sw) { 
26542             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26543             
26544             output = output.replace(swapper, sw[1]);
26545         });
26546         
26547         return output;
26548     },
26549     
26550     
26551     cleanUpChildren : function (n)
26552     {
26553         if (!n.childNodes.length) {
26554             return;
26555         }
26556         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26557            this.cleanUpChild(n.childNodes[i]);
26558         }
26559     },
26560     
26561     
26562         
26563     
26564     cleanUpChild : function (node)
26565     {
26566         var ed = this;
26567         //console.log(node);
26568         if (node.nodeName == "#text") {
26569             // clean up silly Windows -- stuff?
26570             return; 
26571         }
26572         if (node.nodeName == "#comment") {
26573             if (!this.allowComments) {
26574                 node.parentNode.removeChild(node);
26575             }
26576             // clean up silly Windows -- stuff?
26577             return; 
26578         }
26579         var lcname = node.tagName.toLowerCase();
26580         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26581         // whitelist of tags..
26582         
26583         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26584             // remove node.
26585             node.parentNode.removeChild(node);
26586             return;
26587             
26588         }
26589         
26590         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26591         
26592         // spans with no attributes - just remove them..
26593         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26594             remove_keep_children = true;
26595         }
26596         
26597         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26598         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26599         
26600         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26601         //    remove_keep_children = true;
26602         //}
26603         
26604         if (remove_keep_children) {
26605             this.cleanUpChildren(node);
26606             // inserts everything just before this node...
26607             while (node.childNodes.length) {
26608                 var cn = node.childNodes[0];
26609                 node.removeChild(cn);
26610                 node.parentNode.insertBefore(cn, node);
26611             }
26612             node.parentNode.removeChild(node);
26613             return;
26614         }
26615         
26616         if (!node.attributes || !node.attributes.length) {
26617             
26618           
26619             
26620             
26621             this.cleanUpChildren(node);
26622             return;
26623         }
26624         
26625         function cleanAttr(n,v)
26626         {
26627             
26628             if (v.match(/^\./) || v.match(/^\//)) {
26629                 return;
26630             }
26631             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26632                 return;
26633             }
26634             if (v.match(/^#/)) {
26635                 return;
26636             }
26637             if (v.match(/^\{/)) { // allow template editing.
26638                 return;
26639             }
26640 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26641             node.removeAttribute(n);
26642             
26643         }
26644         
26645         var cwhite = this.cwhite;
26646         var cblack = this.cblack;
26647             
26648         function cleanStyle(n,v)
26649         {
26650             if (v.match(/expression/)) { //XSS?? should we even bother..
26651                 node.removeAttribute(n);
26652                 return;
26653             }
26654             
26655             var parts = v.split(/;/);
26656             var clean = [];
26657             
26658             Roo.each(parts, function(p) {
26659                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26660                 if (!p.length) {
26661                     return true;
26662                 }
26663                 var l = p.split(':').shift().replace(/\s+/g,'');
26664                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26665                 
26666                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26667 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26668                     //node.removeAttribute(n);
26669                     return true;
26670                 }
26671                 //Roo.log()
26672                 // only allow 'c whitelisted system attributes'
26673                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26674 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26675                     //node.removeAttribute(n);
26676                     return true;
26677                 }
26678                 
26679                 
26680                  
26681                 
26682                 clean.push(p);
26683                 return true;
26684             });
26685             if (clean.length) { 
26686                 node.setAttribute(n, clean.join(';'));
26687             } else {
26688                 node.removeAttribute(n);
26689             }
26690             
26691         }
26692         
26693         
26694         for (var i = node.attributes.length-1; i > -1 ; i--) {
26695             var a = node.attributes[i];
26696             //console.log(a);
26697             
26698             if (a.name.toLowerCase().substr(0,2)=='on')  {
26699                 node.removeAttribute(a.name);
26700                 continue;
26701             }
26702             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26703                 node.removeAttribute(a.name);
26704                 continue;
26705             }
26706             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26707                 cleanAttr(a.name,a.value); // fixme..
26708                 continue;
26709             }
26710             if (a.name == 'style') {
26711                 cleanStyle(a.name,a.value);
26712                 continue;
26713             }
26714             /// clean up MS crap..
26715             // tecnically this should be a list of valid class'es..
26716             
26717             
26718             if (a.name == 'class') {
26719                 if (a.value.match(/^Mso/)) {
26720                     node.removeAttribute('class');
26721                 }
26722                 
26723                 if (a.value.match(/^body$/)) {
26724                     node.removeAttribute('class');
26725                 }
26726                 continue;
26727             }
26728             
26729             // style cleanup!?
26730             // class cleanup?
26731             
26732         }
26733         
26734         
26735         this.cleanUpChildren(node);
26736         
26737         
26738     },
26739     
26740     /**
26741      * Clean up MS wordisms...
26742      */
26743     cleanWord : function(node)
26744     {
26745         if (!node) {
26746             this.cleanWord(this.doc.body);
26747             return;
26748         }
26749         
26750         if(
26751                 node.nodeName == 'SPAN' &&
26752                 !node.hasAttributes() &&
26753                 node.childNodes.length == 1 &&
26754                 node.firstChild.nodeName == "#text"  
26755         ) {
26756             var textNode = node.firstChild;
26757             node.removeChild(textNode);
26758             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26759                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26760             }
26761             node.parentNode.insertBefore(textNode, node);
26762             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26763                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26764             }
26765             node.parentNode.removeChild(node);
26766         }
26767         
26768         if (node.nodeName == "#text") {
26769             // clean up silly Windows -- stuff?
26770             return; 
26771         }
26772         if (node.nodeName == "#comment") {
26773             node.parentNode.removeChild(node);
26774             // clean up silly Windows -- stuff?
26775             return; 
26776         }
26777         
26778         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26779             node.parentNode.removeChild(node);
26780             return;
26781         }
26782         //Roo.log(node.tagName);
26783         // remove - but keep children..
26784         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26785             //Roo.log('-- removed');
26786             while (node.childNodes.length) {
26787                 var cn = node.childNodes[0];
26788                 node.removeChild(cn);
26789                 node.parentNode.insertBefore(cn, node);
26790                 // move node to parent - and clean it..
26791                 this.cleanWord(cn);
26792             }
26793             node.parentNode.removeChild(node);
26794             /// no need to iterate chidlren = it's got none..
26795             //this.iterateChildren(node, this.cleanWord);
26796             return;
26797         }
26798         // clean styles
26799         if (node.className.length) {
26800             
26801             var cn = node.className.split(/\W+/);
26802             var cna = [];
26803             Roo.each(cn, function(cls) {
26804                 if (cls.match(/Mso[a-zA-Z]+/)) {
26805                     return;
26806                 }
26807                 cna.push(cls);
26808             });
26809             node.className = cna.length ? cna.join(' ') : '';
26810             if (!cna.length) {
26811                 node.removeAttribute("class");
26812             }
26813         }
26814         
26815         if (node.hasAttribute("lang")) {
26816             node.removeAttribute("lang");
26817         }
26818         
26819         if (node.hasAttribute("style")) {
26820             
26821             var styles = node.getAttribute("style").split(";");
26822             var nstyle = [];
26823             Roo.each(styles, function(s) {
26824                 if (!s.match(/:/)) {
26825                     return;
26826                 }
26827                 var kv = s.split(":");
26828                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26829                     return;
26830                 }
26831                 // what ever is left... we allow.
26832                 nstyle.push(s);
26833             });
26834             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26835             if (!nstyle.length) {
26836                 node.removeAttribute('style');
26837             }
26838         }
26839         this.iterateChildren(node, this.cleanWord);
26840         
26841         
26842         
26843     },
26844     /**
26845      * iterateChildren of a Node, calling fn each time, using this as the scole..
26846      * @param {DomNode} node node to iterate children of.
26847      * @param {Function} fn method of this class to call on each item.
26848      */
26849     iterateChildren : function(node, fn)
26850     {
26851         if (!node.childNodes.length) {
26852                 return;
26853         }
26854         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26855            fn.call(this, node.childNodes[i])
26856         }
26857     },
26858     
26859     
26860     /**
26861      * cleanTableWidths.
26862      *
26863      * Quite often pasting from word etc.. results in tables with column and widths.
26864      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26865      *
26866      */
26867     cleanTableWidths : function(node)
26868     {
26869          
26870          
26871         if (!node) {
26872             this.cleanTableWidths(this.doc.body);
26873             return;
26874         }
26875         
26876         // ignore list...
26877         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26878             return; 
26879         }
26880         Roo.log(node.tagName);
26881         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26882             this.iterateChildren(node, this.cleanTableWidths);
26883             return;
26884         }
26885         if (node.hasAttribute('width')) {
26886             node.removeAttribute('width');
26887         }
26888         
26889          
26890         if (node.hasAttribute("style")) {
26891             // pretty basic...
26892             
26893             var styles = node.getAttribute("style").split(";");
26894             var nstyle = [];
26895             Roo.each(styles, function(s) {
26896                 if (!s.match(/:/)) {
26897                     return;
26898                 }
26899                 var kv = s.split(":");
26900                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26901                     return;
26902                 }
26903                 // what ever is left... we allow.
26904                 nstyle.push(s);
26905             });
26906             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26907             if (!nstyle.length) {
26908                 node.removeAttribute('style');
26909             }
26910         }
26911         
26912         this.iterateChildren(node, this.cleanTableWidths);
26913         
26914         
26915     },
26916     
26917     
26918     
26919     
26920     domToHTML : function(currentElement, depth, nopadtext) {
26921         
26922         depth = depth || 0;
26923         nopadtext = nopadtext || false;
26924     
26925         if (!currentElement) {
26926             return this.domToHTML(this.doc.body);
26927         }
26928         
26929         //Roo.log(currentElement);
26930         var j;
26931         var allText = false;
26932         var nodeName = currentElement.nodeName;
26933         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26934         
26935         if  (nodeName == '#text') {
26936             
26937             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26938         }
26939         
26940         
26941         var ret = '';
26942         if (nodeName != 'BODY') {
26943              
26944             var i = 0;
26945             // Prints the node tagName, such as <A>, <IMG>, etc
26946             if (tagName) {
26947                 var attr = [];
26948                 for(i = 0; i < currentElement.attributes.length;i++) {
26949                     // quoting?
26950                     var aname = currentElement.attributes.item(i).name;
26951                     if (!currentElement.attributes.item(i).value.length) {
26952                         continue;
26953                     }
26954                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26955                 }
26956                 
26957                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26958             } 
26959             else {
26960                 
26961                 // eack
26962             }
26963         } else {
26964             tagName = false;
26965         }
26966         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26967             return ret;
26968         }
26969         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26970             nopadtext = true;
26971         }
26972         
26973         
26974         // Traverse the tree
26975         i = 0;
26976         var currentElementChild = currentElement.childNodes.item(i);
26977         var allText = true;
26978         var innerHTML  = '';
26979         lastnode = '';
26980         while (currentElementChild) {
26981             // Formatting code (indent the tree so it looks nice on the screen)
26982             var nopad = nopadtext;
26983             if (lastnode == 'SPAN') {
26984                 nopad  = true;
26985             }
26986             // text
26987             if  (currentElementChild.nodeName == '#text') {
26988                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26989                 toadd = nopadtext ? toadd : toadd.trim();
26990                 if (!nopad && toadd.length > 80) {
26991                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26992                 }
26993                 innerHTML  += toadd;
26994                 
26995                 i++;
26996                 currentElementChild = currentElement.childNodes.item(i);
26997                 lastNode = '';
26998                 continue;
26999             }
27000             allText = false;
27001             
27002             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27003                 
27004             // Recursively traverse the tree structure of the child node
27005             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27006             lastnode = currentElementChild.nodeName;
27007             i++;
27008             currentElementChild=currentElement.childNodes.item(i);
27009         }
27010         
27011         ret += innerHTML;
27012         
27013         if (!allText) {
27014                 // The remaining code is mostly for formatting the tree
27015             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27016         }
27017         
27018         
27019         if (tagName) {
27020             ret+= "</"+tagName+">";
27021         }
27022         return ret;
27023         
27024     },
27025         
27026     applyBlacklists : function()
27027     {
27028         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27029         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27030         
27031         this.white = [];
27032         this.black = [];
27033         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27034             if (b.indexOf(tag) > -1) {
27035                 return;
27036             }
27037             this.white.push(tag);
27038             
27039         }, this);
27040         
27041         Roo.each(w, function(tag) {
27042             if (b.indexOf(tag) > -1) {
27043                 return;
27044             }
27045             if (this.white.indexOf(tag) > -1) {
27046                 return;
27047             }
27048             this.white.push(tag);
27049             
27050         }, this);
27051         
27052         
27053         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27054             if (w.indexOf(tag) > -1) {
27055                 return;
27056             }
27057             this.black.push(tag);
27058             
27059         }, this);
27060         
27061         Roo.each(b, function(tag) {
27062             if (w.indexOf(tag) > -1) {
27063                 return;
27064             }
27065             if (this.black.indexOf(tag) > -1) {
27066                 return;
27067             }
27068             this.black.push(tag);
27069             
27070         }, this);
27071         
27072         
27073         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27074         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27075         
27076         this.cwhite = [];
27077         this.cblack = [];
27078         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27079             if (b.indexOf(tag) > -1) {
27080                 return;
27081             }
27082             this.cwhite.push(tag);
27083             
27084         }, this);
27085         
27086         Roo.each(w, function(tag) {
27087             if (b.indexOf(tag) > -1) {
27088                 return;
27089             }
27090             if (this.cwhite.indexOf(tag) > -1) {
27091                 return;
27092             }
27093             this.cwhite.push(tag);
27094             
27095         }, this);
27096         
27097         
27098         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27099             if (w.indexOf(tag) > -1) {
27100                 return;
27101             }
27102             this.cblack.push(tag);
27103             
27104         }, this);
27105         
27106         Roo.each(b, function(tag) {
27107             if (w.indexOf(tag) > -1) {
27108                 return;
27109             }
27110             if (this.cblack.indexOf(tag) > -1) {
27111                 return;
27112             }
27113             this.cblack.push(tag);
27114             
27115         }, this);
27116     },
27117     
27118     setStylesheets : function(stylesheets)
27119     {
27120         if(typeof(stylesheets) == 'string'){
27121             Roo.get(this.iframe.contentDocument.head).createChild({
27122                 tag : 'link',
27123                 rel : 'stylesheet',
27124                 type : 'text/css',
27125                 href : stylesheets
27126             });
27127             
27128             return;
27129         }
27130         var _this = this;
27131      
27132         Roo.each(stylesheets, function(s) {
27133             if(!s.length){
27134                 return;
27135             }
27136             
27137             Roo.get(_this.iframe.contentDocument.head).createChild({
27138                 tag : 'link',
27139                 rel : 'stylesheet',
27140                 type : 'text/css',
27141                 href : s
27142             });
27143         });
27144
27145         
27146     },
27147     
27148     removeStylesheets : function()
27149     {
27150         var _this = this;
27151         
27152         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27153             s.remove();
27154         });
27155     },
27156     
27157     setStyle : function(style)
27158     {
27159         Roo.get(this.iframe.contentDocument.head).createChild({
27160             tag : 'style',
27161             type : 'text/css',
27162             html : style
27163         });
27164
27165         return;
27166     }
27167     
27168     // hide stuff that is not compatible
27169     /**
27170      * @event blur
27171      * @hide
27172      */
27173     /**
27174      * @event change
27175      * @hide
27176      */
27177     /**
27178      * @event focus
27179      * @hide
27180      */
27181     /**
27182      * @event specialkey
27183      * @hide
27184      */
27185     /**
27186      * @cfg {String} fieldClass @hide
27187      */
27188     /**
27189      * @cfg {String} focusClass @hide
27190      */
27191     /**
27192      * @cfg {String} autoCreate @hide
27193      */
27194     /**
27195      * @cfg {String} inputType @hide
27196      */
27197     /**
27198      * @cfg {String} invalidClass @hide
27199      */
27200     /**
27201      * @cfg {String} invalidText @hide
27202      */
27203     /**
27204      * @cfg {String} msgFx @hide
27205      */
27206     /**
27207      * @cfg {String} validateOnBlur @hide
27208      */
27209 });
27210
27211 Roo.HtmlEditorCore.white = [
27212         'area', 'br', 'img', 'input', 'hr', 'wbr',
27213         
27214        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27215        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27216        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27217        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27218        'table',   'ul',         'xmp', 
27219        
27220        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27221       'thead',   'tr', 
27222      
27223       'dir', 'menu', 'ol', 'ul', 'dl',
27224        
27225       'embed',  'object'
27226 ];
27227
27228
27229 Roo.HtmlEditorCore.black = [
27230     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27231         'applet', // 
27232         'base',   'basefont', 'bgsound', 'blink',  'body', 
27233         'frame',  'frameset', 'head',    'html',   'ilayer', 
27234         'iframe', 'layer',  'link',     'meta',    'object',   
27235         'script', 'style' ,'title',  'xml' // clean later..
27236 ];
27237 Roo.HtmlEditorCore.clean = [
27238     'script', 'style', 'title', 'xml'
27239 ];
27240 Roo.HtmlEditorCore.remove = [
27241     'font'
27242 ];
27243 // attributes..
27244
27245 Roo.HtmlEditorCore.ablack = [
27246     'on'
27247 ];
27248     
27249 Roo.HtmlEditorCore.aclean = [ 
27250     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27251 ];
27252
27253 // protocols..
27254 Roo.HtmlEditorCore.pwhite= [
27255         'http',  'https',  'mailto'
27256 ];
27257
27258 // white listed style attributes.
27259 Roo.HtmlEditorCore.cwhite= [
27260       //  'text-align', /// default is to allow most things..
27261       
27262          
27263 //        'font-size'//??
27264 ];
27265
27266 // black listed style attributes.
27267 Roo.HtmlEditorCore.cblack= [
27268       //  'font-size' -- this can be set by the project 
27269 ];
27270
27271
27272 Roo.HtmlEditorCore.swapCodes   =[ 
27273     [    8211, "&#8211;" ], 
27274     [    8212, "&#8212;" ], 
27275     [    8216,  "'" ],  
27276     [    8217, "'" ],  
27277     [    8220, '"' ],  
27278     [    8221, '"' ],  
27279     [    8226, "*" ],  
27280     [    8230, "..." ]
27281 ]; 
27282
27283     /*
27284  * - LGPL
27285  *
27286  * HtmlEditor
27287  * 
27288  */
27289
27290 /**
27291  * @class Roo.bootstrap.HtmlEditor
27292  * @extends Roo.bootstrap.TextArea
27293  * Bootstrap HtmlEditor class
27294
27295  * @constructor
27296  * Create a new HtmlEditor
27297  * @param {Object} config The config object
27298  */
27299
27300 Roo.bootstrap.HtmlEditor = function(config){
27301     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27302     if (!this.toolbars) {
27303         this.toolbars = [];
27304     }
27305     
27306     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27307     this.addEvents({
27308             /**
27309              * @event initialize
27310              * Fires when the editor is fully initialized (including the iframe)
27311              * @param {HtmlEditor} this
27312              */
27313             initialize: true,
27314             /**
27315              * @event activate
27316              * Fires when the editor is first receives the focus. Any insertion must wait
27317              * until after this event.
27318              * @param {HtmlEditor} this
27319              */
27320             activate: true,
27321              /**
27322              * @event beforesync
27323              * Fires before the textarea is updated with content from the editor iframe. Return false
27324              * to cancel the sync.
27325              * @param {HtmlEditor} this
27326              * @param {String} html
27327              */
27328             beforesync: true,
27329              /**
27330              * @event beforepush
27331              * Fires before the iframe editor is updated with content from the textarea. Return false
27332              * to cancel the push.
27333              * @param {HtmlEditor} this
27334              * @param {String} html
27335              */
27336             beforepush: true,
27337              /**
27338              * @event sync
27339              * Fires when the textarea is updated with content from the editor iframe.
27340              * @param {HtmlEditor} this
27341              * @param {String} html
27342              */
27343             sync: true,
27344              /**
27345              * @event push
27346              * Fires when the iframe editor is updated with content from the textarea.
27347              * @param {HtmlEditor} this
27348              * @param {String} html
27349              */
27350             push: true,
27351              /**
27352              * @event editmodechange
27353              * Fires when the editor switches edit modes
27354              * @param {HtmlEditor} this
27355              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27356              */
27357             editmodechange: true,
27358             /**
27359              * @event editorevent
27360              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27361              * @param {HtmlEditor} this
27362              */
27363             editorevent: true,
27364             /**
27365              * @event firstfocus
27366              * Fires when on first focus - needed by toolbars..
27367              * @param {HtmlEditor} this
27368              */
27369             firstfocus: true,
27370             /**
27371              * @event autosave
27372              * Auto save the htmlEditor value as a file into Events
27373              * @param {HtmlEditor} this
27374              */
27375             autosave: true,
27376             /**
27377              * @event savedpreview
27378              * preview the saved version of htmlEditor
27379              * @param {HtmlEditor} this
27380              */
27381             savedpreview: true
27382         });
27383 };
27384
27385
27386 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27387     
27388     
27389       /**
27390      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27391      */
27392     toolbars : false,
27393     
27394      /**
27395     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27396     */
27397     btns : [],
27398    
27399      /**
27400      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27401      *                        Roo.resizable.
27402      */
27403     resizable : false,
27404      /**
27405      * @cfg {Number} height (in pixels)
27406      */   
27407     height: 300,
27408    /**
27409      * @cfg {Number} width (in pixels)
27410      */   
27411     width: false,
27412     
27413     /**
27414      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27415      * 
27416      */
27417     stylesheets: false,
27418     
27419     // id of frame..
27420     frameId: false,
27421     
27422     // private properties
27423     validationEvent : false,
27424     deferHeight: true,
27425     initialized : false,
27426     activated : false,
27427     
27428     onFocus : Roo.emptyFn,
27429     iframePad:3,
27430     hideMode:'offsets',
27431     
27432     tbContainer : false,
27433     
27434     bodyCls : '',
27435     
27436     toolbarContainer :function() {
27437         return this.wrap.select('.x-html-editor-tb',true).first();
27438     },
27439
27440     /**
27441      * Protected method that will not generally be called directly. It
27442      * is called when the editor creates its toolbar. Override this method if you need to
27443      * add custom toolbar buttons.
27444      * @param {HtmlEditor} editor
27445      */
27446     createToolbar : function(){
27447         Roo.log('renewing');
27448         Roo.log("create toolbars");
27449         
27450         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27451         this.toolbars[0].render(this.toolbarContainer());
27452         
27453         return;
27454         
27455 //        if (!editor.toolbars || !editor.toolbars.length) {
27456 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27457 //        }
27458 //        
27459 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27460 //            editor.toolbars[i] = Roo.factory(
27461 //                    typeof(editor.toolbars[i]) == 'string' ?
27462 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27463 //                Roo.bootstrap.HtmlEditor);
27464 //            editor.toolbars[i].init(editor);
27465 //        }
27466     },
27467
27468      
27469     // private
27470     onRender : function(ct, position)
27471     {
27472        // Roo.log("Call onRender: " + this.xtype);
27473         var _t = this;
27474         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27475       
27476         this.wrap = this.inputEl().wrap({
27477             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27478         });
27479         
27480         this.editorcore.onRender(ct, position);
27481          
27482         if (this.resizable) {
27483             this.resizeEl = new Roo.Resizable(this.wrap, {
27484                 pinned : true,
27485                 wrap: true,
27486                 dynamic : true,
27487                 minHeight : this.height,
27488                 height: this.height,
27489                 handles : this.resizable,
27490                 width: this.width,
27491                 listeners : {
27492                     resize : function(r, w, h) {
27493                         _t.onResize(w,h); // -something
27494                     }
27495                 }
27496             });
27497             
27498         }
27499         this.createToolbar(this);
27500        
27501         
27502         if(!this.width && this.resizable){
27503             this.setSize(this.wrap.getSize());
27504         }
27505         if (this.resizeEl) {
27506             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27507             // should trigger onReize..
27508         }
27509         
27510     },
27511
27512     // private
27513     onResize : function(w, h)
27514     {
27515         Roo.log('resize: ' +w + ',' + h );
27516         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27517         var ew = false;
27518         var eh = false;
27519         
27520         if(this.inputEl() ){
27521             if(typeof w == 'number'){
27522                 var aw = w - this.wrap.getFrameWidth('lr');
27523                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27524                 ew = aw;
27525             }
27526             if(typeof h == 'number'){
27527                  var tbh = -11;  // fixme it needs to tool bar size!
27528                 for (var i =0; i < this.toolbars.length;i++) {
27529                     // fixme - ask toolbars for heights?
27530                     tbh += this.toolbars[i].el.getHeight();
27531                     //if (this.toolbars[i].footer) {
27532                     //    tbh += this.toolbars[i].footer.el.getHeight();
27533                     //}
27534                 }
27535               
27536                 
27537                 
27538                 
27539                 
27540                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27541                 ah -= 5; // knock a few pixes off for look..
27542                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27543                 var eh = ah;
27544             }
27545         }
27546         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27547         this.editorcore.onResize(ew,eh);
27548         
27549     },
27550
27551     /**
27552      * Toggles the editor between standard and source edit mode.
27553      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27554      */
27555     toggleSourceEdit : function(sourceEditMode)
27556     {
27557         this.editorcore.toggleSourceEdit(sourceEditMode);
27558         
27559         if(this.editorcore.sourceEditMode){
27560             Roo.log('editor - showing textarea');
27561             
27562 //            Roo.log('in');
27563 //            Roo.log(this.syncValue());
27564             this.syncValue();
27565             this.inputEl().removeClass(['hide', 'x-hidden']);
27566             this.inputEl().dom.removeAttribute('tabIndex');
27567             this.inputEl().focus();
27568         }else{
27569             Roo.log('editor - hiding textarea');
27570 //            Roo.log('out')
27571 //            Roo.log(this.pushValue()); 
27572             this.pushValue();
27573             
27574             this.inputEl().addClass(['hide', 'x-hidden']);
27575             this.inputEl().dom.setAttribute('tabIndex', -1);
27576             //this.deferFocus();
27577         }
27578          
27579         if(this.resizable){
27580             this.setSize(this.wrap.getSize());
27581         }
27582         
27583         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27584     },
27585  
27586     // private (for BoxComponent)
27587     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27588
27589     // private (for BoxComponent)
27590     getResizeEl : function(){
27591         return this.wrap;
27592     },
27593
27594     // private (for BoxComponent)
27595     getPositionEl : function(){
27596         return this.wrap;
27597     },
27598
27599     // private
27600     initEvents : function(){
27601         this.originalValue = this.getValue();
27602     },
27603
27604 //    /**
27605 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27606 //     * @method
27607 //     */
27608 //    markInvalid : Roo.emptyFn,
27609 //    /**
27610 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27611 //     * @method
27612 //     */
27613 //    clearInvalid : Roo.emptyFn,
27614
27615     setValue : function(v){
27616         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27617         this.editorcore.pushValue();
27618     },
27619
27620      
27621     // private
27622     deferFocus : function(){
27623         this.focus.defer(10, this);
27624     },
27625
27626     // doc'ed in Field
27627     focus : function(){
27628         this.editorcore.focus();
27629         
27630     },
27631       
27632
27633     // private
27634     onDestroy : function(){
27635         
27636         
27637         
27638         if(this.rendered){
27639             
27640             for (var i =0; i < this.toolbars.length;i++) {
27641                 // fixme - ask toolbars for heights?
27642                 this.toolbars[i].onDestroy();
27643             }
27644             
27645             this.wrap.dom.innerHTML = '';
27646             this.wrap.remove();
27647         }
27648     },
27649
27650     // private
27651     onFirstFocus : function(){
27652         //Roo.log("onFirstFocus");
27653         this.editorcore.onFirstFocus();
27654          for (var i =0; i < this.toolbars.length;i++) {
27655             this.toolbars[i].onFirstFocus();
27656         }
27657         
27658     },
27659     
27660     // private
27661     syncValue : function()
27662     {   
27663         this.editorcore.syncValue();
27664     },
27665     
27666     pushValue : function()
27667     {   
27668         this.editorcore.pushValue();
27669     }
27670      
27671     
27672     // hide stuff that is not compatible
27673     /**
27674      * @event blur
27675      * @hide
27676      */
27677     /**
27678      * @event change
27679      * @hide
27680      */
27681     /**
27682      * @event focus
27683      * @hide
27684      */
27685     /**
27686      * @event specialkey
27687      * @hide
27688      */
27689     /**
27690      * @cfg {String} fieldClass @hide
27691      */
27692     /**
27693      * @cfg {String} focusClass @hide
27694      */
27695     /**
27696      * @cfg {String} autoCreate @hide
27697      */
27698     /**
27699      * @cfg {String} inputType @hide
27700      */
27701      
27702     /**
27703      * @cfg {String} invalidText @hide
27704      */
27705     /**
27706      * @cfg {String} msgFx @hide
27707      */
27708     /**
27709      * @cfg {String} validateOnBlur @hide
27710      */
27711 });
27712  
27713     
27714    
27715    
27716    
27717       
27718 Roo.namespace('Roo.bootstrap.htmleditor');
27719 /**
27720  * @class Roo.bootstrap.HtmlEditorToolbar1
27721  * Basic Toolbar
27722  * 
27723  * @example
27724  * Usage:
27725  *
27726  new Roo.bootstrap.HtmlEditor({
27727     ....
27728     toolbars : [
27729         new Roo.bootstrap.HtmlEditorToolbar1({
27730             disable : { fonts: 1 , format: 1, ..., ... , ...],
27731             btns : [ .... ]
27732         })
27733     }
27734      
27735  * 
27736  * @cfg {Object} disable List of elements to disable..
27737  * @cfg {Array} btns List of additional buttons.
27738  * 
27739  * 
27740  * NEEDS Extra CSS? 
27741  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27742  */
27743  
27744 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27745 {
27746     
27747     Roo.apply(this, config);
27748     
27749     // default disabled, based on 'good practice'..
27750     this.disable = this.disable || {};
27751     Roo.applyIf(this.disable, {
27752         fontSize : true,
27753         colors : true,
27754         specialElements : true
27755     });
27756     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27757     
27758     this.editor = config.editor;
27759     this.editorcore = config.editor.editorcore;
27760     
27761     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27762     
27763     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27764     // dont call parent... till later.
27765 }
27766 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27767      
27768     bar : true,
27769     
27770     editor : false,
27771     editorcore : false,
27772     
27773     
27774     formats : [
27775         "p" ,  
27776         "h1","h2","h3","h4","h5","h6", 
27777         "pre", "code", 
27778         "abbr", "acronym", "address", "cite", "samp", "var",
27779         'div','span'
27780     ],
27781     
27782     onRender : function(ct, position)
27783     {
27784        // Roo.log("Call onRender: " + this.xtype);
27785         
27786        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27787        Roo.log(this.el);
27788        this.el.dom.style.marginBottom = '0';
27789        var _this = this;
27790        var editorcore = this.editorcore;
27791        var editor= this.editor;
27792        
27793        var children = [];
27794        var btn = function(id,cmd , toggle, handler, html){
27795        
27796             var  event = toggle ? 'toggle' : 'click';
27797        
27798             var a = {
27799                 size : 'sm',
27800                 xtype: 'Button',
27801                 xns: Roo.bootstrap,
27802                 //glyphicon : id,
27803                 fa: id,
27804                 cmd : id || cmd,
27805                 enableToggle:toggle !== false,
27806                 html : html || '',
27807                 pressed : toggle ? false : null,
27808                 listeners : {}
27809             };
27810             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27811                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27812             };
27813             children.push(a);
27814             return a;
27815        }
27816        
27817     //    var cb_box = function...
27818         
27819         var style = {
27820                 xtype: 'Button',
27821                 size : 'sm',
27822                 xns: Roo.bootstrap,
27823                 fa : 'font',
27824                 //html : 'submit'
27825                 menu : {
27826                     xtype: 'Menu',
27827                     xns: Roo.bootstrap,
27828                     items:  []
27829                 }
27830         };
27831         Roo.each(this.formats, function(f) {
27832             style.menu.items.push({
27833                 xtype :'MenuItem',
27834                 xns: Roo.bootstrap,
27835                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27836                 tagname : f,
27837                 listeners : {
27838                     click : function()
27839                     {
27840                         editorcore.insertTag(this.tagname);
27841                         editor.focus();
27842                     }
27843                 }
27844                 
27845             });
27846         });
27847         children.push(style);   
27848         
27849         btn('bold',false,true);
27850         btn('italic',false,true);
27851         btn('align-left', 'justifyleft',true);
27852         btn('align-center', 'justifycenter',true);
27853         btn('align-right' , 'justifyright',true);
27854         btn('link', false, false, function(btn) {
27855             //Roo.log("create link?");
27856             var url = prompt(this.createLinkText, this.defaultLinkValue);
27857             if(url && url != 'http:/'+'/'){
27858                 this.editorcore.relayCmd('createlink', url);
27859             }
27860         }),
27861         btn('list','insertunorderedlist',true);
27862         btn('pencil', false,true, function(btn){
27863                 Roo.log(this);
27864                 this.toggleSourceEdit(btn.pressed);
27865         });
27866         
27867         if (this.editor.btns.length > 0) {
27868             for (var i = 0; i<this.editor.btns.length; i++) {
27869                 children.push(this.editor.btns[i]);
27870             }
27871         }
27872         
27873         /*
27874         var cog = {
27875                 xtype: 'Button',
27876                 size : 'sm',
27877                 xns: Roo.bootstrap,
27878                 glyphicon : 'cog',
27879                 //html : 'submit'
27880                 menu : {
27881                     xtype: 'Menu',
27882                     xns: Roo.bootstrap,
27883                     items:  []
27884                 }
27885         };
27886         
27887         cog.menu.items.push({
27888             xtype :'MenuItem',
27889             xns: Roo.bootstrap,
27890             html : Clean styles,
27891             tagname : f,
27892             listeners : {
27893                 click : function()
27894                 {
27895                     editorcore.insertTag(this.tagname);
27896                     editor.focus();
27897                 }
27898             }
27899             
27900         });
27901        */
27902         
27903          
27904        this.xtype = 'NavSimplebar';
27905         
27906         for(var i=0;i< children.length;i++) {
27907             
27908             this.buttons.add(this.addxtypeChild(children[i]));
27909             
27910         }
27911         
27912         editor.on('editorevent', this.updateToolbar, this);
27913     },
27914     onBtnClick : function(id)
27915     {
27916        this.editorcore.relayCmd(id);
27917        this.editorcore.focus();
27918     },
27919     
27920     /**
27921      * Protected method that will not generally be called directly. It triggers
27922      * a toolbar update by reading the markup state of the current selection in the editor.
27923      */
27924     updateToolbar: function(){
27925
27926         if(!this.editorcore.activated){
27927             this.editor.onFirstFocus(); // is this neeed?
27928             return;
27929         }
27930
27931         var btns = this.buttons; 
27932         var doc = this.editorcore.doc;
27933         btns.get('bold').setActive(doc.queryCommandState('bold'));
27934         btns.get('italic').setActive(doc.queryCommandState('italic'));
27935         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27936         
27937         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27938         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27939         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27940         
27941         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27942         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27943          /*
27944         
27945         var ans = this.editorcore.getAllAncestors();
27946         if (this.formatCombo) {
27947             
27948             
27949             var store = this.formatCombo.store;
27950             this.formatCombo.setValue("");
27951             for (var i =0; i < ans.length;i++) {
27952                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27953                     // select it..
27954                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27955                     break;
27956                 }
27957             }
27958         }
27959         
27960         
27961         
27962         // hides menus... - so this cant be on a menu...
27963         Roo.bootstrap.MenuMgr.hideAll();
27964         */
27965         Roo.bootstrap.MenuMgr.hideAll();
27966         //this.editorsyncValue();
27967     },
27968     onFirstFocus: function() {
27969         this.buttons.each(function(item){
27970            item.enable();
27971         });
27972     },
27973     toggleSourceEdit : function(sourceEditMode){
27974         
27975           
27976         if(sourceEditMode){
27977             Roo.log("disabling buttons");
27978            this.buttons.each( function(item){
27979                 if(item.cmd != 'pencil'){
27980                     item.disable();
27981                 }
27982             });
27983           
27984         }else{
27985             Roo.log("enabling buttons");
27986             if(this.editorcore.initialized){
27987                 this.buttons.each( function(item){
27988                     item.enable();
27989                 });
27990             }
27991             
27992         }
27993         Roo.log("calling toggole on editor");
27994         // tell the editor that it's been pressed..
27995         this.editor.toggleSourceEdit(sourceEditMode);
27996        
27997     }
27998 });
27999
28000
28001
28002
28003  
28004 /*
28005  * - LGPL
28006  */
28007
28008 /**
28009  * @class Roo.bootstrap.Markdown
28010  * @extends Roo.bootstrap.TextArea
28011  * Bootstrap Showdown editable area
28012  * @cfg {string} content
28013  * 
28014  * @constructor
28015  * Create a new Showdown
28016  */
28017
28018 Roo.bootstrap.Markdown = function(config){
28019     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
28020    
28021 };
28022
28023 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
28024     
28025     editing :false,
28026     
28027     initEvents : function()
28028     {
28029         
28030         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28031         this.markdownEl = this.el.createChild({
28032             cls : 'roo-markdown-area'
28033         });
28034         this.inputEl().addClass('d-none');
28035         if (this.getValue() == '') {
28036             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28037             
28038         } else {
28039             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28040         }
28041         this.markdownEl.on('click', this.toggleTextEdit, this);
28042         this.on('blur', this.toggleTextEdit, this);
28043         this.on('specialkey', this.resizeTextArea, this);
28044     },
28045     
28046     toggleTextEdit : function()
28047     {
28048         var sh = this.markdownEl.getHeight();
28049         this.inputEl().addClass('d-none');
28050         this.markdownEl.addClass('d-none');
28051         if (!this.editing) {
28052             // show editor?
28053             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28054             this.inputEl().removeClass('d-none');
28055             this.inputEl().focus();
28056             this.editing = true;
28057             return;
28058         }
28059         // show showdown...
28060         this.updateMarkdown();
28061         this.markdownEl.removeClass('d-none');
28062         this.editing = false;
28063         return;
28064     },
28065     updateMarkdown : function()
28066     {
28067         if (this.getValue() == '') {
28068             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28069             return;
28070         }
28071  
28072         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28073     },
28074     
28075     resizeTextArea: function () {
28076         
28077         var sh = 100;
28078         Roo.log([sh, this.getValue().split("\n").length * 30]);
28079         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28080     },
28081     setValue : function(val)
28082     {
28083         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28084         if (!this.editing) {
28085             this.updateMarkdown();
28086         }
28087         
28088     },
28089     focus : function()
28090     {
28091         if (!this.editing) {
28092             this.toggleTextEdit();
28093         }
28094         
28095     }
28096
28097
28098 });/*
28099  * Based on:
28100  * Ext JS Library 1.1.1
28101  * Copyright(c) 2006-2007, Ext JS, LLC.
28102  *
28103  * Originally Released Under LGPL - original licence link has changed is not relivant.
28104  *
28105  * Fork - LGPL
28106  * <script type="text/javascript">
28107  */
28108  
28109 /**
28110  * @class Roo.bootstrap.PagingToolbar
28111  * @extends Roo.bootstrap.NavSimplebar
28112  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28113  * @constructor
28114  * Create a new PagingToolbar
28115  * @param {Object} config The config object
28116  * @param {Roo.data.Store} store
28117  */
28118 Roo.bootstrap.PagingToolbar = function(config)
28119 {
28120     // old args format still supported... - xtype is prefered..
28121         // created from xtype...
28122     
28123     this.ds = config.dataSource;
28124     
28125     if (config.store && !this.ds) {
28126         this.store= Roo.factory(config.store, Roo.data);
28127         this.ds = this.store;
28128         this.ds.xmodule = this.xmodule || false;
28129     }
28130     
28131     this.toolbarItems = [];
28132     if (config.items) {
28133         this.toolbarItems = config.items;
28134     }
28135     
28136     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28137     
28138     this.cursor = 0;
28139     
28140     if (this.ds) { 
28141         this.bind(this.ds);
28142     }
28143     
28144     if (Roo.bootstrap.version == 4) {
28145         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28146     } else {
28147         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28148     }
28149     
28150 };
28151
28152 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28153     /**
28154      * @cfg {Roo.bootstrap.Button} buttons[]
28155      * Buttons for the toolbar
28156      */
28157      /**
28158      * @cfg {Roo.data.Store} store
28159      * The underlying data store providing the paged data
28160      */
28161     /**
28162      * @cfg {String/HTMLElement/Element} container
28163      * container The id or element that will contain the toolbar
28164      */
28165     /**
28166      * @cfg {Boolean} displayInfo
28167      * True to display the displayMsg (defaults to false)
28168      */
28169     /**
28170      * @cfg {Number} pageSize
28171      * The number of records to display per page (defaults to 20)
28172      */
28173     pageSize: 20,
28174     /**
28175      * @cfg {String} displayMsg
28176      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28177      */
28178     displayMsg : 'Displaying {0} - {1} of {2}',
28179     /**
28180      * @cfg {String} emptyMsg
28181      * The message to display when no records are found (defaults to "No data to display")
28182      */
28183     emptyMsg : 'No data to display',
28184     /**
28185      * Customizable piece of the default paging text (defaults to "Page")
28186      * @type String
28187      */
28188     beforePageText : "Page",
28189     /**
28190      * Customizable piece of the default paging text (defaults to "of %0")
28191      * @type String
28192      */
28193     afterPageText : "of {0}",
28194     /**
28195      * Customizable piece of the default paging text (defaults to "First Page")
28196      * @type String
28197      */
28198     firstText : "First Page",
28199     /**
28200      * Customizable piece of the default paging text (defaults to "Previous Page")
28201      * @type String
28202      */
28203     prevText : "Previous Page",
28204     /**
28205      * Customizable piece of the default paging text (defaults to "Next Page")
28206      * @type String
28207      */
28208     nextText : "Next Page",
28209     /**
28210      * Customizable piece of the default paging text (defaults to "Last Page")
28211      * @type String
28212      */
28213     lastText : "Last Page",
28214     /**
28215      * Customizable piece of the default paging text (defaults to "Refresh")
28216      * @type String
28217      */
28218     refreshText : "Refresh",
28219
28220     buttons : false,
28221     // private
28222     onRender : function(ct, position) 
28223     {
28224         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28225         this.navgroup.parentId = this.id;
28226         this.navgroup.onRender(this.el, null);
28227         // add the buttons to the navgroup
28228         
28229         if(this.displayInfo){
28230             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28231             this.displayEl = this.el.select('.x-paging-info', true).first();
28232 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28233 //            this.displayEl = navel.el.select('span',true).first();
28234         }
28235         
28236         var _this = this;
28237         
28238         if(this.buttons){
28239             Roo.each(_this.buttons, function(e){ // this might need to use render????
28240                Roo.factory(e).render(_this.el);
28241             });
28242         }
28243             
28244         Roo.each(_this.toolbarItems, function(e) {
28245             _this.navgroup.addItem(e);
28246         });
28247         
28248         
28249         this.first = this.navgroup.addItem({
28250             tooltip: this.firstText,
28251             cls: "prev btn-outline-secondary",
28252             html : ' <i class="fa fa-step-backward"></i>',
28253             disabled: true,
28254             preventDefault: true,
28255             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28256         });
28257         
28258         this.prev =  this.navgroup.addItem({
28259             tooltip: this.prevText,
28260             cls: "prev btn-outline-secondary",
28261             html : ' <i class="fa fa-backward"></i>',
28262             disabled: true,
28263             preventDefault: true,
28264             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28265         });
28266     //this.addSeparator();
28267         
28268         
28269         var field = this.navgroup.addItem( {
28270             tagtype : 'span',
28271             cls : 'x-paging-position  btn-outline-secondary',
28272              disabled: true,
28273             html : this.beforePageText  +
28274                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28275                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28276          } ); //?? escaped?
28277         
28278         this.field = field.el.select('input', true).first();
28279         this.field.on("keydown", this.onPagingKeydown, this);
28280         this.field.on("focus", function(){this.dom.select();});
28281     
28282     
28283         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28284         //this.field.setHeight(18);
28285         //this.addSeparator();
28286         this.next = this.navgroup.addItem({
28287             tooltip: this.nextText,
28288             cls: "next btn-outline-secondary",
28289             html : ' <i class="fa fa-forward"></i>',
28290             disabled: true,
28291             preventDefault: true,
28292             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28293         });
28294         this.last = this.navgroup.addItem({
28295             tooltip: this.lastText,
28296             html : ' <i class="fa fa-step-forward"></i>',
28297             cls: "next btn-outline-secondary",
28298             disabled: true,
28299             preventDefault: true,
28300             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28301         });
28302     //this.addSeparator();
28303         this.loading = this.navgroup.addItem({
28304             tooltip: this.refreshText,
28305             cls: "btn-outline-secondary",
28306             html : ' <i class="fa fa-refresh"></i>',
28307             preventDefault: true,
28308             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28309         });
28310         
28311     },
28312
28313     // private
28314     updateInfo : function(){
28315         if(this.displayEl){
28316             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28317             var msg = count == 0 ?
28318                 this.emptyMsg :
28319                 String.format(
28320                     this.displayMsg,
28321                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28322                 );
28323             this.displayEl.update(msg);
28324         }
28325     },
28326
28327     // private
28328     onLoad : function(ds, r, o)
28329     {
28330         this.cursor = o.params && o.params.start ? o.params.start : 0;
28331         
28332         var d = this.getPageData(),
28333             ap = d.activePage,
28334             ps = d.pages;
28335         
28336         
28337         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28338         this.field.dom.value = ap;
28339         this.first.setDisabled(ap == 1);
28340         this.prev.setDisabled(ap == 1);
28341         this.next.setDisabled(ap == ps);
28342         this.last.setDisabled(ap == ps);
28343         this.loading.enable();
28344         this.updateInfo();
28345     },
28346
28347     // private
28348     getPageData : function(){
28349         var total = this.ds.getTotalCount();
28350         return {
28351             total : total,
28352             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28353             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28354         };
28355     },
28356
28357     // private
28358     onLoadError : function(){
28359         this.loading.enable();
28360     },
28361
28362     // private
28363     onPagingKeydown : function(e){
28364         var k = e.getKey();
28365         var d = this.getPageData();
28366         if(k == e.RETURN){
28367             var v = this.field.dom.value, pageNum;
28368             if(!v || isNaN(pageNum = parseInt(v, 10))){
28369                 this.field.dom.value = d.activePage;
28370                 return;
28371             }
28372             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28373             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28374             e.stopEvent();
28375         }
28376         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))
28377         {
28378           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28379           this.field.dom.value = pageNum;
28380           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28381           e.stopEvent();
28382         }
28383         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28384         {
28385           var v = this.field.dom.value, pageNum; 
28386           var increment = (e.shiftKey) ? 10 : 1;
28387           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28388                 increment *= -1;
28389           }
28390           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28391             this.field.dom.value = d.activePage;
28392             return;
28393           }
28394           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28395           {
28396             this.field.dom.value = parseInt(v, 10) + increment;
28397             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28398             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28399           }
28400           e.stopEvent();
28401         }
28402     },
28403
28404     // private
28405     beforeLoad : function(){
28406         if(this.loading){
28407             this.loading.disable();
28408         }
28409     },
28410
28411     // private
28412     onClick : function(which){
28413         
28414         var ds = this.ds;
28415         if (!ds) {
28416             return;
28417         }
28418         
28419         switch(which){
28420             case "first":
28421                 ds.load({params:{start: 0, limit: this.pageSize}});
28422             break;
28423             case "prev":
28424                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28425             break;
28426             case "next":
28427                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28428             break;
28429             case "last":
28430                 var total = ds.getTotalCount();
28431                 var extra = total % this.pageSize;
28432                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28433                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28434             break;
28435             case "refresh":
28436                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28437             break;
28438         }
28439     },
28440
28441     /**
28442      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28443      * @param {Roo.data.Store} store The data store to unbind
28444      */
28445     unbind : function(ds){
28446         ds.un("beforeload", this.beforeLoad, this);
28447         ds.un("load", this.onLoad, this);
28448         ds.un("loadexception", this.onLoadError, this);
28449         ds.un("remove", this.updateInfo, this);
28450         ds.un("add", this.updateInfo, this);
28451         this.ds = undefined;
28452     },
28453
28454     /**
28455      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28456      * @param {Roo.data.Store} store The data store to bind
28457      */
28458     bind : function(ds){
28459         ds.on("beforeload", this.beforeLoad, this);
28460         ds.on("load", this.onLoad, this);
28461         ds.on("loadexception", this.onLoadError, this);
28462         ds.on("remove", this.updateInfo, this);
28463         ds.on("add", this.updateInfo, this);
28464         this.ds = ds;
28465     }
28466 });/*
28467  * - LGPL
28468  *
28469  * element
28470  * 
28471  */
28472
28473 /**
28474  * @class Roo.bootstrap.MessageBar
28475  * @extends Roo.bootstrap.Component
28476  * Bootstrap MessageBar class
28477  * @cfg {String} html contents of the MessageBar
28478  * @cfg {String} weight (info | success | warning | danger) default info
28479  * @cfg {String} beforeClass insert the bar before the given class
28480  * @cfg {Boolean} closable (true | false) default false
28481  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28482  * 
28483  * @constructor
28484  * Create a new Element
28485  * @param {Object} config The config object
28486  */
28487
28488 Roo.bootstrap.MessageBar = function(config){
28489     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28490 };
28491
28492 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28493     
28494     html: '',
28495     weight: 'info',
28496     closable: false,
28497     fixed: false,
28498     beforeClass: 'bootstrap-sticky-wrap',
28499     
28500     getAutoCreate : function(){
28501         
28502         var cfg = {
28503             tag: 'div',
28504             cls: 'alert alert-dismissable alert-' + this.weight,
28505             cn: [
28506                 {
28507                     tag: 'span',
28508                     cls: 'message',
28509                     html: this.html || ''
28510                 }
28511             ]
28512         };
28513         
28514         if(this.fixed){
28515             cfg.cls += ' alert-messages-fixed';
28516         }
28517         
28518         if(this.closable){
28519             cfg.cn.push({
28520                 tag: 'button',
28521                 cls: 'close',
28522                 html: 'x'
28523             });
28524         }
28525         
28526         return cfg;
28527     },
28528     
28529     onRender : function(ct, position)
28530     {
28531         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28532         
28533         if(!this.el){
28534             var cfg = Roo.apply({},  this.getAutoCreate());
28535             cfg.id = Roo.id();
28536             
28537             if (this.cls) {
28538                 cfg.cls += ' ' + this.cls;
28539             }
28540             if (this.style) {
28541                 cfg.style = this.style;
28542             }
28543             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28544             
28545             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28546         }
28547         
28548         this.el.select('>button.close').on('click', this.hide, this);
28549         
28550     },
28551     
28552     show : function()
28553     {
28554         if (!this.rendered) {
28555             this.render();
28556         }
28557         
28558         this.el.show();
28559         
28560         this.fireEvent('show', this);
28561         
28562     },
28563     
28564     hide : function()
28565     {
28566         if (!this.rendered) {
28567             this.render();
28568         }
28569         
28570         this.el.hide();
28571         
28572         this.fireEvent('hide', this);
28573     },
28574     
28575     update : function()
28576     {
28577 //        var e = this.el.dom.firstChild;
28578 //        
28579 //        if(this.closable){
28580 //            e = e.nextSibling;
28581 //        }
28582 //        
28583 //        e.data = this.html || '';
28584
28585         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28586     }
28587    
28588 });
28589
28590  
28591
28592      /*
28593  * - LGPL
28594  *
28595  * Graph
28596  * 
28597  */
28598
28599
28600 /**
28601  * @class Roo.bootstrap.Graph
28602  * @extends Roo.bootstrap.Component
28603  * Bootstrap Graph class
28604 > Prameters
28605  -sm {number} sm 4
28606  -md {number} md 5
28607  @cfg {String} graphtype  bar | vbar | pie
28608  @cfg {number} g_x coodinator | centre x (pie)
28609  @cfg {number} g_y coodinator | centre y (pie)
28610  @cfg {number} g_r radius (pie)
28611  @cfg {number} g_height height of the chart (respected by all elements in the set)
28612  @cfg {number} g_width width of the chart (respected by all elements in the set)
28613  @cfg {Object} title The title of the chart
28614     
28615  -{Array}  values
28616  -opts (object) options for the chart 
28617      o {
28618      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28619      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28620      o vgutter (number)
28621      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.
28622      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28623      o to
28624      o stretch (boolean)
28625      o }
28626  -opts (object) options for the pie
28627      o{
28628      o cut
28629      o startAngle (number)
28630      o endAngle (number)
28631      } 
28632  *
28633  * @constructor
28634  * Create a new Input
28635  * @param {Object} config The config object
28636  */
28637
28638 Roo.bootstrap.Graph = function(config){
28639     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28640     
28641     this.addEvents({
28642         // img events
28643         /**
28644          * @event click
28645          * The img click event for the img.
28646          * @param {Roo.EventObject} e
28647          */
28648         "click" : true
28649     });
28650 };
28651
28652 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28653     
28654     sm: 4,
28655     md: 5,
28656     graphtype: 'bar',
28657     g_height: 250,
28658     g_width: 400,
28659     g_x: 50,
28660     g_y: 50,
28661     g_r: 30,
28662     opts:{
28663         //g_colors: this.colors,
28664         g_type: 'soft',
28665         g_gutter: '20%'
28666
28667     },
28668     title : false,
28669
28670     getAutoCreate : function(){
28671         
28672         var cfg = {
28673             tag: 'div',
28674             html : null
28675         };
28676         
28677         
28678         return  cfg;
28679     },
28680
28681     onRender : function(ct,position){
28682         
28683         
28684         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28685         
28686         if (typeof(Raphael) == 'undefined') {
28687             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28688             return;
28689         }
28690         
28691         this.raphael = Raphael(this.el.dom);
28692         
28693                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28694                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28695                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28696                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28697                 /*
28698                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28699                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28700                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28701                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28702                 
28703                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28704                 r.barchart(330, 10, 300, 220, data1);
28705                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28706                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28707                 */
28708                 
28709                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28710                 // r.barchart(30, 30, 560, 250,  xdata, {
28711                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28712                 //     axis : "0 0 1 1",
28713                 //     axisxlabels :  xdata
28714                 //     //yvalues : cols,
28715                    
28716                 // });
28717 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28718 //        
28719 //        this.load(null,xdata,{
28720 //                axis : "0 0 1 1",
28721 //                axisxlabels :  xdata
28722 //                });
28723
28724     },
28725
28726     load : function(graphtype,xdata,opts)
28727     {
28728         this.raphael.clear();
28729         if(!graphtype) {
28730             graphtype = this.graphtype;
28731         }
28732         if(!opts){
28733             opts = this.opts;
28734         }
28735         var r = this.raphael,
28736             fin = function () {
28737                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28738             },
28739             fout = function () {
28740                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28741             },
28742             pfin = function() {
28743                 this.sector.stop();
28744                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28745
28746                 if (this.label) {
28747                     this.label[0].stop();
28748                     this.label[0].attr({ r: 7.5 });
28749                     this.label[1].attr({ "font-weight": 800 });
28750                 }
28751             },
28752             pfout = function() {
28753                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28754
28755                 if (this.label) {
28756                     this.label[0].animate({ r: 5 }, 500, "bounce");
28757                     this.label[1].attr({ "font-weight": 400 });
28758                 }
28759             };
28760
28761         switch(graphtype){
28762             case 'bar':
28763                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28764                 break;
28765             case 'hbar':
28766                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28767                 break;
28768             case 'pie':
28769 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28770 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28771 //            
28772                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28773                 
28774                 break;
28775
28776         }
28777         
28778         if(this.title){
28779             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28780         }
28781         
28782     },
28783     
28784     setTitle: function(o)
28785     {
28786         this.title = o;
28787     },
28788     
28789     initEvents: function() {
28790         
28791         if(!this.href){
28792             this.el.on('click', this.onClick, this);
28793         }
28794     },
28795     
28796     onClick : function(e)
28797     {
28798         Roo.log('img onclick');
28799         this.fireEvent('click', this, e);
28800     }
28801    
28802 });
28803
28804  
28805 /*
28806  * - LGPL
28807  *
28808  * numberBox
28809  * 
28810  */
28811 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28812
28813 /**
28814  * @class Roo.bootstrap.dash.NumberBox
28815  * @extends Roo.bootstrap.Component
28816  * Bootstrap NumberBox class
28817  * @cfg {String} headline Box headline
28818  * @cfg {String} content Box content
28819  * @cfg {String} icon Box icon
28820  * @cfg {String} footer Footer text
28821  * @cfg {String} fhref Footer href
28822  * 
28823  * @constructor
28824  * Create a new NumberBox
28825  * @param {Object} config The config object
28826  */
28827
28828
28829 Roo.bootstrap.dash.NumberBox = function(config){
28830     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28831     
28832 };
28833
28834 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28835     
28836     headline : '',
28837     content : '',
28838     icon : '',
28839     footer : '',
28840     fhref : '',
28841     ficon : '',
28842     
28843     getAutoCreate : function(){
28844         
28845         var cfg = {
28846             tag : 'div',
28847             cls : 'small-box ',
28848             cn : [
28849                 {
28850                     tag : 'div',
28851                     cls : 'inner',
28852                     cn :[
28853                         {
28854                             tag : 'h3',
28855                             cls : 'roo-headline',
28856                             html : this.headline
28857                         },
28858                         {
28859                             tag : 'p',
28860                             cls : 'roo-content',
28861                             html : this.content
28862                         }
28863                     ]
28864                 }
28865             ]
28866         };
28867         
28868         if(this.icon){
28869             cfg.cn.push({
28870                 tag : 'div',
28871                 cls : 'icon',
28872                 cn :[
28873                     {
28874                         tag : 'i',
28875                         cls : 'ion ' + this.icon
28876                     }
28877                 ]
28878             });
28879         }
28880         
28881         if(this.footer){
28882             var footer = {
28883                 tag : 'a',
28884                 cls : 'small-box-footer',
28885                 href : this.fhref || '#',
28886                 html : this.footer
28887             };
28888             
28889             cfg.cn.push(footer);
28890             
28891         }
28892         
28893         return  cfg;
28894     },
28895
28896     onRender : function(ct,position){
28897         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28898
28899
28900        
28901                 
28902     },
28903
28904     setHeadline: function (value)
28905     {
28906         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28907     },
28908     
28909     setFooter: function (value, href)
28910     {
28911         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28912         
28913         if(href){
28914             this.el.select('a.small-box-footer',true).first().attr('href', href);
28915         }
28916         
28917     },
28918
28919     setContent: function (value)
28920     {
28921         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28922     },
28923
28924     initEvents: function() 
28925     {   
28926         
28927     }
28928     
28929 });
28930
28931  
28932 /*
28933  * - LGPL
28934  *
28935  * TabBox
28936  * 
28937  */
28938 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28939
28940 /**
28941  * @class Roo.bootstrap.dash.TabBox
28942  * @extends Roo.bootstrap.Component
28943  * Bootstrap TabBox class
28944  * @cfg {String} title Title of the TabBox
28945  * @cfg {String} icon Icon of the TabBox
28946  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28947  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28948  * 
28949  * @constructor
28950  * Create a new TabBox
28951  * @param {Object} config The config object
28952  */
28953
28954
28955 Roo.bootstrap.dash.TabBox = function(config){
28956     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28957     this.addEvents({
28958         // raw events
28959         /**
28960          * @event addpane
28961          * When a pane is added
28962          * @param {Roo.bootstrap.dash.TabPane} pane
28963          */
28964         "addpane" : true,
28965         /**
28966          * @event activatepane
28967          * When a pane is activated
28968          * @param {Roo.bootstrap.dash.TabPane} pane
28969          */
28970         "activatepane" : true
28971         
28972          
28973     });
28974     
28975     this.panes = [];
28976 };
28977
28978 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28979
28980     title : '',
28981     icon : false,
28982     showtabs : true,
28983     tabScrollable : false,
28984     
28985     getChildContainer : function()
28986     {
28987         return this.el.select('.tab-content', true).first();
28988     },
28989     
28990     getAutoCreate : function(){
28991         
28992         var header = {
28993             tag: 'li',
28994             cls: 'pull-left header',
28995             html: this.title,
28996             cn : []
28997         };
28998         
28999         if(this.icon){
29000             header.cn.push({
29001                 tag: 'i',
29002                 cls: 'fa ' + this.icon
29003             });
29004         }
29005         
29006         var h = {
29007             tag: 'ul',
29008             cls: 'nav nav-tabs pull-right',
29009             cn: [
29010                 header
29011             ]
29012         };
29013         
29014         if(this.tabScrollable){
29015             h = {
29016                 tag: 'div',
29017                 cls: 'tab-header',
29018                 cn: [
29019                     {
29020                         tag: 'ul',
29021                         cls: 'nav nav-tabs pull-right',
29022                         cn: [
29023                             header
29024                         ]
29025                     }
29026                 ]
29027             };
29028         }
29029         
29030         var cfg = {
29031             tag: 'div',
29032             cls: 'nav-tabs-custom',
29033             cn: [
29034                 h,
29035                 {
29036                     tag: 'div',
29037                     cls: 'tab-content no-padding',
29038                     cn: []
29039                 }
29040             ]
29041         };
29042
29043         return  cfg;
29044     },
29045     initEvents : function()
29046     {
29047         //Roo.log('add add pane handler');
29048         this.on('addpane', this.onAddPane, this);
29049     },
29050      /**
29051      * Updates the box title
29052      * @param {String} html to set the title to.
29053      */
29054     setTitle : function(value)
29055     {
29056         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29057     },
29058     onAddPane : function(pane)
29059     {
29060         this.panes.push(pane);
29061         //Roo.log('addpane');
29062         //Roo.log(pane);
29063         // tabs are rendere left to right..
29064         if(!this.showtabs){
29065             return;
29066         }
29067         
29068         var ctr = this.el.select('.nav-tabs', true).first();
29069          
29070          
29071         var existing = ctr.select('.nav-tab',true);
29072         var qty = existing.getCount();;
29073         
29074         
29075         var tab = ctr.createChild({
29076             tag : 'li',
29077             cls : 'nav-tab' + (qty ? '' : ' active'),
29078             cn : [
29079                 {
29080                     tag : 'a',
29081                     href:'#',
29082                     html : pane.title
29083                 }
29084             ]
29085         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29086         pane.tab = tab;
29087         
29088         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29089         if (!qty) {
29090             pane.el.addClass('active');
29091         }
29092         
29093                 
29094     },
29095     onTabClick : function(ev,un,ob,pane)
29096     {
29097         //Roo.log('tab - prev default');
29098         ev.preventDefault();
29099         
29100         
29101         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29102         pane.tab.addClass('active');
29103         //Roo.log(pane.title);
29104         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29105         // technically we should have a deactivate event.. but maybe add later.
29106         // and it should not de-activate the selected tab...
29107         this.fireEvent('activatepane', pane);
29108         pane.el.addClass('active');
29109         pane.fireEvent('activate');
29110         
29111         
29112     },
29113     
29114     getActivePane : function()
29115     {
29116         var r = false;
29117         Roo.each(this.panes, function(p) {
29118             if(p.el.hasClass('active')){
29119                 r = p;
29120                 return false;
29121             }
29122             
29123             return;
29124         });
29125         
29126         return r;
29127     }
29128     
29129     
29130 });
29131
29132  
29133 /*
29134  * - LGPL
29135  *
29136  * Tab pane
29137  * 
29138  */
29139 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29140 /**
29141  * @class Roo.bootstrap.TabPane
29142  * @extends Roo.bootstrap.Component
29143  * Bootstrap TabPane class
29144  * @cfg {Boolean} active (false | true) Default false
29145  * @cfg {String} title title of panel
29146
29147  * 
29148  * @constructor
29149  * Create a new TabPane
29150  * @param {Object} config The config object
29151  */
29152
29153 Roo.bootstrap.dash.TabPane = function(config){
29154     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29155     
29156     this.addEvents({
29157         // raw events
29158         /**
29159          * @event activate
29160          * When a pane is activated
29161          * @param {Roo.bootstrap.dash.TabPane} pane
29162          */
29163         "activate" : true
29164          
29165     });
29166 };
29167
29168 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29169     
29170     active : false,
29171     title : '',
29172     
29173     // the tabBox that this is attached to.
29174     tab : false,
29175      
29176     getAutoCreate : function() 
29177     {
29178         var cfg = {
29179             tag: 'div',
29180             cls: 'tab-pane'
29181         };
29182         
29183         if(this.active){
29184             cfg.cls += ' active';
29185         }
29186         
29187         return cfg;
29188     },
29189     initEvents  : function()
29190     {
29191         //Roo.log('trigger add pane handler');
29192         this.parent().fireEvent('addpane', this)
29193     },
29194     
29195      /**
29196      * Updates the tab title 
29197      * @param {String} html to set the title to.
29198      */
29199     setTitle: function(str)
29200     {
29201         if (!this.tab) {
29202             return;
29203         }
29204         this.title = str;
29205         this.tab.select('a', true).first().dom.innerHTML = str;
29206         
29207     }
29208     
29209     
29210     
29211 });
29212
29213  
29214
29215
29216  /*
29217  * - LGPL
29218  *
29219  * menu
29220  * 
29221  */
29222 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29223
29224 /**
29225  * @class Roo.bootstrap.menu.Menu
29226  * @extends Roo.bootstrap.Component
29227  * Bootstrap Menu class - container for Menu
29228  * @cfg {String} html Text of the menu
29229  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29230  * @cfg {String} icon Font awesome icon
29231  * @cfg {String} pos Menu align to (top | bottom) default bottom
29232  * 
29233  * 
29234  * @constructor
29235  * Create a new Menu
29236  * @param {Object} config The config object
29237  */
29238
29239
29240 Roo.bootstrap.menu.Menu = function(config){
29241     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29242     
29243     this.addEvents({
29244         /**
29245          * @event beforeshow
29246          * Fires before this menu is displayed
29247          * @param {Roo.bootstrap.menu.Menu} this
29248          */
29249         beforeshow : true,
29250         /**
29251          * @event beforehide
29252          * Fires before this menu is hidden
29253          * @param {Roo.bootstrap.menu.Menu} this
29254          */
29255         beforehide : true,
29256         /**
29257          * @event show
29258          * Fires after this menu is displayed
29259          * @param {Roo.bootstrap.menu.Menu} this
29260          */
29261         show : true,
29262         /**
29263          * @event hide
29264          * Fires after this menu is hidden
29265          * @param {Roo.bootstrap.menu.Menu} this
29266          */
29267         hide : true,
29268         /**
29269          * @event click
29270          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29271          * @param {Roo.bootstrap.menu.Menu} this
29272          * @param {Roo.EventObject} e
29273          */
29274         click : true
29275     });
29276     
29277 };
29278
29279 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29280     
29281     submenu : false,
29282     html : '',
29283     weight : 'default',
29284     icon : false,
29285     pos : 'bottom',
29286     
29287     
29288     getChildContainer : function() {
29289         if(this.isSubMenu){
29290             return this.el;
29291         }
29292         
29293         return this.el.select('ul.dropdown-menu', true).first();  
29294     },
29295     
29296     getAutoCreate : function()
29297     {
29298         var text = [
29299             {
29300                 tag : 'span',
29301                 cls : 'roo-menu-text',
29302                 html : this.html
29303             }
29304         ];
29305         
29306         if(this.icon){
29307             text.unshift({
29308                 tag : 'i',
29309                 cls : 'fa ' + this.icon
29310             })
29311         }
29312         
29313         
29314         var cfg = {
29315             tag : 'div',
29316             cls : 'btn-group',
29317             cn : [
29318                 {
29319                     tag : 'button',
29320                     cls : 'dropdown-button btn btn-' + this.weight,
29321                     cn : text
29322                 },
29323                 {
29324                     tag : 'button',
29325                     cls : 'dropdown-toggle btn btn-' + this.weight,
29326                     cn : [
29327                         {
29328                             tag : 'span',
29329                             cls : 'caret'
29330                         }
29331                     ]
29332                 },
29333                 {
29334                     tag : 'ul',
29335                     cls : 'dropdown-menu'
29336                 }
29337             ]
29338             
29339         };
29340         
29341         if(this.pos == 'top'){
29342             cfg.cls += ' dropup';
29343         }
29344         
29345         if(this.isSubMenu){
29346             cfg = {
29347                 tag : 'ul',
29348                 cls : 'dropdown-menu'
29349             }
29350         }
29351         
29352         return cfg;
29353     },
29354     
29355     onRender : function(ct, position)
29356     {
29357         this.isSubMenu = ct.hasClass('dropdown-submenu');
29358         
29359         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29360     },
29361     
29362     initEvents : function() 
29363     {
29364         if(this.isSubMenu){
29365             return;
29366         }
29367         
29368         this.hidden = true;
29369         
29370         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29371         this.triggerEl.on('click', this.onTriggerPress, this);
29372         
29373         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29374         this.buttonEl.on('click', this.onClick, this);
29375         
29376     },
29377     
29378     list : function()
29379     {
29380         if(this.isSubMenu){
29381             return this.el;
29382         }
29383         
29384         return this.el.select('ul.dropdown-menu', true).first();
29385     },
29386     
29387     onClick : function(e)
29388     {
29389         this.fireEvent("click", this, e);
29390     },
29391     
29392     onTriggerPress  : function(e)
29393     {   
29394         if (this.isVisible()) {
29395             this.hide();
29396         } else {
29397             this.show();
29398         }
29399     },
29400     
29401     isVisible : function(){
29402         return !this.hidden;
29403     },
29404     
29405     show : function()
29406     {
29407         this.fireEvent("beforeshow", this);
29408         
29409         this.hidden = false;
29410         this.el.addClass('open');
29411         
29412         Roo.get(document).on("mouseup", this.onMouseUp, this);
29413         
29414         this.fireEvent("show", this);
29415         
29416         
29417     },
29418     
29419     hide : function()
29420     {
29421         this.fireEvent("beforehide", this);
29422         
29423         this.hidden = true;
29424         this.el.removeClass('open');
29425         
29426         Roo.get(document).un("mouseup", this.onMouseUp);
29427         
29428         this.fireEvent("hide", this);
29429     },
29430     
29431     onMouseUp : function()
29432     {
29433         this.hide();
29434     }
29435     
29436 });
29437
29438  
29439  /*
29440  * - LGPL
29441  *
29442  * menu item
29443  * 
29444  */
29445 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29446
29447 /**
29448  * @class Roo.bootstrap.menu.Item
29449  * @extends Roo.bootstrap.Component
29450  * Bootstrap MenuItem class
29451  * @cfg {Boolean} submenu (true | false) default false
29452  * @cfg {String} html text of the item
29453  * @cfg {String} href the link
29454  * @cfg {Boolean} disable (true | false) default false
29455  * @cfg {Boolean} preventDefault (true | false) default true
29456  * @cfg {String} icon Font awesome icon
29457  * @cfg {String} pos Submenu align to (left | right) default right 
29458  * 
29459  * 
29460  * @constructor
29461  * Create a new Item
29462  * @param {Object} config The config object
29463  */
29464
29465
29466 Roo.bootstrap.menu.Item = function(config){
29467     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29468     this.addEvents({
29469         /**
29470          * @event mouseover
29471          * Fires when the mouse is hovering over this menu
29472          * @param {Roo.bootstrap.menu.Item} this
29473          * @param {Roo.EventObject} e
29474          */
29475         mouseover : true,
29476         /**
29477          * @event mouseout
29478          * Fires when the mouse exits this menu
29479          * @param {Roo.bootstrap.menu.Item} this
29480          * @param {Roo.EventObject} e
29481          */
29482         mouseout : true,
29483         // raw events
29484         /**
29485          * @event click
29486          * The raw click event for the entire grid.
29487          * @param {Roo.EventObject} e
29488          */
29489         click : true
29490     });
29491 };
29492
29493 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29494     
29495     submenu : false,
29496     href : '',
29497     html : '',
29498     preventDefault: true,
29499     disable : false,
29500     icon : false,
29501     pos : 'right',
29502     
29503     getAutoCreate : function()
29504     {
29505         var text = [
29506             {
29507                 tag : 'span',
29508                 cls : 'roo-menu-item-text',
29509                 html : this.html
29510             }
29511         ];
29512         
29513         if(this.icon){
29514             text.unshift({
29515                 tag : 'i',
29516                 cls : 'fa ' + this.icon
29517             })
29518         }
29519         
29520         var cfg = {
29521             tag : 'li',
29522             cn : [
29523                 {
29524                     tag : 'a',
29525                     href : this.href || '#',
29526                     cn : text
29527                 }
29528             ]
29529         };
29530         
29531         if(this.disable){
29532             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29533         }
29534         
29535         if(this.submenu){
29536             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29537             
29538             if(this.pos == 'left'){
29539                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29540             }
29541         }
29542         
29543         return cfg;
29544     },
29545     
29546     initEvents : function() 
29547     {
29548         this.el.on('mouseover', this.onMouseOver, this);
29549         this.el.on('mouseout', this.onMouseOut, this);
29550         
29551         this.el.select('a', true).first().on('click', this.onClick, this);
29552         
29553     },
29554     
29555     onClick : function(e)
29556     {
29557         if(this.preventDefault){
29558             e.preventDefault();
29559         }
29560         
29561         this.fireEvent("click", this, e);
29562     },
29563     
29564     onMouseOver : function(e)
29565     {
29566         if(this.submenu && this.pos == 'left'){
29567             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29568         }
29569         
29570         this.fireEvent("mouseover", this, e);
29571     },
29572     
29573     onMouseOut : function(e)
29574     {
29575         this.fireEvent("mouseout", this, e);
29576     }
29577 });
29578
29579  
29580
29581  /*
29582  * - LGPL
29583  *
29584  * menu separator
29585  * 
29586  */
29587 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29588
29589 /**
29590  * @class Roo.bootstrap.menu.Separator
29591  * @extends Roo.bootstrap.Component
29592  * Bootstrap Separator class
29593  * 
29594  * @constructor
29595  * Create a new Separator
29596  * @param {Object} config The config object
29597  */
29598
29599
29600 Roo.bootstrap.menu.Separator = function(config){
29601     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29602 };
29603
29604 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29605     
29606     getAutoCreate : function(){
29607         var cfg = {
29608             tag : 'li',
29609             cls: 'dropdown-divider divider'
29610         };
29611         
29612         return cfg;
29613     }
29614    
29615 });
29616
29617  
29618
29619  /*
29620  * - LGPL
29621  *
29622  * Tooltip
29623  * 
29624  */
29625
29626 /**
29627  * @class Roo.bootstrap.Tooltip
29628  * Bootstrap Tooltip class
29629  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29630  * to determine which dom element triggers the tooltip.
29631  * 
29632  * It needs to add support for additional attributes like tooltip-position
29633  * 
29634  * @constructor
29635  * Create a new Toolti
29636  * @param {Object} config The config object
29637  */
29638
29639 Roo.bootstrap.Tooltip = function(config){
29640     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29641     
29642     this.alignment = Roo.bootstrap.Tooltip.alignment;
29643     
29644     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29645         this.alignment = config.alignment;
29646     }
29647     
29648 };
29649
29650 Roo.apply(Roo.bootstrap.Tooltip, {
29651     /**
29652      * @function init initialize tooltip monitoring.
29653      * @static
29654      */
29655     currentEl : false,
29656     currentTip : false,
29657     currentRegion : false,
29658     
29659     //  init : delay?
29660     
29661     init : function()
29662     {
29663         Roo.get(document).on('mouseover', this.enter ,this);
29664         Roo.get(document).on('mouseout', this.leave, this);
29665          
29666         
29667         this.currentTip = new Roo.bootstrap.Tooltip();
29668     },
29669     
29670     enter : function(ev)
29671     {
29672         var dom = ev.getTarget();
29673         
29674         //Roo.log(['enter',dom]);
29675         var el = Roo.fly(dom);
29676         if (this.currentEl) {
29677             //Roo.log(dom);
29678             //Roo.log(this.currentEl);
29679             //Roo.log(this.currentEl.contains(dom));
29680             if (this.currentEl == el) {
29681                 return;
29682             }
29683             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29684                 return;
29685             }
29686
29687         }
29688         
29689         if (this.currentTip.el) {
29690             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29691         }    
29692         //Roo.log(ev);
29693         
29694         if(!el || el.dom == document){
29695             return;
29696         }
29697         
29698         var bindEl = el; 
29699         var pel = false;
29700         if (!el.attr('tooltip')) {
29701             pel = el.findParent("[tooltip]");
29702             if (pel) {
29703                 bindEl = Roo.get(pel);
29704             }
29705         }
29706         
29707        
29708         
29709         // you can not look for children, as if el is the body.. then everythign is the child..
29710         if (!pel && !el.attr('tooltip')) { //
29711             if (!el.select("[tooltip]").elements.length) {
29712                 return;
29713             }
29714             // is the mouse over this child...?
29715             bindEl = el.select("[tooltip]").first();
29716             var xy = ev.getXY();
29717             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29718                 //Roo.log("not in region.");
29719                 return;
29720             }
29721             //Roo.log("child element over..");
29722             
29723         }
29724         this.currentEl = el;
29725         this.currentTip.bind(bindEl);
29726         this.currentRegion = Roo.lib.Region.getRegion(dom);
29727         this.currentTip.enter();
29728         
29729     },
29730     leave : function(ev)
29731     {
29732         var dom = ev.getTarget();
29733         //Roo.log(['leave',dom]);
29734         if (!this.currentEl) {
29735             return;
29736         }
29737         
29738         
29739         if (dom != this.currentEl.dom) {
29740             return;
29741         }
29742         var xy = ev.getXY();
29743         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29744             return;
29745         }
29746         // only activate leave if mouse cursor is outside... bounding box..
29747         
29748         
29749         
29750         
29751         if (this.currentTip) {
29752             this.currentTip.leave();
29753         }
29754         //Roo.log('clear currentEl');
29755         this.currentEl = false;
29756         
29757         
29758     },
29759     alignment : {
29760         'left' : ['r-l', [-2,0], 'right'],
29761         'right' : ['l-r', [2,0], 'left'],
29762         'bottom' : ['t-b', [0,2], 'top'],
29763         'top' : [ 'b-t', [0,-2], 'bottom']
29764     }
29765     
29766 });
29767
29768
29769 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29770     
29771     
29772     bindEl : false,
29773     
29774     delay : null, // can be { show : 300 , hide: 500}
29775     
29776     timeout : null,
29777     
29778     hoverState : null, //???
29779     
29780     placement : 'bottom', 
29781     
29782     alignment : false,
29783     
29784     getAutoCreate : function(){
29785     
29786         var cfg = {
29787            cls : 'tooltip',   
29788            role : 'tooltip',
29789            cn : [
29790                 {
29791                     cls : 'tooltip-arrow arrow'
29792                 },
29793                 {
29794                     cls : 'tooltip-inner'
29795                 }
29796            ]
29797         };
29798         
29799         return cfg;
29800     },
29801     bind : function(el)
29802     {
29803         this.bindEl = el;
29804     },
29805     
29806     initEvents : function()
29807     {
29808         this.arrowEl = this.el.select('.arrow', true).first();
29809         this.innerEl = this.el.select('.tooltip-inner', true).first();
29810     },
29811     
29812     enter : function () {
29813        
29814         if (this.timeout != null) {
29815             clearTimeout(this.timeout);
29816         }
29817         
29818         this.hoverState = 'in';
29819          //Roo.log("enter - show");
29820         if (!this.delay || !this.delay.show) {
29821             this.show();
29822             return;
29823         }
29824         var _t = this;
29825         this.timeout = setTimeout(function () {
29826             if (_t.hoverState == 'in') {
29827                 _t.show();
29828             }
29829         }, this.delay.show);
29830     },
29831     leave : function()
29832     {
29833         clearTimeout(this.timeout);
29834     
29835         this.hoverState = 'out';
29836          if (!this.delay || !this.delay.hide) {
29837             this.hide();
29838             return;
29839         }
29840        
29841         var _t = this;
29842         this.timeout = setTimeout(function () {
29843             //Roo.log("leave - timeout");
29844             
29845             if (_t.hoverState == 'out') {
29846                 _t.hide();
29847                 Roo.bootstrap.Tooltip.currentEl = false;
29848             }
29849         }, delay);
29850     },
29851     
29852     show : function (msg)
29853     {
29854         if (!this.el) {
29855             this.render(document.body);
29856         }
29857         // set content.
29858         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29859         
29860         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29861         
29862         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29863         
29864         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29865                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29866         
29867         var placement = typeof this.placement == 'function' ?
29868             this.placement.call(this, this.el, on_el) :
29869             this.placement;
29870             
29871         var autoToken = /\s?auto?\s?/i;
29872         var autoPlace = autoToken.test(placement);
29873         if (autoPlace) {
29874             placement = placement.replace(autoToken, '') || 'top';
29875         }
29876         
29877         //this.el.detach()
29878         //this.el.setXY([0,0]);
29879         this.el.show();
29880         //this.el.dom.style.display='block';
29881         
29882         //this.el.appendTo(on_el);
29883         
29884         var p = this.getPosition();
29885         var box = this.el.getBox();
29886         
29887         if (autoPlace) {
29888             // fixme..
29889         }
29890         
29891         var align = this.alignment[placement];
29892         
29893         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29894         
29895         if(placement == 'top' || placement == 'bottom'){
29896             if(xy[0] < 0){
29897                 placement = 'right';
29898             }
29899             
29900             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29901                 placement = 'left';
29902             }
29903             
29904             var scroll = Roo.select('body', true).first().getScroll();
29905             
29906             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29907                 placement = 'top';
29908             }
29909             
29910             align = this.alignment[placement];
29911             
29912             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29913             
29914         }
29915         
29916         var elems = document.getElementsByTagName('div');
29917         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29918         for (var i = 0; i < elems.length; i++) {
29919           var zindex = Number.parseInt(
29920                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29921                 10
29922           );
29923           if (zindex > highest) {
29924             highest = zindex;
29925           }
29926         }
29927         
29928         
29929         
29930         this.el.dom.style.zIndex = highest;
29931         
29932         this.el.alignTo(this.bindEl, align[0],align[1]);
29933         //var arrow = this.el.select('.arrow',true).first();
29934         //arrow.set(align[2], 
29935         
29936         this.el.addClass(placement);
29937         this.el.addClass("bs-tooltip-"+ placement);
29938         
29939         this.el.addClass('in fade show');
29940         
29941         this.hoverState = null;
29942         
29943         if (this.el.hasClass('fade')) {
29944             // fade it?
29945         }
29946         
29947         
29948         
29949         
29950         
29951     },
29952     hide : function()
29953     {
29954          
29955         if (!this.el) {
29956             return;
29957         }
29958         //this.el.setXY([0,0]);
29959         this.el.removeClass(['show', 'in']);
29960         //this.el.hide();
29961         
29962     }
29963     
29964 });
29965  
29966
29967  /*
29968  * - LGPL
29969  *
29970  * Location Picker
29971  * 
29972  */
29973
29974 /**
29975  * @class Roo.bootstrap.LocationPicker
29976  * @extends Roo.bootstrap.Component
29977  * Bootstrap LocationPicker class
29978  * @cfg {Number} latitude Position when init default 0
29979  * @cfg {Number} longitude Position when init default 0
29980  * @cfg {Number} zoom default 15
29981  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29982  * @cfg {Boolean} mapTypeControl default false
29983  * @cfg {Boolean} disableDoubleClickZoom default false
29984  * @cfg {Boolean} scrollwheel default true
29985  * @cfg {Boolean} streetViewControl default false
29986  * @cfg {Number} radius default 0
29987  * @cfg {String} locationName
29988  * @cfg {Boolean} draggable default true
29989  * @cfg {Boolean} enableAutocomplete default false
29990  * @cfg {Boolean} enableReverseGeocode default true
29991  * @cfg {String} markerTitle
29992  * 
29993  * @constructor
29994  * Create a new LocationPicker
29995  * @param {Object} config The config object
29996  */
29997
29998
29999 Roo.bootstrap.LocationPicker = function(config){
30000     
30001     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30002     
30003     this.addEvents({
30004         /**
30005          * @event initial
30006          * Fires when the picker initialized.
30007          * @param {Roo.bootstrap.LocationPicker} this
30008          * @param {Google Location} location
30009          */
30010         initial : true,
30011         /**
30012          * @event positionchanged
30013          * Fires when the picker position changed.
30014          * @param {Roo.bootstrap.LocationPicker} this
30015          * @param {Google Location} location
30016          */
30017         positionchanged : true,
30018         /**
30019          * @event resize
30020          * Fires when the map resize.
30021          * @param {Roo.bootstrap.LocationPicker} this
30022          */
30023         resize : true,
30024         /**
30025          * @event show
30026          * Fires when the map show.
30027          * @param {Roo.bootstrap.LocationPicker} this
30028          */
30029         show : true,
30030         /**
30031          * @event hide
30032          * Fires when the map hide.
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          */
30035         hide : true,
30036         /**
30037          * @event mapClick
30038          * Fires when click the map.
30039          * @param {Roo.bootstrap.LocationPicker} this
30040          * @param {Map event} e
30041          */
30042         mapClick : true,
30043         /**
30044          * @event mapRightClick
30045          * Fires when right click the map.
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          * @param {Map event} e
30048          */
30049         mapRightClick : true,
30050         /**
30051          * @event markerClick
30052          * Fires when click the marker.
30053          * @param {Roo.bootstrap.LocationPicker} this
30054          * @param {Map event} e
30055          */
30056         markerClick : true,
30057         /**
30058          * @event markerRightClick
30059          * Fires when right click the marker.
30060          * @param {Roo.bootstrap.LocationPicker} this
30061          * @param {Map event} e
30062          */
30063         markerRightClick : true,
30064         /**
30065          * @event OverlayViewDraw
30066          * Fires when OverlayView Draw
30067          * @param {Roo.bootstrap.LocationPicker} this
30068          */
30069         OverlayViewDraw : true,
30070         /**
30071          * @event OverlayViewOnAdd
30072          * Fires when OverlayView Draw
30073          * @param {Roo.bootstrap.LocationPicker} this
30074          */
30075         OverlayViewOnAdd : true,
30076         /**
30077          * @event OverlayViewOnRemove
30078          * Fires when OverlayView Draw
30079          * @param {Roo.bootstrap.LocationPicker} this
30080          */
30081         OverlayViewOnRemove : true,
30082         /**
30083          * @event OverlayViewShow
30084          * Fires when OverlayView Draw
30085          * @param {Roo.bootstrap.LocationPicker} this
30086          * @param {Pixel} cpx
30087          */
30088         OverlayViewShow : true,
30089         /**
30090          * @event OverlayViewHide
30091          * Fires when OverlayView Draw
30092          * @param {Roo.bootstrap.LocationPicker} this
30093          */
30094         OverlayViewHide : true,
30095         /**
30096          * @event loadexception
30097          * Fires when load google lib failed.
30098          * @param {Roo.bootstrap.LocationPicker} this
30099          */
30100         loadexception : true
30101     });
30102         
30103 };
30104
30105 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30106     
30107     gMapContext: false,
30108     
30109     latitude: 0,
30110     longitude: 0,
30111     zoom: 15,
30112     mapTypeId: false,
30113     mapTypeControl: false,
30114     disableDoubleClickZoom: false,
30115     scrollwheel: true,
30116     streetViewControl: false,
30117     radius: 0,
30118     locationName: '',
30119     draggable: true,
30120     enableAutocomplete: false,
30121     enableReverseGeocode: true,
30122     markerTitle: '',
30123     
30124     getAutoCreate: function()
30125     {
30126
30127         var cfg = {
30128             tag: 'div',
30129             cls: 'roo-location-picker'
30130         };
30131         
30132         return cfg
30133     },
30134     
30135     initEvents: function(ct, position)
30136     {       
30137         if(!this.el.getWidth() || this.isApplied()){
30138             return;
30139         }
30140         
30141         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30142         
30143         this.initial();
30144     },
30145     
30146     initial: function()
30147     {
30148         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30149             this.fireEvent('loadexception', this);
30150             return;
30151         }
30152         
30153         if(!this.mapTypeId){
30154             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30155         }
30156         
30157         this.gMapContext = this.GMapContext();
30158         
30159         this.initOverlayView();
30160         
30161         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30162         
30163         var _this = this;
30164                 
30165         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30166             _this.setPosition(_this.gMapContext.marker.position);
30167         });
30168         
30169         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30170             _this.fireEvent('mapClick', this, event);
30171             
30172         });
30173
30174         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30175             _this.fireEvent('mapRightClick', this, event);
30176             
30177         });
30178         
30179         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30180             _this.fireEvent('markerClick', this, event);
30181             
30182         });
30183
30184         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30185             _this.fireEvent('markerRightClick', this, event);
30186             
30187         });
30188         
30189         this.setPosition(this.gMapContext.location);
30190         
30191         this.fireEvent('initial', this, this.gMapContext.location);
30192     },
30193     
30194     initOverlayView: function()
30195     {
30196         var _this = this;
30197         
30198         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30199             
30200             draw: function()
30201             {
30202                 _this.fireEvent('OverlayViewDraw', _this);
30203             },
30204             
30205             onAdd: function()
30206             {
30207                 _this.fireEvent('OverlayViewOnAdd', _this);
30208             },
30209             
30210             onRemove: function()
30211             {
30212                 _this.fireEvent('OverlayViewOnRemove', _this);
30213             },
30214             
30215             show: function(cpx)
30216             {
30217                 _this.fireEvent('OverlayViewShow', _this, cpx);
30218             },
30219             
30220             hide: function()
30221             {
30222                 _this.fireEvent('OverlayViewHide', _this);
30223             }
30224             
30225         });
30226     },
30227     
30228     fromLatLngToContainerPixel: function(event)
30229     {
30230         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30231     },
30232     
30233     isApplied: function() 
30234     {
30235         return this.getGmapContext() == false ? false : true;
30236     },
30237     
30238     getGmapContext: function() 
30239     {
30240         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30241     },
30242     
30243     GMapContext: function() 
30244     {
30245         var position = new google.maps.LatLng(this.latitude, this.longitude);
30246         
30247         var _map = new google.maps.Map(this.el.dom, {
30248             center: position,
30249             zoom: this.zoom,
30250             mapTypeId: this.mapTypeId,
30251             mapTypeControl: this.mapTypeControl,
30252             disableDoubleClickZoom: this.disableDoubleClickZoom,
30253             scrollwheel: this.scrollwheel,
30254             streetViewControl: this.streetViewControl,
30255             locationName: this.locationName,
30256             draggable: this.draggable,
30257             enableAutocomplete: this.enableAutocomplete,
30258             enableReverseGeocode: this.enableReverseGeocode
30259         });
30260         
30261         var _marker = new google.maps.Marker({
30262             position: position,
30263             map: _map,
30264             title: this.markerTitle,
30265             draggable: this.draggable
30266         });
30267         
30268         return {
30269             map: _map,
30270             marker: _marker,
30271             circle: null,
30272             location: position,
30273             radius: this.radius,
30274             locationName: this.locationName,
30275             addressComponents: {
30276                 formatted_address: null,
30277                 addressLine1: null,
30278                 addressLine2: null,
30279                 streetName: null,
30280                 streetNumber: null,
30281                 city: null,
30282                 district: null,
30283                 state: null,
30284                 stateOrProvince: null
30285             },
30286             settings: this,
30287             domContainer: this.el.dom,
30288             geodecoder: new google.maps.Geocoder()
30289         };
30290     },
30291     
30292     drawCircle: function(center, radius, options) 
30293     {
30294         if (this.gMapContext.circle != null) {
30295             this.gMapContext.circle.setMap(null);
30296         }
30297         if (radius > 0) {
30298             radius *= 1;
30299             options = Roo.apply({}, options, {
30300                 strokeColor: "#0000FF",
30301                 strokeOpacity: .35,
30302                 strokeWeight: 2,
30303                 fillColor: "#0000FF",
30304                 fillOpacity: .2
30305             });
30306             
30307             options.map = this.gMapContext.map;
30308             options.radius = radius;
30309             options.center = center;
30310             this.gMapContext.circle = new google.maps.Circle(options);
30311             return this.gMapContext.circle;
30312         }
30313         
30314         return null;
30315     },
30316     
30317     setPosition: function(location) 
30318     {
30319         this.gMapContext.location = location;
30320         this.gMapContext.marker.setPosition(location);
30321         this.gMapContext.map.panTo(location);
30322         this.drawCircle(location, this.gMapContext.radius, {});
30323         
30324         var _this = this;
30325         
30326         if (this.gMapContext.settings.enableReverseGeocode) {
30327             this.gMapContext.geodecoder.geocode({
30328                 latLng: this.gMapContext.location
30329             }, function(results, status) {
30330                 
30331                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30332                     _this.gMapContext.locationName = results[0].formatted_address;
30333                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30334                     
30335                     _this.fireEvent('positionchanged', this, location);
30336                 }
30337             });
30338             
30339             return;
30340         }
30341         
30342         this.fireEvent('positionchanged', this, location);
30343     },
30344     
30345     resize: function()
30346     {
30347         google.maps.event.trigger(this.gMapContext.map, "resize");
30348         
30349         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30350         
30351         this.fireEvent('resize', this);
30352     },
30353     
30354     setPositionByLatLng: function(latitude, longitude)
30355     {
30356         this.setPosition(new google.maps.LatLng(latitude, longitude));
30357     },
30358     
30359     getCurrentPosition: function() 
30360     {
30361         return {
30362             latitude: this.gMapContext.location.lat(),
30363             longitude: this.gMapContext.location.lng()
30364         };
30365     },
30366     
30367     getAddressName: function() 
30368     {
30369         return this.gMapContext.locationName;
30370     },
30371     
30372     getAddressComponents: function() 
30373     {
30374         return this.gMapContext.addressComponents;
30375     },
30376     
30377     address_component_from_google_geocode: function(address_components) 
30378     {
30379         var result = {};
30380         
30381         for (var i = 0; i < address_components.length; i++) {
30382             var component = address_components[i];
30383             if (component.types.indexOf("postal_code") >= 0) {
30384                 result.postalCode = component.short_name;
30385             } else if (component.types.indexOf("street_number") >= 0) {
30386                 result.streetNumber = component.short_name;
30387             } else if (component.types.indexOf("route") >= 0) {
30388                 result.streetName = component.short_name;
30389             } else if (component.types.indexOf("neighborhood") >= 0) {
30390                 result.city = component.short_name;
30391             } else if (component.types.indexOf("locality") >= 0) {
30392                 result.city = component.short_name;
30393             } else if (component.types.indexOf("sublocality") >= 0) {
30394                 result.district = component.short_name;
30395             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30396                 result.stateOrProvince = component.short_name;
30397             } else if (component.types.indexOf("country") >= 0) {
30398                 result.country = component.short_name;
30399             }
30400         }
30401         
30402         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30403         result.addressLine2 = "";
30404         return result;
30405     },
30406     
30407     setZoomLevel: function(zoom)
30408     {
30409         this.gMapContext.map.setZoom(zoom);
30410     },
30411     
30412     show: function()
30413     {
30414         if(!this.el){
30415             return;
30416         }
30417         
30418         this.el.show();
30419         
30420         this.resize();
30421         
30422         this.fireEvent('show', this);
30423     },
30424     
30425     hide: function()
30426     {
30427         if(!this.el){
30428             return;
30429         }
30430         
30431         this.el.hide();
30432         
30433         this.fireEvent('hide', this);
30434     }
30435     
30436 });
30437
30438 Roo.apply(Roo.bootstrap.LocationPicker, {
30439     
30440     OverlayView : function(map, options)
30441     {
30442         options = options || {};
30443         
30444         this.setMap(map);
30445     }
30446     
30447     
30448 });/**
30449  * @class Roo.bootstrap.Alert
30450  * @extends Roo.bootstrap.Component
30451  * Bootstrap Alert class - shows an alert area box
30452  * eg
30453  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30454   Enter a valid email address
30455 </div>
30456  * @licence LGPL
30457  * @cfg {String} title The title of alert
30458  * @cfg {String} html The content of alert
30459  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30460  * @cfg {String} fa font-awesomeicon
30461  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30462  * @cfg {Boolean} close true to show a x closer
30463  * 
30464  * 
30465  * @constructor
30466  * Create a new alert
30467  * @param {Object} config The config object
30468  */
30469
30470
30471 Roo.bootstrap.Alert = function(config){
30472     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30473     
30474 };
30475
30476 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30477     
30478     title: '',
30479     html: '',
30480     weight: false,
30481     fa: false,
30482     faicon: false, // BC
30483     close : false,
30484     
30485     
30486     getAutoCreate : function()
30487     {
30488         
30489         var cfg = {
30490             tag : 'div',
30491             cls : 'alert',
30492             cn : [
30493                 {
30494                     tag: 'button',
30495                     type :  "button",
30496                     cls: "close",
30497                     html : '×',
30498                     style : this.close ? '' : 'display:none'
30499                 },
30500                 {
30501                     tag : 'i',
30502                     cls : 'roo-alert-icon'
30503                     
30504                 },
30505                 {
30506                     tag : 'b',
30507                     cls : 'roo-alert-title',
30508                     html : this.title
30509                 },
30510                 {
30511                     tag : 'span',
30512                     cls : 'roo-alert-text',
30513                     html : this.html
30514                 }
30515             ]
30516         };
30517         
30518         if(this.faicon){
30519             cfg.cn[0].cls += ' fa ' + this.faicon;
30520         }
30521         if(this.fa){
30522             cfg.cn[0].cls += ' fa ' + this.fa;
30523         }
30524         
30525         if(this.weight){
30526             cfg.cls += ' alert-' + this.weight;
30527         }
30528         
30529         return cfg;
30530     },
30531     
30532     initEvents: function() 
30533     {
30534         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30535         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30536         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30537         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30538         if (this.seconds > 0) {
30539             this.hide.defer(this.seconds, this);
30540         }
30541     },
30542     /**
30543      * Set the Title Message HTML
30544      * @param {String} html
30545      */
30546     setTitle : function(str)
30547     {
30548         this.titleEl.dom.innerHTML = str;
30549     },
30550      
30551      /**
30552      * Set the Body Message HTML
30553      * @param {String} html
30554      */
30555     setHtml : function(str)
30556     {
30557         this.htmlEl.dom.innerHTML = str;
30558     },
30559     /**
30560      * Set the Weight of the alert
30561      * @param {String} (success|info|warning|danger) weight
30562      */
30563     
30564     setWeight : function(weight)
30565     {
30566         if(this.weight){
30567             this.el.removeClass('alert-' + this.weight);
30568         }
30569         
30570         this.weight = weight;
30571         
30572         this.el.addClass('alert-' + this.weight);
30573     },
30574       /**
30575      * Set the Icon of the alert
30576      * @param {String} see fontawsome names (name without the 'fa-' bit)
30577      */
30578     setIcon : function(icon)
30579     {
30580         if(this.faicon){
30581             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30582         }
30583         
30584         this.faicon = icon;
30585         
30586         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30587     },
30588     /**
30589      * Hide the Alert
30590      */
30591     hide: function() 
30592     {
30593         this.el.hide();   
30594     },
30595     /**
30596      * Show the Alert
30597      */
30598     show: function() 
30599     {  
30600         this.el.show();   
30601     }
30602     
30603 });
30604
30605  
30606 /*
30607 * Licence: LGPL
30608 */
30609
30610 /**
30611  * @class Roo.bootstrap.UploadCropbox
30612  * @extends Roo.bootstrap.Component
30613  * Bootstrap UploadCropbox class
30614  * @cfg {String} emptyText show when image has been loaded
30615  * @cfg {String} rotateNotify show when image too small to rotate
30616  * @cfg {Number} errorTimeout default 3000
30617  * @cfg {Number} minWidth default 300
30618  * @cfg {Number} minHeight default 300
30619  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30620  * @cfg {Boolean} isDocument (true|false) default false
30621  * @cfg {String} url action url
30622  * @cfg {String} paramName default 'imageUpload'
30623  * @cfg {String} method default POST
30624  * @cfg {Boolean} loadMask (true|false) default true
30625  * @cfg {Boolean} loadingText default 'Loading...'
30626  * 
30627  * @constructor
30628  * Create a new UploadCropbox
30629  * @param {Object} config The config object
30630  */
30631
30632 Roo.bootstrap.UploadCropbox = function(config){
30633     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30634     
30635     this.addEvents({
30636         /**
30637          * @event beforeselectfile
30638          * Fire before select file
30639          * @param {Roo.bootstrap.UploadCropbox} this
30640          */
30641         "beforeselectfile" : true,
30642         /**
30643          * @event initial
30644          * Fire after initEvent
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          */
30647         "initial" : true,
30648         /**
30649          * @event crop
30650          * Fire after initEvent
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          * @param {String} data
30653          */
30654         "crop" : true,
30655         /**
30656          * @event prepare
30657          * Fire when preparing the file data
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          * @param {Object} file
30660          */
30661         "prepare" : true,
30662         /**
30663          * @event exception
30664          * Fire when get exception
30665          * @param {Roo.bootstrap.UploadCropbox} this
30666          * @param {XMLHttpRequest} xhr
30667          */
30668         "exception" : true,
30669         /**
30670          * @event beforeloadcanvas
30671          * Fire before load the canvas
30672          * @param {Roo.bootstrap.UploadCropbox} this
30673          * @param {String} src
30674          */
30675         "beforeloadcanvas" : true,
30676         /**
30677          * @event trash
30678          * Fire when trash image
30679          * @param {Roo.bootstrap.UploadCropbox} this
30680          */
30681         "trash" : true,
30682         /**
30683          * @event download
30684          * Fire when download the image
30685          * @param {Roo.bootstrap.UploadCropbox} this
30686          */
30687         "download" : true,
30688         /**
30689          * @event footerbuttonclick
30690          * Fire when footerbuttonclick
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          * @param {String} type
30693          */
30694         "footerbuttonclick" : true,
30695         /**
30696          * @event resize
30697          * Fire when resize
30698          * @param {Roo.bootstrap.UploadCropbox} this
30699          */
30700         "resize" : true,
30701         /**
30702          * @event rotate
30703          * Fire when rotate the image
30704          * @param {Roo.bootstrap.UploadCropbox} this
30705          * @param {String} pos
30706          */
30707         "rotate" : true,
30708         /**
30709          * @event inspect
30710          * Fire when inspect the file
30711          * @param {Roo.bootstrap.UploadCropbox} this
30712          * @param {Object} file
30713          */
30714         "inspect" : true,
30715         /**
30716          * @event upload
30717          * Fire when xhr upload the file
30718          * @param {Roo.bootstrap.UploadCropbox} this
30719          * @param {Object} data
30720          */
30721         "upload" : true,
30722         /**
30723          * @event arrange
30724          * Fire when arrange the file data
30725          * @param {Roo.bootstrap.UploadCropbox} this
30726          * @param {Object} formData
30727          */
30728         "arrange" : true
30729     });
30730     
30731     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30732 };
30733
30734 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30735     
30736     emptyText : 'Click to upload image',
30737     rotateNotify : 'Image is too small to rotate',
30738     errorTimeout : 3000,
30739     scale : 0,
30740     baseScale : 1,
30741     rotate : 0,
30742     dragable : false,
30743     pinching : false,
30744     mouseX : 0,
30745     mouseY : 0,
30746     cropData : false,
30747     minWidth : 300,
30748     minHeight : 300,
30749     file : false,
30750     exif : {},
30751     baseRotate : 1,
30752     cropType : 'image/jpeg',
30753     buttons : false,
30754     canvasLoaded : false,
30755     isDocument : false,
30756     method : 'POST',
30757     paramName : 'imageUpload',
30758     loadMask : true,
30759     loadingText : 'Loading...',
30760     maskEl : false,
30761     
30762     getAutoCreate : function()
30763     {
30764         var cfg = {
30765             tag : 'div',
30766             cls : 'roo-upload-cropbox',
30767             cn : [
30768                 {
30769                     tag : 'input',
30770                     cls : 'roo-upload-cropbox-selector',
30771                     type : 'file'
30772                 },
30773                 {
30774                     tag : 'div',
30775                     cls : 'roo-upload-cropbox-body',
30776                     style : 'cursor:pointer',
30777                     cn : [
30778                         {
30779                             tag : 'div',
30780                             cls : 'roo-upload-cropbox-preview'
30781                         },
30782                         {
30783                             tag : 'div',
30784                             cls : 'roo-upload-cropbox-thumb'
30785                         },
30786                         {
30787                             tag : 'div',
30788                             cls : 'roo-upload-cropbox-empty-notify',
30789                             html : this.emptyText
30790                         },
30791                         {
30792                             tag : 'div',
30793                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30794                             html : this.rotateNotify
30795                         }
30796                     ]
30797                 },
30798                 {
30799                     tag : 'div',
30800                     cls : 'roo-upload-cropbox-footer',
30801                     cn : {
30802                         tag : 'div',
30803                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30804                         cn : []
30805                     }
30806                 }
30807             ]
30808         };
30809         
30810         return cfg;
30811     },
30812     
30813     onRender : function(ct, position)
30814     {
30815         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30816         
30817         if (this.buttons.length) {
30818             
30819             Roo.each(this.buttons, function(bb) {
30820                 
30821                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30822                 
30823                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30824                 
30825             }, this);
30826         }
30827         
30828         if(this.loadMask){
30829             this.maskEl = this.el;
30830         }
30831     },
30832     
30833     initEvents : function()
30834     {
30835         this.urlAPI = (window.createObjectURL && window) || 
30836                                 (window.URL && URL.revokeObjectURL && URL) || 
30837                                 (window.webkitURL && webkitURL);
30838                         
30839         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30840         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30841         
30842         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30843         this.selectorEl.hide();
30844         
30845         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30846         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30847         
30848         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30849         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30850         this.thumbEl.hide();
30851         
30852         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30853         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30854         
30855         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30856         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30857         this.errorEl.hide();
30858         
30859         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30860         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30861         this.footerEl.hide();
30862         
30863         this.setThumbBoxSize();
30864         
30865         this.bind();
30866         
30867         this.resize();
30868         
30869         this.fireEvent('initial', this);
30870     },
30871
30872     bind : function()
30873     {
30874         var _this = this;
30875         
30876         window.addEventListener("resize", function() { _this.resize(); } );
30877         
30878         this.bodyEl.on('click', this.beforeSelectFile, this);
30879         
30880         if(Roo.isTouch){
30881             this.bodyEl.on('touchstart', this.onTouchStart, this);
30882             this.bodyEl.on('touchmove', this.onTouchMove, this);
30883             this.bodyEl.on('touchend', this.onTouchEnd, this);
30884         }
30885         
30886         if(!Roo.isTouch){
30887             this.bodyEl.on('mousedown', this.onMouseDown, this);
30888             this.bodyEl.on('mousemove', this.onMouseMove, this);
30889             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30890             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30891             Roo.get(document).on('mouseup', this.onMouseUp, this);
30892         }
30893         
30894         this.selectorEl.on('change', this.onFileSelected, this);
30895     },
30896     
30897     reset : function()
30898     {    
30899         this.scale = 0;
30900         this.baseScale = 1;
30901         this.rotate = 0;
30902         this.baseRotate = 1;
30903         this.dragable = false;
30904         this.pinching = false;
30905         this.mouseX = 0;
30906         this.mouseY = 0;
30907         this.cropData = false;
30908         this.notifyEl.dom.innerHTML = this.emptyText;
30909         
30910         this.selectorEl.dom.value = '';
30911         
30912     },
30913     
30914     resize : function()
30915     {
30916         if(this.fireEvent('resize', this) != false){
30917             this.setThumbBoxPosition();
30918             this.setCanvasPosition();
30919         }
30920     },
30921     
30922     onFooterButtonClick : function(e, el, o, type)
30923     {
30924         switch (type) {
30925             case 'rotate-left' :
30926                 this.onRotateLeft(e);
30927                 break;
30928             case 'rotate-right' :
30929                 this.onRotateRight(e);
30930                 break;
30931             case 'picture' :
30932                 this.beforeSelectFile(e);
30933                 break;
30934             case 'trash' :
30935                 this.trash(e);
30936                 break;
30937             case 'crop' :
30938                 this.crop(e);
30939                 break;
30940             case 'download' :
30941                 this.download(e);
30942                 break;
30943             default :
30944                 break;
30945         }
30946         
30947         this.fireEvent('footerbuttonclick', this, type);
30948     },
30949     
30950     beforeSelectFile : function(e)
30951     {
30952         e.preventDefault();
30953         
30954         if(this.fireEvent('beforeselectfile', this) != false){
30955             this.selectorEl.dom.click();
30956         }
30957     },
30958     
30959     onFileSelected : function(e)
30960     {
30961         e.preventDefault();
30962         
30963         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30964             return;
30965         }
30966         
30967         var file = this.selectorEl.dom.files[0];
30968         
30969         if(this.fireEvent('inspect', this, file) != false){
30970             this.prepare(file);
30971         }
30972         
30973     },
30974     
30975     trash : function(e)
30976     {
30977         this.fireEvent('trash', this);
30978     },
30979     
30980     download : function(e)
30981     {
30982         this.fireEvent('download', this);
30983     },
30984     
30985     loadCanvas : function(src)
30986     {   
30987         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30988             
30989             this.reset();
30990             
30991             this.imageEl = document.createElement('img');
30992             
30993             var _this = this;
30994             
30995             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30996             
30997             this.imageEl.src = src;
30998         }
30999     },
31000     
31001     onLoadCanvas : function()
31002     {   
31003         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31004         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31005         
31006         this.bodyEl.un('click', this.beforeSelectFile, this);
31007         
31008         this.notifyEl.hide();
31009         this.thumbEl.show();
31010         this.footerEl.show();
31011         
31012         this.baseRotateLevel();
31013         
31014         if(this.isDocument){
31015             this.setThumbBoxSize();
31016         }
31017         
31018         this.setThumbBoxPosition();
31019         
31020         this.baseScaleLevel();
31021         
31022         this.draw();
31023         
31024         this.resize();
31025         
31026         this.canvasLoaded = true;
31027         
31028         if(this.loadMask){
31029             this.maskEl.unmask();
31030         }
31031         
31032     },
31033     
31034     setCanvasPosition : function()
31035     {   
31036         if(!this.canvasEl){
31037             return;
31038         }
31039         
31040         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31041         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31042         
31043         this.previewEl.setLeft(pw);
31044         this.previewEl.setTop(ph);
31045         
31046     },
31047     
31048     onMouseDown : function(e)
31049     {   
31050         e.stopEvent();
31051         
31052         this.dragable = true;
31053         this.pinching = false;
31054         
31055         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31056             this.dragable = false;
31057             return;
31058         }
31059         
31060         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31061         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31062         
31063     },
31064     
31065     onMouseMove : function(e)
31066     {   
31067         e.stopEvent();
31068         
31069         if(!this.canvasLoaded){
31070             return;
31071         }
31072         
31073         if (!this.dragable){
31074             return;
31075         }
31076         
31077         var minX = Math.ceil(this.thumbEl.getLeft(true));
31078         var minY = Math.ceil(this.thumbEl.getTop(true));
31079         
31080         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31081         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31082         
31083         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31084         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31085         
31086         x = x - this.mouseX;
31087         y = y - this.mouseY;
31088         
31089         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31090         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31091         
31092         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31093         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31094         
31095         this.previewEl.setLeft(bgX);
31096         this.previewEl.setTop(bgY);
31097         
31098         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31099         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31100     },
31101     
31102     onMouseUp : function(e)
31103     {   
31104         e.stopEvent();
31105         
31106         this.dragable = false;
31107     },
31108     
31109     onMouseWheel : function(e)
31110     {   
31111         e.stopEvent();
31112         
31113         this.startScale = this.scale;
31114         
31115         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31116         
31117         if(!this.zoomable()){
31118             this.scale = this.startScale;
31119             return;
31120         }
31121         
31122         this.draw();
31123         
31124         return;
31125     },
31126     
31127     zoomable : function()
31128     {
31129         var minScale = this.thumbEl.getWidth() / this.minWidth;
31130         
31131         if(this.minWidth < this.minHeight){
31132             minScale = this.thumbEl.getHeight() / this.minHeight;
31133         }
31134         
31135         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31136         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31137         
31138         if(
31139                 this.isDocument &&
31140                 (this.rotate == 0 || this.rotate == 180) && 
31141                 (
31142                     width > this.imageEl.OriginWidth || 
31143                     height > this.imageEl.OriginHeight ||
31144                     (width < this.minWidth && height < this.minHeight)
31145                 )
31146         ){
31147             return false;
31148         }
31149         
31150         if(
31151                 this.isDocument &&
31152                 (this.rotate == 90 || this.rotate == 270) && 
31153                 (
31154                     width > this.imageEl.OriginWidth || 
31155                     height > this.imageEl.OriginHeight ||
31156                     (width < this.minHeight && height < this.minWidth)
31157                 )
31158         ){
31159             return false;
31160         }
31161         
31162         if(
31163                 !this.isDocument &&
31164                 (this.rotate == 0 || this.rotate == 180) && 
31165                 (
31166                     width < this.minWidth || 
31167                     width > this.imageEl.OriginWidth || 
31168                     height < this.minHeight || 
31169                     height > this.imageEl.OriginHeight
31170                 )
31171         ){
31172             return false;
31173         }
31174         
31175         if(
31176                 !this.isDocument &&
31177                 (this.rotate == 90 || this.rotate == 270) && 
31178                 (
31179                     width < this.minHeight || 
31180                     width > this.imageEl.OriginWidth || 
31181                     height < this.minWidth || 
31182                     height > this.imageEl.OriginHeight
31183                 )
31184         ){
31185             return false;
31186         }
31187         
31188         return true;
31189         
31190     },
31191     
31192     onRotateLeft : function(e)
31193     {   
31194         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31195             
31196             var minScale = this.thumbEl.getWidth() / this.minWidth;
31197             
31198             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31199             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31200             
31201             this.startScale = this.scale;
31202             
31203             while (this.getScaleLevel() < minScale){
31204             
31205                 this.scale = this.scale + 1;
31206                 
31207                 if(!this.zoomable()){
31208                     break;
31209                 }
31210                 
31211                 if(
31212                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31213                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31214                 ){
31215                     continue;
31216                 }
31217                 
31218                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31219
31220                 this.draw();
31221                 
31222                 return;
31223             }
31224             
31225             this.scale = this.startScale;
31226             
31227             this.onRotateFail();
31228             
31229             return false;
31230         }
31231         
31232         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31233
31234         if(this.isDocument){
31235             this.setThumbBoxSize();
31236             this.setThumbBoxPosition();
31237             this.setCanvasPosition();
31238         }
31239         
31240         this.draw();
31241         
31242         this.fireEvent('rotate', this, 'left');
31243         
31244     },
31245     
31246     onRotateRight : function(e)
31247     {
31248         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31249             
31250             var minScale = this.thumbEl.getWidth() / this.minWidth;
31251         
31252             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31253             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31254             
31255             this.startScale = this.scale;
31256             
31257             while (this.getScaleLevel() < minScale){
31258             
31259                 this.scale = this.scale + 1;
31260                 
31261                 if(!this.zoomable()){
31262                     break;
31263                 }
31264                 
31265                 if(
31266                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31267                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31268                 ){
31269                     continue;
31270                 }
31271                 
31272                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31273
31274                 this.draw();
31275                 
31276                 return;
31277             }
31278             
31279             this.scale = this.startScale;
31280             
31281             this.onRotateFail();
31282             
31283             return false;
31284         }
31285         
31286         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31287
31288         if(this.isDocument){
31289             this.setThumbBoxSize();
31290             this.setThumbBoxPosition();
31291             this.setCanvasPosition();
31292         }
31293         
31294         this.draw();
31295         
31296         this.fireEvent('rotate', this, 'right');
31297     },
31298     
31299     onRotateFail : function()
31300     {
31301         this.errorEl.show(true);
31302         
31303         var _this = this;
31304         
31305         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31306     },
31307     
31308     draw : function()
31309     {
31310         this.previewEl.dom.innerHTML = '';
31311         
31312         var canvasEl = document.createElement("canvas");
31313         
31314         var contextEl = canvasEl.getContext("2d");
31315         
31316         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31317         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31318         var center = this.imageEl.OriginWidth / 2;
31319         
31320         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31321             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31322             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31323             center = this.imageEl.OriginHeight / 2;
31324         }
31325         
31326         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31327         
31328         contextEl.translate(center, center);
31329         contextEl.rotate(this.rotate * Math.PI / 180);
31330
31331         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31332         
31333         this.canvasEl = document.createElement("canvas");
31334         
31335         this.contextEl = this.canvasEl.getContext("2d");
31336         
31337         switch (this.rotate) {
31338             case 0 :
31339                 
31340                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31341                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31342                 
31343                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31344                 
31345                 break;
31346             case 90 : 
31347                 
31348                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31349                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31350                 
31351                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31352                     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);
31353                     break;
31354                 }
31355                 
31356                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31357                 
31358                 break;
31359             case 180 :
31360                 
31361                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31362                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31363                 
31364                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365                     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);
31366                     break;
31367                 }
31368                 
31369                 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);
31370                 
31371                 break;
31372             case 270 :
31373                 
31374                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31375                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31376         
31377                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31378                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31379                     break;
31380                 }
31381                 
31382                 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);
31383                 
31384                 break;
31385             default : 
31386                 break;
31387         }
31388         
31389         this.previewEl.appendChild(this.canvasEl);
31390         
31391         this.setCanvasPosition();
31392     },
31393     
31394     crop : function()
31395     {
31396         if(!this.canvasLoaded){
31397             return;
31398         }
31399         
31400         var imageCanvas = document.createElement("canvas");
31401         
31402         var imageContext = imageCanvas.getContext("2d");
31403         
31404         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31405         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31406         
31407         var center = imageCanvas.width / 2;
31408         
31409         imageContext.translate(center, center);
31410         
31411         imageContext.rotate(this.rotate * Math.PI / 180);
31412         
31413         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31414         
31415         var canvas = document.createElement("canvas");
31416         
31417         var context = canvas.getContext("2d");
31418                 
31419         canvas.width = this.minWidth;
31420         canvas.height = this.minHeight;
31421
31422         switch (this.rotate) {
31423             case 0 :
31424                 
31425                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31426                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31427                 
31428                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31429                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31430                 
31431                 var targetWidth = this.minWidth - 2 * x;
31432                 var targetHeight = this.minHeight - 2 * y;
31433                 
31434                 var scale = 1;
31435                 
31436                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31437                     scale = targetWidth / width;
31438                 }
31439                 
31440                 if(x > 0 && y == 0){
31441                     scale = targetHeight / height;
31442                 }
31443                 
31444                 if(x > 0 && y > 0){
31445                     scale = targetWidth / width;
31446                     
31447                     if(width < height){
31448                         scale = targetHeight / height;
31449                     }
31450                 }
31451                 
31452                 context.scale(scale, scale);
31453                 
31454                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31455                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31456
31457                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31458                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31459
31460                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31461                 
31462                 break;
31463             case 90 : 
31464                 
31465                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31466                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31467                 
31468                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31469                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31470                 
31471                 var targetWidth = this.minWidth - 2 * x;
31472                 var targetHeight = this.minHeight - 2 * y;
31473                 
31474                 var scale = 1;
31475                 
31476                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31477                     scale = targetWidth / width;
31478                 }
31479                 
31480                 if(x > 0 && y == 0){
31481                     scale = targetHeight / height;
31482                 }
31483                 
31484                 if(x > 0 && y > 0){
31485                     scale = targetWidth / width;
31486                     
31487                     if(width < height){
31488                         scale = targetHeight / height;
31489                     }
31490                 }
31491                 
31492                 context.scale(scale, scale);
31493                 
31494                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31495                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31496
31497                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31498                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31499                 
31500                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31501                 
31502                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31503                 
31504                 break;
31505             case 180 :
31506                 
31507                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31508                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31509                 
31510                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31511                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31512                 
31513                 var targetWidth = this.minWidth - 2 * x;
31514                 var targetHeight = this.minHeight - 2 * y;
31515                 
31516                 var scale = 1;
31517                 
31518                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31519                     scale = targetWidth / width;
31520                 }
31521                 
31522                 if(x > 0 && y == 0){
31523                     scale = targetHeight / height;
31524                 }
31525                 
31526                 if(x > 0 && y > 0){
31527                     scale = targetWidth / width;
31528                     
31529                     if(width < height){
31530                         scale = targetHeight / height;
31531                     }
31532                 }
31533                 
31534                 context.scale(scale, scale);
31535                 
31536                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31537                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31538
31539                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31540                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31541
31542                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31543                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31544                 
31545                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31546                 
31547                 break;
31548             case 270 :
31549                 
31550                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31551                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31552                 
31553                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31554                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31555                 
31556                 var targetWidth = this.minWidth - 2 * x;
31557                 var targetHeight = this.minHeight - 2 * y;
31558                 
31559                 var scale = 1;
31560                 
31561                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31562                     scale = targetWidth / width;
31563                 }
31564                 
31565                 if(x > 0 && y == 0){
31566                     scale = targetHeight / height;
31567                 }
31568                 
31569                 if(x > 0 && y > 0){
31570                     scale = targetWidth / width;
31571                     
31572                     if(width < height){
31573                         scale = targetHeight / height;
31574                     }
31575                 }
31576                 
31577                 context.scale(scale, scale);
31578                 
31579                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31580                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31581
31582                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31583                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31584                 
31585                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31586                 
31587                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31588                 
31589                 break;
31590             default : 
31591                 break;
31592         }
31593         
31594         this.cropData = canvas.toDataURL(this.cropType);
31595         
31596         if(this.fireEvent('crop', this, this.cropData) !== false){
31597             this.process(this.file, this.cropData);
31598         }
31599         
31600         return;
31601         
31602     },
31603     
31604     setThumbBoxSize : function()
31605     {
31606         var width, height;
31607         
31608         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31609             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31610             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31611             
31612             this.minWidth = width;
31613             this.minHeight = height;
31614             
31615             if(this.rotate == 90 || this.rotate == 270){
31616                 this.minWidth = height;
31617                 this.minHeight = width;
31618             }
31619         }
31620         
31621         height = 300;
31622         width = Math.ceil(this.minWidth * height / this.minHeight);
31623         
31624         if(this.minWidth > this.minHeight){
31625             width = 300;
31626             height = Math.ceil(this.minHeight * width / this.minWidth);
31627         }
31628         
31629         this.thumbEl.setStyle({
31630             width : width + 'px',
31631             height : height + 'px'
31632         });
31633
31634         return;
31635             
31636     },
31637     
31638     setThumbBoxPosition : function()
31639     {
31640         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31641         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31642         
31643         this.thumbEl.setLeft(x);
31644         this.thumbEl.setTop(y);
31645         
31646     },
31647     
31648     baseRotateLevel : function()
31649     {
31650         this.baseRotate = 1;
31651         
31652         if(
31653                 typeof(this.exif) != 'undefined' &&
31654                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31655                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31656         ){
31657             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31658         }
31659         
31660         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31661         
31662     },
31663     
31664     baseScaleLevel : function()
31665     {
31666         var width, height;
31667         
31668         if(this.isDocument){
31669             
31670             if(this.baseRotate == 6 || this.baseRotate == 8){
31671             
31672                 height = this.thumbEl.getHeight();
31673                 this.baseScale = height / this.imageEl.OriginWidth;
31674
31675                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31676                     width = this.thumbEl.getWidth();
31677                     this.baseScale = width / this.imageEl.OriginHeight;
31678                 }
31679
31680                 return;
31681             }
31682
31683             height = this.thumbEl.getHeight();
31684             this.baseScale = height / this.imageEl.OriginHeight;
31685
31686             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31687                 width = this.thumbEl.getWidth();
31688                 this.baseScale = width / this.imageEl.OriginWidth;
31689             }
31690
31691             return;
31692         }
31693         
31694         if(this.baseRotate == 6 || this.baseRotate == 8){
31695             
31696             width = this.thumbEl.getHeight();
31697             this.baseScale = width / this.imageEl.OriginHeight;
31698             
31699             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31700                 height = this.thumbEl.getWidth();
31701                 this.baseScale = height / this.imageEl.OriginHeight;
31702             }
31703             
31704             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31705                 height = this.thumbEl.getWidth();
31706                 this.baseScale = height / this.imageEl.OriginHeight;
31707                 
31708                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31709                     width = this.thumbEl.getHeight();
31710                     this.baseScale = width / this.imageEl.OriginWidth;
31711                 }
31712             }
31713             
31714             return;
31715         }
31716         
31717         width = this.thumbEl.getWidth();
31718         this.baseScale = width / this.imageEl.OriginWidth;
31719         
31720         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31721             height = this.thumbEl.getHeight();
31722             this.baseScale = height / this.imageEl.OriginHeight;
31723         }
31724         
31725         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31726             
31727             height = this.thumbEl.getHeight();
31728             this.baseScale = height / this.imageEl.OriginHeight;
31729             
31730             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31731                 width = this.thumbEl.getWidth();
31732                 this.baseScale = width / this.imageEl.OriginWidth;
31733             }
31734             
31735         }
31736         
31737         return;
31738     },
31739     
31740     getScaleLevel : function()
31741     {
31742         return this.baseScale * Math.pow(1.1, this.scale);
31743     },
31744     
31745     onTouchStart : function(e)
31746     {
31747         if(!this.canvasLoaded){
31748             this.beforeSelectFile(e);
31749             return;
31750         }
31751         
31752         var touches = e.browserEvent.touches;
31753         
31754         if(!touches){
31755             return;
31756         }
31757         
31758         if(touches.length == 1){
31759             this.onMouseDown(e);
31760             return;
31761         }
31762         
31763         if(touches.length != 2){
31764             return;
31765         }
31766         
31767         var coords = [];
31768         
31769         for(var i = 0, finger; finger = touches[i]; i++){
31770             coords.push(finger.pageX, finger.pageY);
31771         }
31772         
31773         var x = Math.pow(coords[0] - coords[2], 2);
31774         var y = Math.pow(coords[1] - coords[3], 2);
31775         
31776         this.startDistance = Math.sqrt(x + y);
31777         
31778         this.startScale = this.scale;
31779         
31780         this.pinching = true;
31781         this.dragable = false;
31782         
31783     },
31784     
31785     onTouchMove : function(e)
31786     {
31787         if(!this.pinching && !this.dragable){
31788             return;
31789         }
31790         
31791         var touches = e.browserEvent.touches;
31792         
31793         if(!touches){
31794             return;
31795         }
31796         
31797         if(this.dragable){
31798             this.onMouseMove(e);
31799             return;
31800         }
31801         
31802         var coords = [];
31803         
31804         for(var i = 0, finger; finger = touches[i]; i++){
31805             coords.push(finger.pageX, finger.pageY);
31806         }
31807         
31808         var x = Math.pow(coords[0] - coords[2], 2);
31809         var y = Math.pow(coords[1] - coords[3], 2);
31810         
31811         this.endDistance = Math.sqrt(x + y);
31812         
31813         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31814         
31815         if(!this.zoomable()){
31816             this.scale = this.startScale;
31817             return;
31818         }
31819         
31820         this.draw();
31821         
31822     },
31823     
31824     onTouchEnd : function(e)
31825     {
31826         this.pinching = false;
31827         this.dragable = false;
31828         
31829     },
31830     
31831     process : function(file, crop)
31832     {
31833         if(this.loadMask){
31834             this.maskEl.mask(this.loadingText);
31835         }
31836         
31837         this.xhr = new XMLHttpRequest();
31838         
31839         file.xhr = this.xhr;
31840
31841         this.xhr.open(this.method, this.url, true);
31842         
31843         var headers = {
31844             "Accept": "application/json",
31845             "Cache-Control": "no-cache",
31846             "X-Requested-With": "XMLHttpRequest"
31847         };
31848         
31849         for (var headerName in headers) {
31850             var headerValue = headers[headerName];
31851             if (headerValue) {
31852                 this.xhr.setRequestHeader(headerName, headerValue);
31853             }
31854         }
31855         
31856         var _this = this;
31857         
31858         this.xhr.onload = function()
31859         {
31860             _this.xhrOnLoad(_this.xhr);
31861         }
31862         
31863         this.xhr.onerror = function()
31864         {
31865             _this.xhrOnError(_this.xhr);
31866         }
31867         
31868         var formData = new FormData();
31869
31870         formData.append('returnHTML', 'NO');
31871         
31872         if(crop){
31873             formData.append('crop', crop);
31874         }
31875         
31876         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31877             formData.append(this.paramName, file, file.name);
31878         }
31879         
31880         if(typeof(file.filename) != 'undefined'){
31881             formData.append('filename', file.filename);
31882         }
31883         
31884         if(typeof(file.mimetype) != 'undefined'){
31885             formData.append('mimetype', file.mimetype);
31886         }
31887         
31888         if(this.fireEvent('arrange', this, formData) != false){
31889             this.xhr.send(formData);
31890         };
31891     },
31892     
31893     xhrOnLoad : function(xhr)
31894     {
31895         if(this.loadMask){
31896             this.maskEl.unmask();
31897         }
31898         
31899         if (xhr.readyState !== 4) {
31900             this.fireEvent('exception', this, xhr);
31901             return;
31902         }
31903
31904         var response = Roo.decode(xhr.responseText);
31905         
31906         if(!response.success){
31907             this.fireEvent('exception', this, xhr);
31908             return;
31909         }
31910         
31911         var response = Roo.decode(xhr.responseText);
31912         
31913         this.fireEvent('upload', this, response);
31914         
31915     },
31916     
31917     xhrOnError : function()
31918     {
31919         if(this.loadMask){
31920             this.maskEl.unmask();
31921         }
31922         
31923         Roo.log('xhr on error');
31924         
31925         var response = Roo.decode(xhr.responseText);
31926           
31927         Roo.log(response);
31928         
31929     },
31930     
31931     prepare : function(file)
31932     {   
31933         if(this.loadMask){
31934             this.maskEl.mask(this.loadingText);
31935         }
31936         
31937         this.file = false;
31938         this.exif = {};
31939         
31940         if(typeof(file) === 'string'){
31941             this.loadCanvas(file);
31942             return;
31943         }
31944         
31945         if(!file || !this.urlAPI){
31946             return;
31947         }
31948         
31949         this.file = file;
31950         this.cropType = file.type;
31951         
31952         var _this = this;
31953         
31954         if(this.fireEvent('prepare', this, this.file) != false){
31955             
31956             var reader = new FileReader();
31957             
31958             reader.onload = function (e) {
31959                 if (e.target.error) {
31960                     Roo.log(e.target.error);
31961                     return;
31962                 }
31963                 
31964                 var buffer = e.target.result,
31965                     dataView = new DataView(buffer),
31966                     offset = 2,
31967                     maxOffset = dataView.byteLength - 4,
31968                     markerBytes,
31969                     markerLength;
31970                 
31971                 if (dataView.getUint16(0) === 0xffd8) {
31972                     while (offset < maxOffset) {
31973                         markerBytes = dataView.getUint16(offset);
31974                         
31975                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31976                             markerLength = dataView.getUint16(offset + 2) + 2;
31977                             if (offset + markerLength > dataView.byteLength) {
31978                                 Roo.log('Invalid meta data: Invalid segment size.');
31979                                 break;
31980                             }
31981                             
31982                             if(markerBytes == 0xffe1){
31983                                 _this.parseExifData(
31984                                     dataView,
31985                                     offset,
31986                                     markerLength
31987                                 );
31988                             }
31989                             
31990                             offset += markerLength;
31991                             
31992                             continue;
31993                         }
31994                         
31995                         break;
31996                     }
31997                     
31998                 }
31999                 
32000                 var url = _this.urlAPI.createObjectURL(_this.file);
32001                 
32002                 _this.loadCanvas(url);
32003                 
32004                 return;
32005             }
32006             
32007             reader.readAsArrayBuffer(this.file);
32008             
32009         }
32010         
32011     },
32012     
32013     parseExifData : function(dataView, offset, length)
32014     {
32015         var tiffOffset = offset + 10,
32016             littleEndian,
32017             dirOffset;
32018     
32019         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32020             // No Exif data, might be XMP data instead
32021             return;
32022         }
32023         
32024         // Check for the ASCII code for "Exif" (0x45786966):
32025         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32026             // No Exif data, might be XMP data instead
32027             return;
32028         }
32029         if (tiffOffset + 8 > dataView.byteLength) {
32030             Roo.log('Invalid Exif data: Invalid segment size.');
32031             return;
32032         }
32033         // Check for the two null bytes:
32034         if (dataView.getUint16(offset + 8) !== 0x0000) {
32035             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32036             return;
32037         }
32038         // Check the byte alignment:
32039         switch (dataView.getUint16(tiffOffset)) {
32040         case 0x4949:
32041             littleEndian = true;
32042             break;
32043         case 0x4D4D:
32044             littleEndian = false;
32045             break;
32046         default:
32047             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32048             return;
32049         }
32050         // Check for the TIFF tag marker (0x002A):
32051         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32052             Roo.log('Invalid Exif data: Missing TIFF marker.');
32053             return;
32054         }
32055         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32056         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32057         
32058         this.parseExifTags(
32059             dataView,
32060             tiffOffset,
32061             tiffOffset + dirOffset,
32062             littleEndian
32063         );
32064     },
32065     
32066     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32067     {
32068         var tagsNumber,
32069             dirEndOffset,
32070             i;
32071         if (dirOffset + 6 > dataView.byteLength) {
32072             Roo.log('Invalid Exif data: Invalid directory offset.');
32073             return;
32074         }
32075         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32076         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32077         if (dirEndOffset + 4 > dataView.byteLength) {
32078             Roo.log('Invalid Exif data: Invalid directory size.');
32079             return;
32080         }
32081         for (i = 0; i < tagsNumber; i += 1) {
32082             this.parseExifTag(
32083                 dataView,
32084                 tiffOffset,
32085                 dirOffset + 2 + 12 * i, // tag offset
32086                 littleEndian
32087             );
32088         }
32089         // Return the offset to the next directory:
32090         return dataView.getUint32(dirEndOffset, littleEndian);
32091     },
32092     
32093     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32094     {
32095         var tag = dataView.getUint16(offset, littleEndian);
32096         
32097         this.exif[tag] = this.getExifValue(
32098             dataView,
32099             tiffOffset,
32100             offset,
32101             dataView.getUint16(offset + 2, littleEndian), // tag type
32102             dataView.getUint32(offset + 4, littleEndian), // tag length
32103             littleEndian
32104         );
32105     },
32106     
32107     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32108     {
32109         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32110             tagSize,
32111             dataOffset,
32112             values,
32113             i,
32114             str,
32115             c;
32116     
32117         if (!tagType) {
32118             Roo.log('Invalid Exif data: Invalid tag type.');
32119             return;
32120         }
32121         
32122         tagSize = tagType.size * length;
32123         // Determine if the value is contained in the dataOffset bytes,
32124         // or if the value at the dataOffset is a pointer to the actual data:
32125         dataOffset = tagSize > 4 ?
32126                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32127         if (dataOffset + tagSize > dataView.byteLength) {
32128             Roo.log('Invalid Exif data: Invalid data offset.');
32129             return;
32130         }
32131         if (length === 1) {
32132             return tagType.getValue(dataView, dataOffset, littleEndian);
32133         }
32134         values = [];
32135         for (i = 0; i < length; i += 1) {
32136             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32137         }
32138         
32139         if (tagType.ascii) {
32140             str = '';
32141             // Concatenate the chars:
32142             for (i = 0; i < values.length; i += 1) {
32143                 c = values[i];
32144                 // Ignore the terminating NULL byte(s):
32145                 if (c === '\u0000') {
32146                     break;
32147                 }
32148                 str += c;
32149             }
32150             return str;
32151         }
32152         return values;
32153     }
32154     
32155 });
32156
32157 Roo.apply(Roo.bootstrap.UploadCropbox, {
32158     tags : {
32159         'Orientation': 0x0112
32160     },
32161     
32162     Orientation: {
32163             1: 0, //'top-left',
32164 //            2: 'top-right',
32165             3: 180, //'bottom-right',
32166 //            4: 'bottom-left',
32167 //            5: 'left-top',
32168             6: 90, //'right-top',
32169 //            7: 'right-bottom',
32170             8: 270 //'left-bottom'
32171     },
32172     
32173     exifTagTypes : {
32174         // byte, 8-bit unsigned int:
32175         1: {
32176             getValue: function (dataView, dataOffset) {
32177                 return dataView.getUint8(dataOffset);
32178             },
32179             size: 1
32180         },
32181         // ascii, 8-bit byte:
32182         2: {
32183             getValue: function (dataView, dataOffset) {
32184                 return String.fromCharCode(dataView.getUint8(dataOffset));
32185             },
32186             size: 1,
32187             ascii: true
32188         },
32189         // short, 16 bit int:
32190         3: {
32191             getValue: function (dataView, dataOffset, littleEndian) {
32192                 return dataView.getUint16(dataOffset, littleEndian);
32193             },
32194             size: 2
32195         },
32196         // long, 32 bit int:
32197         4: {
32198             getValue: function (dataView, dataOffset, littleEndian) {
32199                 return dataView.getUint32(dataOffset, littleEndian);
32200             },
32201             size: 4
32202         },
32203         // rational = two long values, first is numerator, second is denominator:
32204         5: {
32205             getValue: function (dataView, dataOffset, littleEndian) {
32206                 return dataView.getUint32(dataOffset, littleEndian) /
32207                     dataView.getUint32(dataOffset + 4, littleEndian);
32208             },
32209             size: 8
32210         },
32211         // slong, 32 bit signed int:
32212         9: {
32213             getValue: function (dataView, dataOffset, littleEndian) {
32214                 return dataView.getInt32(dataOffset, littleEndian);
32215             },
32216             size: 4
32217         },
32218         // srational, two slongs, first is numerator, second is denominator:
32219         10: {
32220             getValue: function (dataView, dataOffset, littleEndian) {
32221                 return dataView.getInt32(dataOffset, littleEndian) /
32222                     dataView.getInt32(dataOffset + 4, littleEndian);
32223             },
32224             size: 8
32225         }
32226     },
32227     
32228     footer : {
32229         STANDARD : [
32230             {
32231                 tag : 'div',
32232                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32233                 action : 'rotate-left',
32234                 cn : [
32235                     {
32236                         tag : 'button',
32237                         cls : 'btn btn-default',
32238                         html : '<i class="fa fa-undo"></i>'
32239                     }
32240                 ]
32241             },
32242             {
32243                 tag : 'div',
32244                 cls : 'btn-group roo-upload-cropbox-picture',
32245                 action : 'picture',
32246                 cn : [
32247                     {
32248                         tag : 'button',
32249                         cls : 'btn btn-default',
32250                         html : '<i class="fa fa-picture-o"></i>'
32251                     }
32252                 ]
32253             },
32254             {
32255                 tag : 'div',
32256                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32257                 action : 'rotate-right',
32258                 cn : [
32259                     {
32260                         tag : 'button',
32261                         cls : 'btn btn-default',
32262                         html : '<i class="fa fa-repeat"></i>'
32263                     }
32264                 ]
32265             }
32266         ],
32267         DOCUMENT : [
32268             {
32269                 tag : 'div',
32270                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32271                 action : 'rotate-left',
32272                 cn : [
32273                     {
32274                         tag : 'button',
32275                         cls : 'btn btn-default',
32276                         html : '<i class="fa fa-undo"></i>'
32277                     }
32278                 ]
32279             },
32280             {
32281                 tag : 'div',
32282                 cls : 'btn-group roo-upload-cropbox-download',
32283                 action : 'download',
32284                 cn : [
32285                     {
32286                         tag : 'button',
32287                         cls : 'btn btn-default',
32288                         html : '<i class="fa fa-download"></i>'
32289                     }
32290                 ]
32291             },
32292             {
32293                 tag : 'div',
32294                 cls : 'btn-group roo-upload-cropbox-crop',
32295                 action : 'crop',
32296                 cn : [
32297                     {
32298                         tag : 'button',
32299                         cls : 'btn btn-default',
32300                         html : '<i class="fa fa-crop"></i>'
32301                     }
32302                 ]
32303             },
32304             {
32305                 tag : 'div',
32306                 cls : 'btn-group roo-upload-cropbox-trash',
32307                 action : 'trash',
32308                 cn : [
32309                     {
32310                         tag : 'button',
32311                         cls : 'btn btn-default',
32312                         html : '<i class="fa fa-trash"></i>'
32313                     }
32314                 ]
32315             },
32316             {
32317                 tag : 'div',
32318                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32319                 action : 'rotate-right',
32320                 cn : [
32321                     {
32322                         tag : 'button',
32323                         cls : 'btn btn-default',
32324                         html : '<i class="fa fa-repeat"></i>'
32325                     }
32326                 ]
32327             }
32328         ],
32329         ROTATOR : [
32330             {
32331                 tag : 'div',
32332                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32333                 action : 'rotate-left',
32334                 cn : [
32335                     {
32336                         tag : 'button',
32337                         cls : 'btn btn-default',
32338                         html : '<i class="fa fa-undo"></i>'
32339                     }
32340                 ]
32341             },
32342             {
32343                 tag : 'div',
32344                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32345                 action : 'rotate-right',
32346                 cn : [
32347                     {
32348                         tag : 'button',
32349                         cls : 'btn btn-default',
32350                         html : '<i class="fa fa-repeat"></i>'
32351                     }
32352                 ]
32353             }
32354         ]
32355     }
32356 });
32357
32358 /*
32359 * Licence: LGPL
32360 */
32361
32362 /**
32363  * @class Roo.bootstrap.DocumentManager
32364  * @extends Roo.bootstrap.Component
32365  * Bootstrap DocumentManager class
32366  * @cfg {String} paramName default 'imageUpload'
32367  * @cfg {String} toolTipName default 'filename'
32368  * @cfg {String} method default POST
32369  * @cfg {String} url action url
32370  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32371  * @cfg {Boolean} multiple multiple upload default true
32372  * @cfg {Number} thumbSize default 300
32373  * @cfg {String} fieldLabel
32374  * @cfg {Number} labelWidth default 4
32375  * @cfg {String} labelAlign (left|top) default left
32376  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32377 * @cfg {Number} labellg set the width of label (1-12)
32378  * @cfg {Number} labelmd set the width of label (1-12)
32379  * @cfg {Number} labelsm set the width of label (1-12)
32380  * @cfg {Number} labelxs set the width of label (1-12)
32381  * 
32382  * @constructor
32383  * Create a new DocumentManager
32384  * @param {Object} config The config object
32385  */
32386
32387 Roo.bootstrap.DocumentManager = function(config){
32388     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32389     
32390     this.files = [];
32391     this.delegates = [];
32392     
32393     this.addEvents({
32394         /**
32395          * @event initial
32396          * Fire when initial the DocumentManager
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          */
32399         "initial" : true,
32400         /**
32401          * @event inspect
32402          * inspect selected file
32403          * @param {Roo.bootstrap.DocumentManager} this
32404          * @param {File} file
32405          */
32406         "inspect" : true,
32407         /**
32408          * @event exception
32409          * Fire when xhr load exception
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          * @param {XMLHttpRequest} xhr
32412          */
32413         "exception" : true,
32414         /**
32415          * @event afterupload
32416          * Fire when xhr load exception
32417          * @param {Roo.bootstrap.DocumentManager} this
32418          * @param {XMLHttpRequest} xhr
32419          */
32420         "afterupload" : true,
32421         /**
32422          * @event prepare
32423          * prepare the form data
32424          * @param {Roo.bootstrap.DocumentManager} this
32425          * @param {Object} formData
32426          */
32427         "prepare" : true,
32428         /**
32429          * @event remove
32430          * Fire when remove the file
32431          * @param {Roo.bootstrap.DocumentManager} this
32432          * @param {Object} file
32433          */
32434         "remove" : true,
32435         /**
32436          * @event refresh
32437          * Fire after refresh the file
32438          * @param {Roo.bootstrap.DocumentManager} this
32439          */
32440         "refresh" : true,
32441         /**
32442          * @event click
32443          * Fire after click the image
32444          * @param {Roo.bootstrap.DocumentManager} this
32445          * @param {Object} file
32446          */
32447         "click" : true,
32448         /**
32449          * @event edit
32450          * Fire when upload a image and editable set to true
32451          * @param {Roo.bootstrap.DocumentManager} this
32452          * @param {Object} file
32453          */
32454         "edit" : true,
32455         /**
32456          * @event beforeselectfile
32457          * Fire before select file
32458          * @param {Roo.bootstrap.DocumentManager} this
32459          */
32460         "beforeselectfile" : true,
32461         /**
32462          * @event process
32463          * Fire before process file
32464          * @param {Roo.bootstrap.DocumentManager} this
32465          * @param {Object} file
32466          */
32467         "process" : true,
32468         /**
32469          * @event previewrendered
32470          * Fire when preview rendered
32471          * @param {Roo.bootstrap.DocumentManager} this
32472          * @param {Object} file
32473          */
32474         "previewrendered" : true,
32475         /**
32476          */
32477         "previewResize" : true
32478         
32479     });
32480 };
32481
32482 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32483     
32484     boxes : 0,
32485     inputName : '',
32486     thumbSize : 300,
32487     multiple : true,
32488     files : false,
32489     method : 'POST',
32490     url : '',
32491     paramName : 'imageUpload',
32492     toolTipName : 'filename',
32493     fieldLabel : '',
32494     labelWidth : 4,
32495     labelAlign : 'left',
32496     editable : true,
32497     delegates : false,
32498     xhr : false, 
32499     
32500     labellg : 0,
32501     labelmd : 0,
32502     labelsm : 0,
32503     labelxs : 0,
32504     
32505     getAutoCreate : function()
32506     {   
32507         var managerWidget = {
32508             tag : 'div',
32509             cls : 'roo-document-manager',
32510             cn : [
32511                 {
32512                     tag : 'input',
32513                     cls : 'roo-document-manager-selector',
32514                     type : 'file'
32515                 },
32516                 {
32517                     tag : 'div',
32518                     cls : 'roo-document-manager-uploader',
32519                     cn : [
32520                         {
32521                             tag : 'div',
32522                             cls : 'roo-document-manager-upload-btn',
32523                             html : '<i class="fa fa-plus"></i>'
32524                         }
32525                     ]
32526                     
32527                 }
32528             ]
32529         };
32530         
32531         var content = [
32532             {
32533                 tag : 'div',
32534                 cls : 'column col-md-12',
32535                 cn : managerWidget
32536             }
32537         ];
32538         
32539         if(this.fieldLabel.length){
32540             
32541             content = [
32542                 {
32543                     tag : 'div',
32544                     cls : 'column col-md-12',
32545                     html : this.fieldLabel
32546                 },
32547                 {
32548                     tag : 'div',
32549                     cls : 'column col-md-12',
32550                     cn : managerWidget
32551                 }
32552             ];
32553
32554             if(this.labelAlign == 'left'){
32555                 content = [
32556                     {
32557                         tag : 'div',
32558                         cls : 'column',
32559                         html : this.fieldLabel
32560                     },
32561                     {
32562                         tag : 'div',
32563                         cls : 'column',
32564                         cn : managerWidget
32565                     }
32566                 ];
32567                 
32568                 if(this.labelWidth > 12){
32569                     content[0].style = "width: " + this.labelWidth + 'px';
32570                 }
32571
32572                 if(this.labelWidth < 13 && this.labelmd == 0){
32573                     this.labelmd = this.labelWidth;
32574                 }
32575
32576                 if(this.labellg > 0){
32577                     content[0].cls += ' col-lg-' + this.labellg;
32578                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32579                 }
32580
32581                 if(this.labelmd > 0){
32582                     content[0].cls += ' col-md-' + this.labelmd;
32583                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32584                 }
32585
32586                 if(this.labelsm > 0){
32587                     content[0].cls += ' col-sm-' + this.labelsm;
32588                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32589                 }
32590
32591                 if(this.labelxs > 0){
32592                     content[0].cls += ' col-xs-' + this.labelxs;
32593                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32594                 }
32595                 
32596             }
32597         }
32598         
32599         var cfg = {
32600             tag : 'div',
32601             cls : 'row clearfix',
32602             cn : content
32603         };
32604         
32605         return cfg;
32606         
32607     },
32608     
32609     initEvents : function()
32610     {
32611         this.managerEl = this.el.select('.roo-document-manager', true).first();
32612         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32613         
32614         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32615         this.selectorEl.hide();
32616         
32617         if(this.multiple){
32618             this.selectorEl.attr('multiple', 'multiple');
32619         }
32620         
32621         this.selectorEl.on('change', this.onFileSelected, this);
32622         
32623         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32624         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32625         
32626         this.uploader.on('click', this.onUploaderClick, this);
32627         
32628         this.renderProgressDialog();
32629         
32630         var _this = this;
32631         
32632         window.addEventListener("resize", function() { _this.refresh(); } );
32633         
32634         this.fireEvent('initial', this);
32635     },
32636     
32637     renderProgressDialog : function()
32638     {
32639         var _this = this;
32640         
32641         this.progressDialog = new Roo.bootstrap.Modal({
32642             cls : 'roo-document-manager-progress-dialog',
32643             allow_close : false,
32644             animate : false,
32645             title : '',
32646             buttons : [
32647                 {
32648                     name  :'cancel',
32649                     weight : 'danger',
32650                     html : 'Cancel'
32651                 }
32652             ], 
32653             listeners : { 
32654                 btnclick : function() {
32655                     _this.uploadCancel();
32656                     this.hide();
32657                 }
32658             }
32659         });
32660          
32661         this.progressDialog.render(Roo.get(document.body));
32662          
32663         this.progress = new Roo.bootstrap.Progress({
32664             cls : 'roo-document-manager-progress',
32665             active : true,
32666             striped : true
32667         });
32668         
32669         this.progress.render(this.progressDialog.getChildContainer());
32670         
32671         this.progressBar = new Roo.bootstrap.ProgressBar({
32672             cls : 'roo-document-manager-progress-bar',
32673             aria_valuenow : 0,
32674             aria_valuemin : 0,
32675             aria_valuemax : 12,
32676             panel : 'success'
32677         });
32678         
32679         this.progressBar.render(this.progress.getChildContainer());
32680     },
32681     
32682     onUploaderClick : function(e)
32683     {
32684         e.preventDefault();
32685      
32686         if(this.fireEvent('beforeselectfile', this) != false){
32687             this.selectorEl.dom.click();
32688         }
32689         
32690     },
32691     
32692     onFileSelected : function(e)
32693     {
32694         e.preventDefault();
32695         
32696         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32697             return;
32698         }
32699         
32700         Roo.each(this.selectorEl.dom.files, function(file){
32701             if(this.fireEvent('inspect', this, file) != false){
32702                 this.files.push(file);
32703             }
32704         }, this);
32705         
32706         this.queue();
32707         
32708     },
32709     
32710     queue : function()
32711     {
32712         this.selectorEl.dom.value = '';
32713         
32714         if(!this.files || !this.files.length){
32715             return;
32716         }
32717         
32718         if(this.boxes > 0 && this.files.length > this.boxes){
32719             this.files = this.files.slice(0, this.boxes);
32720         }
32721         
32722         this.uploader.show();
32723         
32724         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32725             this.uploader.hide();
32726         }
32727         
32728         var _this = this;
32729         
32730         var files = [];
32731         
32732         var docs = [];
32733         
32734         Roo.each(this.files, function(file){
32735             
32736             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32737                 var f = this.renderPreview(file);
32738                 files.push(f);
32739                 return;
32740             }
32741             
32742             if(file.type.indexOf('image') != -1){
32743                 this.delegates.push(
32744                     (function(){
32745                         _this.process(file);
32746                     }).createDelegate(this)
32747                 );
32748         
32749                 return;
32750             }
32751             
32752             docs.push(
32753                 (function(){
32754                     _this.process(file);
32755                 }).createDelegate(this)
32756             );
32757             
32758         }, this);
32759         
32760         this.files = files;
32761         
32762         this.delegates = this.delegates.concat(docs);
32763         
32764         if(!this.delegates.length){
32765             this.refresh();
32766             return;
32767         }
32768         
32769         this.progressBar.aria_valuemax = this.delegates.length;
32770         
32771         this.arrange();
32772         
32773         return;
32774     },
32775     
32776     arrange : function()
32777     {
32778         if(!this.delegates.length){
32779             this.progressDialog.hide();
32780             this.refresh();
32781             return;
32782         }
32783         
32784         var delegate = this.delegates.shift();
32785         
32786         this.progressDialog.show();
32787         
32788         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32789         
32790         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32791         
32792         delegate();
32793     },
32794     
32795     refresh : function()
32796     {
32797         this.uploader.show();
32798         
32799         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32800             this.uploader.hide();
32801         }
32802         
32803         Roo.isTouch ? this.closable(false) : this.closable(true);
32804         
32805         this.fireEvent('refresh', this);
32806     },
32807     
32808     onRemove : function(e, el, o)
32809     {
32810         e.preventDefault();
32811         
32812         this.fireEvent('remove', this, o);
32813         
32814     },
32815     
32816     remove : function(o)
32817     {
32818         var files = [];
32819         
32820         Roo.each(this.files, function(file){
32821             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32822                 files.push(file);
32823                 return;
32824             }
32825
32826             o.target.remove();
32827
32828         }, this);
32829         
32830         this.files = files;
32831         
32832         this.refresh();
32833     },
32834     
32835     clear : function()
32836     {
32837         Roo.each(this.files, function(file){
32838             if(!file.target){
32839                 return;
32840             }
32841             
32842             file.target.remove();
32843
32844         }, this);
32845         
32846         this.files = [];
32847         
32848         this.refresh();
32849     },
32850     
32851     onClick : function(e, el, o)
32852     {
32853         e.preventDefault();
32854         
32855         this.fireEvent('click', this, o);
32856         
32857     },
32858     
32859     closable : function(closable)
32860     {
32861         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32862             
32863             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32864             
32865             if(closable){
32866                 el.show();
32867                 return;
32868             }
32869             
32870             el.hide();
32871             
32872         }, this);
32873     },
32874     
32875     xhrOnLoad : function(xhr)
32876     {
32877         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32878             el.remove();
32879         }, this);
32880         
32881         if (xhr.readyState !== 4) {
32882             this.arrange();
32883             this.fireEvent('exception', this, xhr);
32884             return;
32885         }
32886
32887         var response = Roo.decode(xhr.responseText);
32888         
32889         if(!response.success){
32890             this.arrange();
32891             this.fireEvent('exception', this, xhr);
32892             return;
32893         }
32894         
32895         var file = this.renderPreview(response.data);
32896         
32897         this.files.push(file);
32898         
32899         this.arrange();
32900         
32901         this.fireEvent('afterupload', this, xhr);
32902         
32903     },
32904     
32905     xhrOnError : function(xhr)
32906     {
32907         Roo.log('xhr on error');
32908         
32909         var response = Roo.decode(xhr.responseText);
32910           
32911         Roo.log(response);
32912         
32913         this.arrange();
32914     },
32915     
32916     process : function(file)
32917     {
32918         if(this.fireEvent('process', this, file) !== false){
32919             if(this.editable && file.type.indexOf('image') != -1){
32920                 this.fireEvent('edit', this, file);
32921                 return;
32922             }
32923
32924             this.uploadStart(file, false);
32925
32926             return;
32927         }
32928         
32929     },
32930     
32931     uploadStart : function(file, crop)
32932     {
32933         this.xhr = new XMLHttpRequest();
32934         
32935         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32936             this.arrange();
32937             return;
32938         }
32939         
32940         file.xhr = this.xhr;
32941             
32942         this.managerEl.createChild({
32943             tag : 'div',
32944             cls : 'roo-document-manager-loading',
32945             cn : [
32946                 {
32947                     tag : 'div',
32948                     tooltip : file.name,
32949                     cls : 'roo-document-manager-thumb',
32950                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32951                 }
32952             ]
32953
32954         });
32955
32956         this.xhr.open(this.method, this.url, true);
32957         
32958         var headers = {
32959             "Accept": "application/json",
32960             "Cache-Control": "no-cache",
32961             "X-Requested-With": "XMLHttpRequest"
32962         };
32963         
32964         for (var headerName in headers) {
32965             var headerValue = headers[headerName];
32966             if (headerValue) {
32967                 this.xhr.setRequestHeader(headerName, headerValue);
32968             }
32969         }
32970         
32971         var _this = this;
32972         
32973         this.xhr.onload = function()
32974         {
32975             _this.xhrOnLoad(_this.xhr);
32976         }
32977         
32978         this.xhr.onerror = function()
32979         {
32980             _this.xhrOnError(_this.xhr);
32981         }
32982         
32983         var formData = new FormData();
32984
32985         formData.append('returnHTML', 'NO');
32986         
32987         if(crop){
32988             formData.append('crop', crop);
32989         }
32990         
32991         formData.append(this.paramName, file, file.name);
32992         
32993         var options = {
32994             file : file, 
32995             manually : false
32996         };
32997         
32998         if(this.fireEvent('prepare', this, formData, options) != false){
32999             
33000             if(options.manually){
33001                 return;
33002             }
33003             
33004             this.xhr.send(formData);
33005             return;
33006         };
33007         
33008         this.uploadCancel();
33009     },
33010     
33011     uploadCancel : function()
33012     {
33013         if (this.xhr) {
33014             this.xhr.abort();
33015         }
33016         
33017         this.delegates = [];
33018         
33019         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33020             el.remove();
33021         }, this);
33022         
33023         this.arrange();
33024     },
33025     
33026     renderPreview : function(file)
33027     {
33028         if(typeof(file.target) != 'undefined' && file.target){
33029             return file;
33030         }
33031         
33032         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33033         
33034         var previewEl = this.managerEl.createChild({
33035             tag : 'div',
33036             cls : 'roo-document-manager-preview',
33037             cn : [
33038                 {
33039                     tag : 'div',
33040                     tooltip : file[this.toolTipName],
33041                     cls : 'roo-document-manager-thumb',
33042                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33043                 },
33044                 {
33045                     tag : 'button',
33046                     cls : 'close',
33047                     html : '<i class="fa fa-times-circle"></i>'
33048                 }
33049             ]
33050         });
33051
33052         var close = previewEl.select('button.close', true).first();
33053
33054         close.on('click', this.onRemove, this, file);
33055
33056         file.target = previewEl;
33057
33058         var image = previewEl.select('img', true).first();
33059         
33060         var _this = this;
33061         
33062         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33063         
33064         image.on('click', this.onClick, this, file);
33065         
33066         this.fireEvent('previewrendered', this, file);
33067         
33068         return file;
33069         
33070     },
33071     
33072     onPreviewLoad : function(file, image)
33073     {
33074         if(typeof(file.target) == 'undefined' || !file.target){
33075             return;
33076         }
33077         
33078         var width = image.dom.naturalWidth || image.dom.width;
33079         var height = image.dom.naturalHeight || image.dom.height;
33080         
33081         if(!this.previewResize) {
33082             return;
33083         }
33084         
33085         if(width > height){
33086             file.target.addClass('wide');
33087             return;
33088         }
33089         
33090         file.target.addClass('tall');
33091         return;
33092         
33093     },
33094     
33095     uploadFromSource : function(file, crop)
33096     {
33097         this.xhr = new XMLHttpRequest();
33098         
33099         this.managerEl.createChild({
33100             tag : 'div',
33101             cls : 'roo-document-manager-loading',
33102             cn : [
33103                 {
33104                     tag : 'div',
33105                     tooltip : file.name,
33106                     cls : 'roo-document-manager-thumb',
33107                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33108                 }
33109             ]
33110
33111         });
33112
33113         this.xhr.open(this.method, this.url, true);
33114         
33115         var headers = {
33116             "Accept": "application/json",
33117             "Cache-Control": "no-cache",
33118             "X-Requested-With": "XMLHttpRequest"
33119         };
33120         
33121         for (var headerName in headers) {
33122             var headerValue = headers[headerName];
33123             if (headerValue) {
33124                 this.xhr.setRequestHeader(headerName, headerValue);
33125             }
33126         }
33127         
33128         var _this = this;
33129         
33130         this.xhr.onload = function()
33131         {
33132             _this.xhrOnLoad(_this.xhr);
33133         }
33134         
33135         this.xhr.onerror = function()
33136         {
33137             _this.xhrOnError(_this.xhr);
33138         }
33139         
33140         var formData = new FormData();
33141
33142         formData.append('returnHTML', 'NO');
33143         
33144         formData.append('crop', crop);
33145         
33146         if(typeof(file.filename) != 'undefined'){
33147             formData.append('filename', file.filename);
33148         }
33149         
33150         if(typeof(file.mimetype) != 'undefined'){
33151             formData.append('mimetype', file.mimetype);
33152         }
33153         
33154         Roo.log(formData);
33155         
33156         if(this.fireEvent('prepare', this, formData) != false){
33157             this.xhr.send(formData);
33158         };
33159     }
33160 });
33161
33162 /*
33163 * Licence: LGPL
33164 */
33165
33166 /**
33167  * @class Roo.bootstrap.DocumentViewer
33168  * @extends Roo.bootstrap.Component
33169  * Bootstrap DocumentViewer class
33170  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33171  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33172  * 
33173  * @constructor
33174  * Create a new DocumentViewer
33175  * @param {Object} config The config object
33176  */
33177
33178 Roo.bootstrap.DocumentViewer = function(config){
33179     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33180     
33181     this.addEvents({
33182         /**
33183          * @event initial
33184          * Fire after initEvent
33185          * @param {Roo.bootstrap.DocumentViewer} this
33186          */
33187         "initial" : true,
33188         /**
33189          * @event click
33190          * Fire after click
33191          * @param {Roo.bootstrap.DocumentViewer} this
33192          */
33193         "click" : true,
33194         /**
33195          * @event download
33196          * Fire after download button
33197          * @param {Roo.bootstrap.DocumentViewer} this
33198          */
33199         "download" : true,
33200         /**
33201          * @event trash
33202          * Fire after trash button
33203          * @param {Roo.bootstrap.DocumentViewer} this
33204          */
33205         "trash" : true
33206         
33207     });
33208 };
33209
33210 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33211     
33212     showDownload : true,
33213     
33214     showTrash : true,
33215     
33216     getAutoCreate : function()
33217     {
33218         var cfg = {
33219             tag : 'div',
33220             cls : 'roo-document-viewer',
33221             cn : [
33222                 {
33223                     tag : 'div',
33224                     cls : 'roo-document-viewer-body',
33225                     cn : [
33226                         {
33227                             tag : 'div',
33228                             cls : 'roo-document-viewer-thumb',
33229                             cn : [
33230                                 {
33231                                     tag : 'img',
33232                                     cls : 'roo-document-viewer-image'
33233                                 }
33234                             ]
33235                         }
33236                     ]
33237                 },
33238                 {
33239                     tag : 'div',
33240                     cls : 'roo-document-viewer-footer',
33241                     cn : {
33242                         tag : 'div',
33243                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33244                         cn : [
33245                             {
33246                                 tag : 'div',
33247                                 cls : 'btn-group roo-document-viewer-download',
33248                                 cn : [
33249                                     {
33250                                         tag : 'button',
33251                                         cls : 'btn btn-default',
33252                                         html : '<i class="fa fa-download"></i>'
33253                                     }
33254                                 ]
33255                             },
33256                             {
33257                                 tag : 'div',
33258                                 cls : 'btn-group roo-document-viewer-trash',
33259                                 cn : [
33260                                     {
33261                                         tag : 'button',
33262                                         cls : 'btn btn-default',
33263                                         html : '<i class="fa fa-trash"></i>'
33264                                     }
33265                                 ]
33266                             }
33267                         ]
33268                     }
33269                 }
33270             ]
33271         };
33272         
33273         return cfg;
33274     },
33275     
33276     initEvents : function()
33277     {
33278         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33279         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33280         
33281         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33282         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33283         
33284         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33285         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33286         
33287         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33288         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33289         
33290         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33291         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33292         
33293         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33294         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33295         
33296         this.bodyEl.on('click', this.onClick, this);
33297         this.downloadBtn.on('click', this.onDownload, this);
33298         this.trashBtn.on('click', this.onTrash, this);
33299         
33300         this.downloadBtn.hide();
33301         this.trashBtn.hide();
33302         
33303         if(this.showDownload){
33304             this.downloadBtn.show();
33305         }
33306         
33307         if(this.showTrash){
33308             this.trashBtn.show();
33309         }
33310         
33311         if(!this.showDownload && !this.showTrash) {
33312             this.footerEl.hide();
33313         }
33314         
33315     },
33316     
33317     initial : function()
33318     {
33319         this.fireEvent('initial', this);
33320         
33321     },
33322     
33323     onClick : function(e)
33324     {
33325         e.preventDefault();
33326         
33327         this.fireEvent('click', this);
33328     },
33329     
33330     onDownload : function(e)
33331     {
33332         e.preventDefault();
33333         
33334         this.fireEvent('download', this);
33335     },
33336     
33337     onTrash : function(e)
33338     {
33339         e.preventDefault();
33340         
33341         this.fireEvent('trash', this);
33342     }
33343     
33344 });
33345 /*
33346  * - LGPL
33347  *
33348  * nav progress bar
33349  * 
33350  */
33351
33352 /**
33353  * @class Roo.bootstrap.NavProgressBar
33354  * @extends Roo.bootstrap.Component
33355  * Bootstrap NavProgressBar class
33356  * 
33357  * @constructor
33358  * Create a new nav progress bar - a bar indicating step along a process
33359  * @param {Object} config The config object
33360  */
33361
33362 Roo.bootstrap.NavProgressBar = function(config){
33363     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33364
33365     this.bullets = this.bullets || [];
33366    
33367 //    Roo.bootstrap.NavProgressBar.register(this);
33368      this.addEvents({
33369         /**
33370              * @event changed
33371              * Fires when the active item changes
33372              * @param {Roo.bootstrap.NavProgressBar} this
33373              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33374              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33375          */
33376         'changed': true
33377      });
33378     
33379 };
33380
33381 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33382     /**
33383      * @cfg {Roo.bootstrap.NavProgressItem} NavProgressBar:bullets[]
33384      * Bullets for the Nav Progress bar for the toolbar
33385      */
33386     bullets : [],
33387     barItems : [],
33388     
33389     getAutoCreate : function()
33390     {
33391         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33392         
33393         cfg = {
33394             tag : 'div',
33395             cls : 'roo-navigation-bar-group',
33396             cn : [
33397                 {
33398                     tag : 'div',
33399                     cls : 'roo-navigation-top-bar'
33400                 },
33401                 {
33402                     tag : 'div',
33403                     cls : 'roo-navigation-bullets-bar',
33404                     cn : [
33405                         {
33406                             tag : 'ul',
33407                             cls : 'roo-navigation-bar'
33408                         }
33409                     ]
33410                 },
33411                 
33412                 {
33413                     tag : 'div',
33414                     cls : 'roo-navigation-bottom-bar'
33415                 }
33416             ]
33417             
33418         };
33419         
33420         return cfg;
33421         
33422     },
33423     
33424     initEvents: function() 
33425     {
33426         
33427     },
33428     
33429     onRender : function(ct, position) 
33430     {
33431         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33432         
33433         if(this.bullets.length){
33434             Roo.each(this.bullets, function(b){
33435                this.addItem(b);
33436             }, this);
33437         }
33438         
33439         this.format();
33440         
33441     },
33442     
33443     addItem : function(cfg)
33444     {
33445         var item = new Roo.bootstrap.NavProgressItem(cfg);
33446         
33447         item.parentId = this.id;
33448         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33449         
33450         if(cfg.html){
33451             var top = new Roo.bootstrap.Element({
33452                 tag : 'div',
33453                 cls : 'roo-navigation-bar-text'
33454             });
33455             
33456             var bottom = new Roo.bootstrap.Element({
33457                 tag : 'div',
33458                 cls : 'roo-navigation-bar-text'
33459             });
33460             
33461             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33462             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33463             
33464             var topText = new Roo.bootstrap.Element({
33465                 tag : 'span',
33466                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33467             });
33468             
33469             var bottomText = new Roo.bootstrap.Element({
33470                 tag : 'span',
33471                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33472             });
33473             
33474             topText.onRender(top.el, null);
33475             bottomText.onRender(bottom.el, null);
33476             
33477             item.topEl = top;
33478             item.bottomEl = bottom;
33479         }
33480         
33481         this.barItems.push(item);
33482         
33483         return item;
33484     },
33485     
33486     getActive : function()
33487     {
33488         var active = false;
33489         
33490         Roo.each(this.barItems, function(v){
33491             
33492             if (!v.isActive()) {
33493                 return;
33494             }
33495             
33496             active = v;
33497             return false;
33498             
33499         });
33500         
33501         return active;
33502     },
33503     
33504     setActiveItem : function(item)
33505     {
33506         var prev = false;
33507         
33508         Roo.each(this.barItems, function(v){
33509             if (v.rid == item.rid) {
33510                 return ;
33511             }
33512             
33513             if (v.isActive()) {
33514                 v.setActive(false);
33515                 prev = v;
33516             }
33517         });
33518
33519         item.setActive(true);
33520         
33521         this.fireEvent('changed', this, item, prev);
33522     },
33523     
33524     getBarItem: function(rid)
33525     {
33526         var ret = false;
33527         
33528         Roo.each(this.barItems, function(e) {
33529             if (e.rid != rid) {
33530                 return;
33531             }
33532             
33533             ret =  e;
33534             return false;
33535         });
33536         
33537         return ret;
33538     },
33539     
33540     indexOfItem : function(item)
33541     {
33542         var index = false;
33543         
33544         Roo.each(this.barItems, function(v, i){
33545             
33546             if (v.rid != item.rid) {
33547                 return;
33548             }
33549             
33550             index = i;
33551             return false
33552         });
33553         
33554         return index;
33555     },
33556     
33557     setActiveNext : function()
33558     {
33559         var i = this.indexOfItem(this.getActive());
33560         
33561         if (i > this.barItems.length) {
33562             return;
33563         }
33564         
33565         this.setActiveItem(this.barItems[i+1]);
33566     },
33567     
33568     setActivePrev : function()
33569     {
33570         var i = this.indexOfItem(this.getActive());
33571         
33572         if (i  < 1) {
33573             return;
33574         }
33575         
33576         this.setActiveItem(this.barItems[i-1]);
33577     },
33578     
33579     format : function()
33580     {
33581         if(!this.barItems.length){
33582             return;
33583         }
33584      
33585         var width = 100 / this.barItems.length;
33586         
33587         Roo.each(this.barItems, function(i){
33588             i.el.setStyle('width', width + '%');
33589             i.topEl.el.setStyle('width', width + '%');
33590             i.bottomEl.el.setStyle('width', width + '%');
33591         }, this);
33592         
33593     }
33594     
33595 });
33596 /*
33597  * - LGPL
33598  *
33599  * Nav Progress Item
33600  * 
33601  */
33602
33603 /**
33604  * @class Roo.bootstrap.NavProgressItem
33605  * @extends Roo.bootstrap.Component
33606  * Bootstrap NavProgressItem class
33607  * @cfg {String} rid the reference id
33608  * @cfg {Boolean} active (true|false) Is item active default false
33609  * @cfg {Boolean} disabled (true|false) Is item active default false
33610  * @cfg {String} html
33611  * @cfg {String} position (top|bottom) text position default bottom
33612  * @cfg {String} icon show icon instead of number
33613  * 
33614  * @constructor
33615  * Create a new NavProgressItem
33616  * @param {Object} config The config object
33617  */
33618 Roo.bootstrap.NavProgressItem = function(config){
33619     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33620     this.addEvents({
33621         // raw events
33622         /**
33623          * @event click
33624          * The raw click event for the entire grid.
33625          * @param {Roo.bootstrap.NavProgressItem} this
33626          * @param {Roo.EventObject} e
33627          */
33628         "click" : true
33629     });
33630    
33631 };
33632
33633 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33634     
33635     rid : '',
33636     active : false,
33637     disabled : false,
33638     html : '',
33639     position : 'bottom',
33640     icon : false,
33641     
33642     getAutoCreate : function()
33643     {
33644         var iconCls = 'roo-navigation-bar-item-icon';
33645         
33646         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33647         
33648         var cfg = {
33649             tag: 'li',
33650             cls: 'roo-navigation-bar-item',
33651             cn : [
33652                 {
33653                     tag : 'i',
33654                     cls : iconCls
33655                 }
33656             ]
33657         };
33658         
33659         if(this.active){
33660             cfg.cls += ' active';
33661         }
33662         if(this.disabled){
33663             cfg.cls += ' disabled';
33664         }
33665         
33666         return cfg;
33667     },
33668     
33669     disable : function()
33670     {
33671         this.setDisabled(true);
33672     },
33673     
33674     enable : function()
33675     {
33676         this.setDisabled(false);
33677     },
33678     
33679     initEvents: function() 
33680     {
33681         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33682         
33683         this.iconEl.on('click', this.onClick, this);
33684     },
33685     
33686     onClick : function(e)
33687     {
33688         e.preventDefault();
33689         
33690         if(this.disabled){
33691             return;
33692         }
33693         
33694         if(this.fireEvent('click', this, e) === false){
33695             return;
33696         };
33697         
33698         this.parent().setActiveItem(this);
33699     },
33700     
33701     isActive: function () 
33702     {
33703         return this.active;
33704     },
33705     
33706     setActive : function(state)
33707     {
33708         if(this.active == state){
33709             return;
33710         }
33711         
33712         this.active = state;
33713         
33714         if (state) {
33715             this.el.addClass('active');
33716             return;
33717         }
33718         
33719         this.el.removeClass('active');
33720         
33721         return;
33722     },
33723     
33724     setDisabled : function(state)
33725     {
33726         if(this.disabled == state){
33727             return;
33728         }
33729         
33730         this.disabled = state;
33731         
33732         if (state) {
33733             this.el.addClass('disabled');
33734             return;
33735         }
33736         
33737         this.el.removeClass('disabled');
33738     },
33739     
33740     tooltipEl : function()
33741     {
33742         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33743     }
33744 });
33745  
33746
33747  /*
33748  * - LGPL
33749  *
33750  * FieldLabel
33751  * 
33752  */
33753
33754 /**
33755  * @class Roo.bootstrap.FieldLabel
33756  * @extends Roo.bootstrap.Component
33757  * Bootstrap FieldLabel class
33758  * @cfg {String} html contents of the element
33759  * @cfg {String} tag tag of the element default label
33760  * @cfg {String} cls class of the element
33761  * @cfg {String} target label target 
33762  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33763  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33764  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33765  * @cfg {String} iconTooltip default "This field is required"
33766  * @cfg {String} indicatorpos (left|right) default left
33767  * 
33768  * @constructor
33769  * Create a new FieldLabel
33770  * @param {Object} config The config object
33771  */
33772
33773 Roo.bootstrap.FieldLabel = function(config){
33774     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33775     
33776     this.addEvents({
33777             /**
33778              * @event invalid
33779              * Fires after the field has been marked as invalid.
33780              * @param {Roo.form.FieldLabel} this
33781              * @param {String} msg The validation message
33782              */
33783             invalid : true,
33784             /**
33785              * @event valid
33786              * Fires after the field has been validated with no errors.
33787              * @param {Roo.form.FieldLabel} this
33788              */
33789             valid : true
33790         });
33791 };
33792
33793 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33794     
33795     tag: 'label',
33796     cls: '',
33797     html: '',
33798     target: '',
33799     allowBlank : true,
33800     invalidClass : 'has-warning',
33801     validClass : 'has-success',
33802     iconTooltip : 'This field is required',
33803     indicatorpos : 'left',
33804     
33805     getAutoCreate : function(){
33806         
33807         var cls = "";
33808         if (!this.allowBlank) {
33809             cls  = "visible";
33810         }
33811         
33812         var cfg = {
33813             tag : this.tag,
33814             cls : 'roo-bootstrap-field-label ' + this.cls,
33815             for : this.target,
33816             cn : [
33817                 {
33818                     tag : 'i',
33819                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33820                     tooltip : this.iconTooltip
33821                 },
33822                 {
33823                     tag : 'span',
33824                     html : this.html
33825                 }
33826             ] 
33827         };
33828         
33829         if(this.indicatorpos == 'right'){
33830             var cfg = {
33831                 tag : this.tag,
33832                 cls : 'roo-bootstrap-field-label ' + this.cls,
33833                 for : this.target,
33834                 cn : [
33835                     {
33836                         tag : 'span',
33837                         html : this.html
33838                     },
33839                     {
33840                         tag : 'i',
33841                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33842                         tooltip : this.iconTooltip
33843                     }
33844                 ] 
33845             };
33846         }
33847         
33848         return cfg;
33849     },
33850     
33851     initEvents: function() 
33852     {
33853         Roo.bootstrap.Element.superclass.initEvents.call(this);
33854         
33855         this.indicator = this.indicatorEl();
33856         
33857         if(this.indicator){
33858             this.indicator.removeClass('visible');
33859             this.indicator.addClass('invisible');
33860         }
33861         
33862         Roo.bootstrap.FieldLabel.register(this);
33863     },
33864     
33865     indicatorEl : function()
33866     {
33867         var indicator = this.el.select('i.roo-required-indicator',true).first();
33868         
33869         if(!indicator){
33870             return false;
33871         }
33872         
33873         return indicator;
33874         
33875     },
33876     
33877     /**
33878      * Mark this field as valid
33879      */
33880     markValid : function()
33881     {
33882         if(this.indicator){
33883             this.indicator.removeClass('visible');
33884             this.indicator.addClass('invisible');
33885         }
33886         if (Roo.bootstrap.version == 3) {
33887             this.el.removeClass(this.invalidClass);
33888             this.el.addClass(this.validClass);
33889         } else {
33890             this.el.removeClass('is-invalid');
33891             this.el.addClass('is-valid');
33892         }
33893         
33894         
33895         this.fireEvent('valid', this);
33896     },
33897     
33898     /**
33899      * Mark this field as invalid
33900      * @param {String} msg The validation message
33901      */
33902     markInvalid : function(msg)
33903     {
33904         if(this.indicator){
33905             this.indicator.removeClass('invisible');
33906             this.indicator.addClass('visible');
33907         }
33908           if (Roo.bootstrap.version == 3) {
33909             this.el.removeClass(this.validClass);
33910             this.el.addClass(this.invalidClass);
33911         } else {
33912             this.el.removeClass('is-valid');
33913             this.el.addClass('is-invalid');
33914         }
33915         
33916         
33917         this.fireEvent('invalid', this, msg);
33918     }
33919     
33920    
33921 });
33922
33923 Roo.apply(Roo.bootstrap.FieldLabel, {
33924     
33925     groups: {},
33926     
33927      /**
33928     * register a FieldLabel Group
33929     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33930     */
33931     register : function(label)
33932     {
33933         if(this.groups.hasOwnProperty(label.target)){
33934             return;
33935         }
33936      
33937         this.groups[label.target] = label;
33938         
33939     },
33940     /**
33941     * fetch a FieldLabel Group based on the target
33942     * @param {string} target
33943     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33944     */
33945     get: function(target) {
33946         if (typeof(this.groups[target]) == 'undefined') {
33947             return false;
33948         }
33949         
33950         return this.groups[target] ;
33951     }
33952 });
33953
33954  
33955
33956  /*
33957  * - LGPL
33958  *
33959  * page DateSplitField.
33960  * 
33961  */
33962
33963
33964 /**
33965  * @class Roo.bootstrap.DateSplitField
33966  * @extends Roo.bootstrap.Component
33967  * Bootstrap DateSplitField class
33968  * @cfg {string} fieldLabel - the label associated
33969  * @cfg {Number} labelWidth set the width of label (0-12)
33970  * @cfg {String} labelAlign (top|left)
33971  * @cfg {Boolean} dayAllowBlank (true|false) default false
33972  * @cfg {Boolean} monthAllowBlank (true|false) default false
33973  * @cfg {Boolean} yearAllowBlank (true|false) default false
33974  * @cfg {string} dayPlaceholder 
33975  * @cfg {string} monthPlaceholder
33976  * @cfg {string} yearPlaceholder
33977  * @cfg {string} dayFormat default 'd'
33978  * @cfg {string} monthFormat default 'm'
33979  * @cfg {string} yearFormat default 'Y'
33980  * @cfg {Number} labellg set the width of label (1-12)
33981  * @cfg {Number} labelmd set the width of label (1-12)
33982  * @cfg {Number} labelsm set the width of label (1-12)
33983  * @cfg {Number} labelxs set the width of label (1-12)
33984
33985  *     
33986  * @constructor
33987  * Create a new DateSplitField
33988  * @param {Object} config The config object
33989  */
33990
33991 Roo.bootstrap.DateSplitField = function(config){
33992     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33993     
33994     this.addEvents({
33995         // raw events
33996          /**
33997          * @event years
33998          * getting the data of years
33999          * @param {Roo.bootstrap.DateSplitField} this
34000          * @param {Object} years
34001          */
34002         "years" : true,
34003         /**
34004          * @event days
34005          * getting the data of days
34006          * @param {Roo.bootstrap.DateSplitField} this
34007          * @param {Object} days
34008          */
34009         "days" : true,
34010         /**
34011          * @event invalid
34012          * Fires after the field has been marked as invalid.
34013          * @param {Roo.form.Field} this
34014          * @param {String} msg The validation message
34015          */
34016         invalid : true,
34017        /**
34018          * @event valid
34019          * Fires after the field has been validated with no errors.
34020          * @param {Roo.form.Field} this
34021          */
34022         valid : true
34023     });
34024 };
34025
34026 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
34027     
34028     fieldLabel : '',
34029     labelAlign : 'top',
34030     labelWidth : 3,
34031     dayAllowBlank : false,
34032     monthAllowBlank : false,
34033     yearAllowBlank : false,
34034     dayPlaceholder : '',
34035     monthPlaceholder : '',
34036     yearPlaceholder : '',
34037     dayFormat : 'd',
34038     monthFormat : 'm',
34039     yearFormat : 'Y',
34040     isFormField : true,
34041     labellg : 0,
34042     labelmd : 0,
34043     labelsm : 0,
34044     labelxs : 0,
34045     
34046     getAutoCreate : function()
34047     {
34048         var cfg = {
34049             tag : 'div',
34050             cls : 'row roo-date-split-field-group',
34051             cn : [
34052                 {
34053                     tag : 'input',
34054                     type : 'hidden',
34055                     cls : 'form-hidden-field roo-date-split-field-group-value',
34056                     name : this.name
34057                 }
34058             ]
34059         };
34060         
34061         var labelCls = 'col-md-12';
34062         var contentCls = 'col-md-4';
34063         
34064         if(this.fieldLabel){
34065             
34066             var label = {
34067                 tag : 'div',
34068                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34069                 cn : [
34070                     {
34071                         tag : 'label',
34072                         html : this.fieldLabel
34073                     }
34074                 ]
34075             };
34076             
34077             if(this.labelAlign == 'left'){
34078             
34079                 if(this.labelWidth > 12){
34080                     label.style = "width: " + this.labelWidth + 'px';
34081                 }
34082
34083                 if(this.labelWidth < 13 && this.labelmd == 0){
34084                     this.labelmd = this.labelWidth;
34085                 }
34086
34087                 if(this.labellg > 0){
34088                     labelCls = ' col-lg-' + this.labellg;
34089                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34090                 }
34091
34092                 if(this.labelmd > 0){
34093                     labelCls = ' col-md-' + this.labelmd;
34094                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34095                 }
34096
34097                 if(this.labelsm > 0){
34098                     labelCls = ' col-sm-' + this.labelsm;
34099                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34100                 }
34101
34102                 if(this.labelxs > 0){
34103                     labelCls = ' col-xs-' + this.labelxs;
34104                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34105                 }
34106             }
34107             
34108             label.cls += ' ' + labelCls;
34109             
34110             cfg.cn.push(label);
34111         }
34112         
34113         Roo.each(['day', 'month', 'year'], function(t){
34114             cfg.cn.push({
34115                 tag : 'div',
34116                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34117             });
34118         }, this);
34119         
34120         return cfg;
34121     },
34122     
34123     inputEl: function ()
34124     {
34125         return this.el.select('.roo-date-split-field-group-value', true).first();
34126     },
34127     
34128     onRender : function(ct, position) 
34129     {
34130         var _this = this;
34131         
34132         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34133         
34134         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34135         
34136         this.dayField = new Roo.bootstrap.ComboBox({
34137             allowBlank : this.dayAllowBlank,
34138             alwaysQuery : true,
34139             displayField : 'value',
34140             editable : false,
34141             fieldLabel : '',
34142             forceSelection : true,
34143             mode : 'local',
34144             placeholder : this.dayPlaceholder,
34145             selectOnFocus : true,
34146             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34147             triggerAction : 'all',
34148             typeAhead : true,
34149             valueField : 'value',
34150             store : new Roo.data.SimpleStore({
34151                 data : (function() {    
34152                     var days = [];
34153                     _this.fireEvent('days', _this, days);
34154                     return days;
34155                 })(),
34156                 fields : [ 'value' ]
34157             }),
34158             listeners : {
34159                 select : function (_self, record, index)
34160                 {
34161                     _this.setValue(_this.getValue());
34162                 }
34163             }
34164         });
34165
34166         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34167         
34168         this.monthField = new Roo.bootstrap.MonthField({
34169             after : '<i class=\"fa fa-calendar\"></i>',
34170             allowBlank : this.monthAllowBlank,
34171             placeholder : this.monthPlaceholder,
34172             readOnly : true,
34173             listeners : {
34174                 render : function (_self)
34175                 {
34176                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34177                         e.preventDefault();
34178                         _self.focus();
34179                     });
34180                 },
34181                 select : function (_self, oldvalue, newvalue)
34182                 {
34183                     _this.setValue(_this.getValue());
34184                 }
34185             }
34186         });
34187         
34188         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34189         
34190         this.yearField = new Roo.bootstrap.ComboBox({
34191             allowBlank : this.yearAllowBlank,
34192             alwaysQuery : true,
34193             displayField : 'value',
34194             editable : false,
34195             fieldLabel : '',
34196             forceSelection : true,
34197             mode : 'local',
34198             placeholder : this.yearPlaceholder,
34199             selectOnFocus : true,
34200             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34201             triggerAction : 'all',
34202             typeAhead : true,
34203             valueField : 'value',
34204             store : new Roo.data.SimpleStore({
34205                 data : (function() {
34206                     var years = [];
34207                     _this.fireEvent('years', _this, years);
34208                     return years;
34209                 })(),
34210                 fields : [ 'value' ]
34211             }),
34212             listeners : {
34213                 select : function (_self, record, index)
34214                 {
34215                     _this.setValue(_this.getValue());
34216                 }
34217             }
34218         });
34219
34220         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34221     },
34222     
34223     setValue : function(v, format)
34224     {
34225         this.inputEl.dom.value = v;
34226         
34227         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34228         
34229         var d = Date.parseDate(v, f);
34230         
34231         if(!d){
34232             this.validate();
34233             return;
34234         }
34235         
34236         this.setDay(d.format(this.dayFormat));
34237         this.setMonth(d.format(this.monthFormat));
34238         this.setYear(d.format(this.yearFormat));
34239         
34240         this.validate();
34241         
34242         return;
34243     },
34244     
34245     setDay : function(v)
34246     {
34247         this.dayField.setValue(v);
34248         this.inputEl.dom.value = this.getValue();
34249         this.validate();
34250         return;
34251     },
34252     
34253     setMonth : function(v)
34254     {
34255         this.monthField.setValue(v, true);
34256         this.inputEl.dom.value = this.getValue();
34257         this.validate();
34258         return;
34259     },
34260     
34261     setYear : function(v)
34262     {
34263         this.yearField.setValue(v);
34264         this.inputEl.dom.value = this.getValue();
34265         this.validate();
34266         return;
34267     },
34268     
34269     getDay : function()
34270     {
34271         return this.dayField.getValue();
34272     },
34273     
34274     getMonth : function()
34275     {
34276         return this.monthField.getValue();
34277     },
34278     
34279     getYear : function()
34280     {
34281         return this.yearField.getValue();
34282     },
34283     
34284     getValue : function()
34285     {
34286         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34287         
34288         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34289         
34290         return date;
34291     },
34292     
34293     reset : function()
34294     {
34295         this.setDay('');
34296         this.setMonth('');
34297         this.setYear('');
34298         this.inputEl.dom.value = '';
34299         this.validate();
34300         return;
34301     },
34302     
34303     validate : function()
34304     {
34305         var d = this.dayField.validate();
34306         var m = this.monthField.validate();
34307         var y = this.yearField.validate();
34308         
34309         var valid = true;
34310         
34311         if(
34312                 (!this.dayAllowBlank && !d) ||
34313                 (!this.monthAllowBlank && !m) ||
34314                 (!this.yearAllowBlank && !y)
34315         ){
34316             valid = false;
34317         }
34318         
34319         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34320             return valid;
34321         }
34322         
34323         if(valid){
34324             this.markValid();
34325             return valid;
34326         }
34327         
34328         this.markInvalid();
34329         
34330         return valid;
34331     },
34332     
34333     markValid : function()
34334     {
34335         
34336         var label = this.el.select('label', true).first();
34337         var icon = this.el.select('i.fa-star', true).first();
34338
34339         if(label && icon){
34340             icon.remove();
34341         }
34342         
34343         this.fireEvent('valid', this);
34344     },
34345     
34346      /**
34347      * Mark this field as invalid
34348      * @param {String} msg The validation message
34349      */
34350     markInvalid : function(msg)
34351     {
34352         
34353         var label = this.el.select('label', true).first();
34354         var icon = this.el.select('i.fa-star', true).first();
34355
34356         if(label && !icon){
34357             this.el.select('.roo-date-split-field-label', true).createChild({
34358                 tag : 'i',
34359                 cls : 'text-danger fa fa-lg fa-star',
34360                 tooltip : 'This field is required',
34361                 style : 'margin-right:5px;'
34362             }, label, true);
34363         }
34364         
34365         this.fireEvent('invalid', this, msg);
34366     },
34367     
34368     clearInvalid : function()
34369     {
34370         var label = this.el.select('label', true).first();
34371         var icon = this.el.select('i.fa-star', true).first();
34372
34373         if(label && icon){
34374             icon.remove();
34375         }
34376         
34377         this.fireEvent('valid', this);
34378     },
34379     
34380     getName: function()
34381     {
34382         return this.name;
34383     }
34384     
34385 });
34386
34387  /**
34388  *
34389  * This is based on 
34390  * http://masonry.desandro.com
34391  *
34392  * The idea is to render all the bricks based on vertical width...
34393  *
34394  * The original code extends 'outlayer' - we might need to use that....
34395  * 
34396  */
34397
34398
34399 /**
34400  * @class Roo.bootstrap.LayoutMasonry
34401  * @extends Roo.bootstrap.Component
34402  * Bootstrap Layout Masonry class
34403  * 
34404  * @constructor
34405  * Create a new Element
34406  * @param {Object} config The config object
34407  */
34408
34409 Roo.bootstrap.LayoutMasonry = function(config){
34410     
34411     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34412     
34413     this.bricks = [];
34414     
34415     Roo.bootstrap.LayoutMasonry.register(this);
34416     
34417     this.addEvents({
34418         // raw events
34419         /**
34420          * @event layout
34421          * Fire after layout the items
34422          * @param {Roo.bootstrap.LayoutMasonry} this
34423          * @param {Roo.EventObject} e
34424          */
34425         "layout" : true
34426     });
34427     
34428 };
34429
34430 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34431     
34432     /**
34433      * @cfg {Boolean} isLayoutInstant = no animation?
34434      */   
34435     isLayoutInstant : false, // needed?
34436    
34437     /**
34438      * @cfg {Number} boxWidth  width of the columns
34439      */   
34440     boxWidth : 450,
34441     
34442       /**
34443      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34444      */   
34445     boxHeight : 0,
34446     
34447     /**
34448      * @cfg {Number} padWidth padding below box..
34449      */   
34450     padWidth : 10, 
34451     
34452     /**
34453      * @cfg {Number} gutter gutter width..
34454      */   
34455     gutter : 10,
34456     
34457      /**
34458      * @cfg {Number} maxCols maximum number of columns
34459      */   
34460     
34461     maxCols: 0,
34462     
34463     /**
34464      * @cfg {Boolean} isAutoInitial defalut true
34465      */   
34466     isAutoInitial : true, 
34467     
34468     containerWidth: 0,
34469     
34470     /**
34471      * @cfg {Boolean} isHorizontal defalut false
34472      */   
34473     isHorizontal : false, 
34474
34475     currentSize : null,
34476     
34477     tag: 'div',
34478     
34479     cls: '',
34480     
34481     bricks: null, //CompositeElement
34482     
34483     cols : 1,
34484     
34485     _isLayoutInited : false,
34486     
34487 //    isAlternative : false, // only use for vertical layout...
34488     
34489     /**
34490      * @cfg {Number} alternativePadWidth padding below box..
34491      */   
34492     alternativePadWidth : 50,
34493     
34494     selectedBrick : [],
34495     
34496     getAutoCreate : function(){
34497         
34498         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34499         
34500         var cfg = {
34501             tag: this.tag,
34502             cls: 'blog-masonary-wrapper ' + this.cls,
34503             cn : {
34504                 cls : 'mas-boxes masonary'
34505             }
34506         };
34507         
34508         return cfg;
34509     },
34510     
34511     getChildContainer: function( )
34512     {
34513         if (this.boxesEl) {
34514             return this.boxesEl;
34515         }
34516         
34517         this.boxesEl = this.el.select('.mas-boxes').first();
34518         
34519         return this.boxesEl;
34520     },
34521     
34522     
34523     initEvents : function()
34524     {
34525         var _this = this;
34526         
34527         if(this.isAutoInitial){
34528             Roo.log('hook children rendered');
34529             this.on('childrenrendered', function() {
34530                 Roo.log('children rendered');
34531                 _this.initial();
34532             } ,this);
34533         }
34534     },
34535     
34536     initial : function()
34537     {
34538         this.selectedBrick = [];
34539         
34540         this.currentSize = this.el.getBox(true);
34541         
34542         Roo.EventManager.onWindowResize(this.resize, this); 
34543
34544         if(!this.isAutoInitial){
34545             this.layout();
34546             return;
34547         }
34548         
34549         this.layout();
34550         
34551         return;
34552         //this.layout.defer(500,this);
34553         
34554     },
34555     
34556     resize : function()
34557     {
34558         var cs = this.el.getBox(true);
34559         
34560         if (
34561                 this.currentSize.width == cs.width && 
34562                 this.currentSize.x == cs.x && 
34563                 this.currentSize.height == cs.height && 
34564                 this.currentSize.y == cs.y 
34565         ) {
34566             Roo.log("no change in with or X or Y");
34567             return;
34568         }
34569         
34570         this.currentSize = cs;
34571         
34572         this.layout();
34573         
34574     },
34575     
34576     layout : function()
34577     {   
34578         this._resetLayout();
34579         
34580         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34581         
34582         this.layoutItems( isInstant );
34583       
34584         this._isLayoutInited = true;
34585         
34586         this.fireEvent('layout', this);
34587         
34588     },
34589     
34590     _resetLayout : function()
34591     {
34592         if(this.isHorizontal){
34593             this.horizontalMeasureColumns();
34594             return;
34595         }
34596         
34597         this.verticalMeasureColumns();
34598         
34599     },
34600     
34601     verticalMeasureColumns : function()
34602     {
34603         this.getContainerWidth();
34604         
34605 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34606 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34607 //            return;
34608 //        }
34609         
34610         var boxWidth = this.boxWidth + this.padWidth;
34611         
34612         if(this.containerWidth < this.boxWidth){
34613             boxWidth = this.containerWidth
34614         }
34615         
34616         var containerWidth = this.containerWidth;
34617         
34618         var cols = Math.floor(containerWidth / boxWidth);
34619         
34620         this.cols = Math.max( cols, 1 );
34621         
34622         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34623         
34624         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34625         
34626         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34627         
34628         this.colWidth = boxWidth + avail - this.padWidth;
34629         
34630         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34631         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34632     },
34633     
34634     horizontalMeasureColumns : function()
34635     {
34636         this.getContainerWidth();
34637         
34638         var boxWidth = this.boxWidth;
34639         
34640         if(this.containerWidth < boxWidth){
34641             boxWidth = this.containerWidth;
34642         }
34643         
34644         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34645         
34646         this.el.setHeight(boxWidth);
34647         
34648     },
34649     
34650     getContainerWidth : function()
34651     {
34652         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34653     },
34654     
34655     layoutItems : function( isInstant )
34656     {
34657         Roo.log(this.bricks);
34658         
34659         var items = Roo.apply([], this.bricks);
34660         
34661         if(this.isHorizontal){
34662             this._horizontalLayoutItems( items , isInstant );
34663             return;
34664         }
34665         
34666 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34667 //            this._verticalAlternativeLayoutItems( items , isInstant );
34668 //            return;
34669 //        }
34670         
34671         this._verticalLayoutItems( items , isInstant );
34672         
34673     },
34674     
34675     _verticalLayoutItems : function ( items , isInstant)
34676     {
34677         if ( !items || !items.length ) {
34678             return;
34679         }
34680         
34681         var standard = [
34682             ['xs', 'xs', 'xs', 'tall'],
34683             ['xs', 'xs', 'tall'],
34684             ['xs', 'xs', 'sm'],
34685             ['xs', 'xs', 'xs'],
34686             ['xs', 'tall'],
34687             ['xs', 'sm'],
34688             ['xs', 'xs'],
34689             ['xs'],
34690             
34691             ['sm', 'xs', 'xs'],
34692             ['sm', 'xs'],
34693             ['sm'],
34694             
34695             ['tall', 'xs', 'xs', 'xs'],
34696             ['tall', 'xs', 'xs'],
34697             ['tall', 'xs'],
34698             ['tall']
34699             
34700         ];
34701         
34702         var queue = [];
34703         
34704         var boxes = [];
34705         
34706         var box = [];
34707         
34708         Roo.each(items, function(item, k){
34709             
34710             switch (item.size) {
34711                 // these layouts take up a full box,
34712                 case 'md' :
34713                 case 'md-left' :
34714                 case 'md-right' :
34715                 case 'wide' :
34716                     
34717                     if(box.length){
34718                         boxes.push(box);
34719                         box = [];
34720                     }
34721                     
34722                     boxes.push([item]);
34723                     
34724                     break;
34725                     
34726                 case 'xs' :
34727                 case 'sm' :
34728                 case 'tall' :
34729                     
34730                     box.push(item);
34731                     
34732                     break;
34733                 default :
34734                     break;
34735                     
34736             }
34737             
34738         }, this);
34739         
34740         if(box.length){
34741             boxes.push(box);
34742             box = [];
34743         }
34744         
34745         var filterPattern = function(box, length)
34746         {
34747             if(!box.length){
34748                 return;
34749             }
34750             
34751             var match = false;
34752             
34753             var pattern = box.slice(0, length);
34754             
34755             var format = [];
34756             
34757             Roo.each(pattern, function(i){
34758                 format.push(i.size);
34759             }, this);
34760             
34761             Roo.each(standard, function(s){
34762                 
34763                 if(String(s) != String(format)){
34764                     return;
34765                 }
34766                 
34767                 match = true;
34768                 return false;
34769                 
34770             }, this);
34771             
34772             if(!match && length == 1){
34773                 return;
34774             }
34775             
34776             if(!match){
34777                 filterPattern(box, length - 1);
34778                 return;
34779             }
34780                 
34781             queue.push(pattern);
34782
34783             box = box.slice(length, box.length);
34784
34785             filterPattern(box, 4);
34786
34787             return;
34788             
34789         }
34790         
34791         Roo.each(boxes, function(box, k){
34792             
34793             if(!box.length){
34794                 return;
34795             }
34796             
34797             if(box.length == 1){
34798                 queue.push(box);
34799                 return;
34800             }
34801             
34802             filterPattern(box, 4);
34803             
34804         }, this);
34805         
34806         this._processVerticalLayoutQueue( queue, isInstant );
34807         
34808     },
34809     
34810 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34811 //    {
34812 //        if ( !items || !items.length ) {
34813 //            return;
34814 //        }
34815 //
34816 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34817 //        
34818 //    },
34819     
34820     _horizontalLayoutItems : function ( items , isInstant)
34821     {
34822         if ( !items || !items.length || items.length < 3) {
34823             return;
34824         }
34825         
34826         items.reverse();
34827         
34828         var eItems = items.slice(0, 3);
34829         
34830         items = items.slice(3, items.length);
34831         
34832         var standard = [
34833             ['xs', 'xs', 'xs', 'wide'],
34834             ['xs', 'xs', 'wide'],
34835             ['xs', 'xs', 'sm'],
34836             ['xs', 'xs', 'xs'],
34837             ['xs', 'wide'],
34838             ['xs', 'sm'],
34839             ['xs', 'xs'],
34840             ['xs'],
34841             
34842             ['sm', 'xs', 'xs'],
34843             ['sm', 'xs'],
34844             ['sm'],
34845             
34846             ['wide', 'xs', 'xs', 'xs'],
34847             ['wide', 'xs', 'xs'],
34848             ['wide', 'xs'],
34849             ['wide'],
34850             
34851             ['wide-thin']
34852         ];
34853         
34854         var queue = [];
34855         
34856         var boxes = [];
34857         
34858         var box = [];
34859         
34860         Roo.each(items, function(item, k){
34861             
34862             switch (item.size) {
34863                 case 'md' :
34864                 case 'md-left' :
34865                 case 'md-right' :
34866                 case 'tall' :
34867                     
34868                     if(box.length){
34869                         boxes.push(box);
34870                         box = [];
34871                     }
34872                     
34873                     boxes.push([item]);
34874                     
34875                     break;
34876                     
34877                 case 'xs' :
34878                 case 'sm' :
34879                 case 'wide' :
34880                 case 'wide-thin' :
34881                     
34882                     box.push(item);
34883                     
34884                     break;
34885                 default :
34886                     break;
34887                     
34888             }
34889             
34890         }, this);
34891         
34892         if(box.length){
34893             boxes.push(box);
34894             box = [];
34895         }
34896         
34897         var filterPattern = function(box, length)
34898         {
34899             if(!box.length){
34900                 return;
34901             }
34902             
34903             var match = false;
34904             
34905             var pattern = box.slice(0, length);
34906             
34907             var format = [];
34908             
34909             Roo.each(pattern, function(i){
34910                 format.push(i.size);
34911             }, this);
34912             
34913             Roo.each(standard, function(s){
34914                 
34915                 if(String(s) != String(format)){
34916                     return;
34917                 }
34918                 
34919                 match = true;
34920                 return false;
34921                 
34922             }, this);
34923             
34924             if(!match && length == 1){
34925                 return;
34926             }
34927             
34928             if(!match){
34929                 filterPattern(box, length - 1);
34930                 return;
34931             }
34932                 
34933             queue.push(pattern);
34934
34935             box = box.slice(length, box.length);
34936
34937             filterPattern(box, 4);
34938
34939             return;
34940             
34941         }
34942         
34943         Roo.each(boxes, function(box, k){
34944             
34945             if(!box.length){
34946                 return;
34947             }
34948             
34949             if(box.length == 1){
34950                 queue.push(box);
34951                 return;
34952             }
34953             
34954             filterPattern(box, 4);
34955             
34956         }, this);
34957         
34958         
34959         var prune = [];
34960         
34961         var pos = this.el.getBox(true);
34962         
34963         var minX = pos.x;
34964         
34965         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34966         
34967         var hit_end = false;
34968         
34969         Roo.each(queue, function(box){
34970             
34971             if(hit_end){
34972                 
34973                 Roo.each(box, function(b){
34974                 
34975                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34976                     b.el.hide();
34977
34978                 }, this);
34979
34980                 return;
34981             }
34982             
34983             var mx = 0;
34984             
34985             Roo.each(box, function(b){
34986                 
34987                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34988                 b.el.show();
34989
34990                 mx = Math.max(mx, b.x);
34991                 
34992             }, this);
34993             
34994             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34995             
34996             if(maxX < minX){
34997                 
34998                 Roo.each(box, function(b){
34999                 
35000                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
35001                     b.el.hide();
35002                     
35003                 }, this);
35004                 
35005                 hit_end = true;
35006                 
35007                 return;
35008             }
35009             
35010             prune.push(box);
35011             
35012         }, this);
35013         
35014         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
35015     },
35016     
35017     /** Sets position of item in DOM
35018     * @param {Element} item
35019     * @param {Number} x - horizontal position
35020     * @param {Number} y - vertical position
35021     * @param {Boolean} isInstant - disables transitions
35022     */
35023     _processVerticalLayoutQueue : function( queue, isInstant )
35024     {
35025         var pos = this.el.getBox(true);
35026         var x = pos.x;
35027         var y = pos.y;
35028         var maxY = [];
35029         
35030         for (var i = 0; i < this.cols; i++){
35031             maxY[i] = pos.y;
35032         }
35033         
35034         Roo.each(queue, function(box, k){
35035             
35036             var col = k % this.cols;
35037             
35038             Roo.each(box, function(b,kk){
35039                 
35040                 b.el.position('absolute');
35041                 
35042                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35043                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35044                 
35045                 if(b.size == 'md-left' || b.size == 'md-right'){
35046                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35047                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35048                 }
35049                 
35050                 b.el.setWidth(width);
35051                 b.el.setHeight(height);
35052                 // iframe?
35053                 b.el.select('iframe',true).setSize(width,height);
35054                 
35055             }, this);
35056             
35057             for (var i = 0; i < this.cols; i++){
35058                 
35059                 if(maxY[i] < maxY[col]){
35060                     col = i;
35061                     continue;
35062                 }
35063                 
35064                 col = Math.min(col, i);
35065                 
35066             }
35067             
35068             x = pos.x + col * (this.colWidth + this.padWidth);
35069             
35070             y = maxY[col];
35071             
35072             var positions = [];
35073             
35074             switch (box.length){
35075                 case 1 :
35076                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35077                     break;
35078                 case 2 :
35079                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35080                     break;
35081                 case 3 :
35082                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35083                     break;
35084                 case 4 :
35085                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35086                     break;
35087                 default :
35088                     break;
35089             }
35090             
35091             Roo.each(box, function(b,kk){
35092                 
35093                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35094                 
35095                 var sz = b.el.getSize();
35096                 
35097                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35098                 
35099             }, this);
35100             
35101         }, this);
35102         
35103         var mY = 0;
35104         
35105         for (var i = 0; i < this.cols; i++){
35106             mY = Math.max(mY, maxY[i]);
35107         }
35108         
35109         this.el.setHeight(mY - pos.y);
35110         
35111     },
35112     
35113 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35114 //    {
35115 //        var pos = this.el.getBox(true);
35116 //        var x = pos.x;
35117 //        var y = pos.y;
35118 //        var maxX = pos.right;
35119 //        
35120 //        var maxHeight = 0;
35121 //        
35122 //        Roo.each(items, function(item, k){
35123 //            
35124 //            var c = k % 2;
35125 //            
35126 //            item.el.position('absolute');
35127 //                
35128 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35129 //
35130 //            item.el.setWidth(width);
35131 //
35132 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35133 //
35134 //            item.el.setHeight(height);
35135 //            
35136 //            if(c == 0){
35137 //                item.el.setXY([x, y], isInstant ? false : true);
35138 //            } else {
35139 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35140 //            }
35141 //            
35142 //            y = y + height + this.alternativePadWidth;
35143 //            
35144 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35145 //            
35146 //        }, this);
35147 //        
35148 //        this.el.setHeight(maxHeight);
35149 //        
35150 //    },
35151     
35152     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35153     {
35154         var pos = this.el.getBox(true);
35155         
35156         var minX = pos.x;
35157         var minY = pos.y;
35158         
35159         var maxX = pos.right;
35160         
35161         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35162         
35163         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35164         
35165         Roo.each(queue, function(box, k){
35166             
35167             Roo.each(box, function(b, kk){
35168                 
35169                 b.el.position('absolute');
35170                 
35171                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35172                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35173                 
35174                 if(b.size == 'md-left' || b.size == 'md-right'){
35175                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35176                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35177                 }
35178                 
35179                 b.el.setWidth(width);
35180                 b.el.setHeight(height);
35181                 
35182             }, this);
35183             
35184             if(!box.length){
35185                 return;
35186             }
35187             
35188             var positions = [];
35189             
35190             switch (box.length){
35191                 case 1 :
35192                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35193                     break;
35194                 case 2 :
35195                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35196                     break;
35197                 case 3 :
35198                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35199                     break;
35200                 case 4 :
35201                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35202                     break;
35203                 default :
35204                     break;
35205             }
35206             
35207             Roo.each(box, function(b,kk){
35208                 
35209                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35210                 
35211                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35212                 
35213             }, this);
35214             
35215         }, this);
35216         
35217     },
35218     
35219     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35220     {
35221         Roo.each(eItems, function(b,k){
35222             
35223             b.size = (k == 0) ? 'sm' : 'xs';
35224             b.x = (k == 0) ? 2 : 1;
35225             b.y = (k == 0) ? 2 : 1;
35226             
35227             b.el.position('absolute');
35228             
35229             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35230                 
35231             b.el.setWidth(width);
35232             
35233             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35234             
35235             b.el.setHeight(height);
35236             
35237         }, this);
35238
35239         var positions = [];
35240         
35241         positions.push({
35242             x : maxX - this.unitWidth * 2 - this.gutter,
35243             y : minY
35244         });
35245         
35246         positions.push({
35247             x : maxX - this.unitWidth,
35248             y : minY + (this.unitWidth + this.gutter) * 2
35249         });
35250         
35251         positions.push({
35252             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35253             y : minY
35254         });
35255         
35256         Roo.each(eItems, function(b,k){
35257             
35258             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35259
35260         }, this);
35261         
35262     },
35263     
35264     getVerticalOneBoxColPositions : function(x, y, box)
35265     {
35266         var pos = [];
35267         
35268         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35269         
35270         if(box[0].size == 'md-left'){
35271             rand = 0;
35272         }
35273         
35274         if(box[0].size == 'md-right'){
35275             rand = 1;
35276         }
35277         
35278         pos.push({
35279             x : x + (this.unitWidth + this.gutter) * rand,
35280             y : y
35281         });
35282         
35283         return pos;
35284     },
35285     
35286     getVerticalTwoBoxColPositions : function(x, y, box)
35287     {
35288         var pos = [];
35289         
35290         if(box[0].size == 'xs'){
35291             
35292             pos.push({
35293                 x : x,
35294                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35295             });
35296
35297             pos.push({
35298                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35299                 y : y
35300             });
35301             
35302             return pos;
35303             
35304         }
35305         
35306         pos.push({
35307             x : x,
35308             y : y
35309         });
35310
35311         pos.push({
35312             x : x + (this.unitWidth + this.gutter) * 2,
35313             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35314         });
35315         
35316         return pos;
35317         
35318     },
35319     
35320     getVerticalThreeBoxColPositions : function(x, y, box)
35321     {
35322         var pos = [];
35323         
35324         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35325             
35326             pos.push({
35327                 x : x,
35328                 y : y
35329             });
35330
35331             pos.push({
35332                 x : x + (this.unitWidth + this.gutter) * 1,
35333                 y : y
35334             });
35335             
35336             pos.push({
35337                 x : x + (this.unitWidth + this.gutter) * 2,
35338                 y : y
35339             });
35340             
35341             return pos;
35342             
35343         }
35344         
35345         if(box[0].size == 'xs' && box[1].size == 'xs'){
35346             
35347             pos.push({
35348                 x : x,
35349                 y : y
35350             });
35351
35352             pos.push({
35353                 x : x,
35354                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35355             });
35356             
35357             pos.push({
35358                 x : x + (this.unitWidth + this.gutter) * 1,
35359                 y : y
35360             });
35361             
35362             return pos;
35363             
35364         }
35365         
35366         pos.push({
35367             x : x,
35368             y : y
35369         });
35370
35371         pos.push({
35372             x : x + (this.unitWidth + this.gutter) * 2,
35373             y : y
35374         });
35375
35376         pos.push({
35377             x : x + (this.unitWidth + this.gutter) * 2,
35378             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35379         });
35380             
35381         return pos;
35382         
35383     },
35384     
35385     getVerticalFourBoxColPositions : function(x, y, box)
35386     {
35387         var pos = [];
35388         
35389         if(box[0].size == 'xs'){
35390             
35391             pos.push({
35392                 x : x,
35393                 y : y
35394             });
35395
35396             pos.push({
35397                 x : x,
35398                 y : y + (this.unitHeight + this.gutter) * 1
35399             });
35400             
35401             pos.push({
35402                 x : x,
35403                 y : y + (this.unitHeight + this.gutter) * 2
35404             });
35405             
35406             pos.push({
35407                 x : x + (this.unitWidth + this.gutter) * 1,
35408                 y : y
35409             });
35410             
35411             return pos;
35412             
35413         }
35414         
35415         pos.push({
35416             x : x,
35417             y : y
35418         });
35419
35420         pos.push({
35421             x : x + (this.unitWidth + this.gutter) * 2,
35422             y : y
35423         });
35424
35425         pos.push({
35426             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35427             y : y + (this.unitHeight + this.gutter) * 1
35428         });
35429
35430         pos.push({
35431             x : x + (this.unitWidth + this.gutter) * 2,
35432             y : y + (this.unitWidth + this.gutter) * 2
35433         });
35434
35435         return pos;
35436         
35437     },
35438     
35439     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35440     {
35441         var pos = [];
35442         
35443         if(box[0].size == 'md-left'){
35444             pos.push({
35445                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35446                 y : minY
35447             });
35448             
35449             return pos;
35450         }
35451         
35452         if(box[0].size == 'md-right'){
35453             pos.push({
35454                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35455                 y : minY + (this.unitWidth + this.gutter) * 1
35456             });
35457             
35458             return pos;
35459         }
35460         
35461         var rand = Math.floor(Math.random() * (4 - box[0].y));
35462         
35463         pos.push({
35464             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35465             y : minY + (this.unitWidth + this.gutter) * rand
35466         });
35467         
35468         return pos;
35469         
35470     },
35471     
35472     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35473     {
35474         var pos = [];
35475         
35476         if(box[0].size == 'xs'){
35477             
35478             pos.push({
35479                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35480                 y : minY
35481             });
35482
35483             pos.push({
35484                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35485                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35486             });
35487             
35488             return pos;
35489             
35490         }
35491         
35492         pos.push({
35493             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35494             y : minY
35495         });
35496
35497         pos.push({
35498             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35499             y : minY + (this.unitWidth + this.gutter) * 2
35500         });
35501         
35502         return pos;
35503         
35504     },
35505     
35506     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35507     {
35508         var pos = [];
35509         
35510         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].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[1].x - this.gutter * (box[1].x - 1),
35519                 y : minY + (this.unitWidth + this.gutter) * 1
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) * 2
35525             });
35526             
35527             return pos;
35528             
35529         }
35530         
35531         if(box[0].size == 'xs' && box[1].size == 'xs'){
35532             
35533             pos.push({
35534                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35535                 y : minY
35536             });
35537
35538             pos.push({
35539                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35540                 y : minY
35541             });
35542             
35543             pos.push({
35544                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35545                 y : minY + (this.unitWidth + this.gutter) * 1
35546             });
35547             
35548             return pos;
35549             
35550         }
35551         
35552         pos.push({
35553             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35554             y : minY
35555         });
35556
35557         pos.push({
35558             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35559             y : minY + (this.unitWidth + this.gutter) * 2
35560         });
35561
35562         pos.push({
35563             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35564             y : minY + (this.unitWidth + this.gutter) * 2
35565         });
35566             
35567         return pos;
35568         
35569     },
35570     
35571     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35572     {
35573         var pos = [];
35574         
35575         if(box[0].size == 'xs'){
35576             
35577             pos.push({
35578                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35579                 y : minY
35580             });
35581
35582             pos.push({
35583                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35584                 y : minY
35585             });
35586             
35587             pos.push({
35588                 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),
35589                 y : minY
35590             });
35591             
35592             pos.push({
35593                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35594                 y : minY + (this.unitWidth + this.gutter) * 1
35595             });
35596             
35597             return pos;
35598             
35599         }
35600         
35601         pos.push({
35602             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35603             y : minY
35604         });
35605         
35606         pos.push({
35607             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35608             y : minY + (this.unitWidth + this.gutter) * 2
35609         });
35610         
35611         pos.push({
35612             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35613             y : minY + (this.unitWidth + this.gutter) * 2
35614         });
35615         
35616         pos.push({
35617             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),
35618             y : minY + (this.unitWidth + this.gutter) * 2
35619         });
35620
35621         return pos;
35622         
35623     },
35624     
35625     /**
35626     * remove a Masonry Brick
35627     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35628     */
35629     removeBrick : function(brick_id)
35630     {
35631         if (!brick_id) {
35632             return;
35633         }
35634         
35635         for (var i = 0; i<this.bricks.length; i++) {
35636             if (this.bricks[i].id == brick_id) {
35637                 this.bricks.splice(i,1);
35638                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35639                 this.initial();
35640             }
35641         }
35642     },
35643     
35644     /**
35645     * adds a Masonry Brick
35646     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35647     */
35648     addBrick : function(cfg)
35649     {
35650         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35651         //this.register(cn);
35652         cn.parentId = this.id;
35653         cn.render(this.el);
35654         return cn;
35655     },
35656     
35657     /**
35658     * register a Masonry Brick
35659     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35660     */
35661     
35662     register : function(brick)
35663     {
35664         this.bricks.push(brick);
35665         brick.masonryId = this.id;
35666     },
35667     
35668     /**
35669     * clear all the Masonry Brick
35670     */
35671     clearAll : function()
35672     {
35673         this.bricks = [];
35674         //this.getChildContainer().dom.innerHTML = "";
35675         this.el.dom.innerHTML = '';
35676     },
35677     
35678     getSelected : function()
35679     {
35680         if (!this.selectedBrick) {
35681             return false;
35682         }
35683         
35684         return this.selectedBrick;
35685     }
35686 });
35687
35688 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35689     
35690     groups: {},
35691      /**
35692     * register a Masonry Layout
35693     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35694     */
35695     
35696     register : function(layout)
35697     {
35698         this.groups[layout.id] = layout;
35699     },
35700     /**
35701     * fetch a  Masonry Layout based on the masonry layout ID
35702     * @param {string} the masonry layout to add
35703     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35704     */
35705     
35706     get: function(layout_id) {
35707         if (typeof(this.groups[layout_id]) == 'undefined') {
35708             return false;
35709         }
35710         return this.groups[layout_id] ;
35711     }
35712     
35713     
35714     
35715 });
35716
35717  
35718
35719  /**
35720  *
35721  * This is based on 
35722  * http://masonry.desandro.com
35723  *
35724  * The idea is to render all the bricks based on vertical width...
35725  *
35726  * The original code extends 'outlayer' - we might need to use that....
35727  * 
35728  */
35729
35730
35731 /**
35732  * @class Roo.bootstrap.LayoutMasonryAuto
35733  * @extends Roo.bootstrap.Component
35734  * Bootstrap Layout Masonry class
35735  * 
35736  * @constructor
35737  * Create a new Element
35738  * @param {Object} config The config object
35739  */
35740
35741 Roo.bootstrap.LayoutMasonryAuto = function(config){
35742     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35743 };
35744
35745 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35746     
35747       /**
35748      * @cfg {Boolean} isFitWidth  - resize the width..
35749      */   
35750     isFitWidth : false,  // options..
35751     /**
35752      * @cfg {Boolean} isOriginLeft = left align?
35753      */   
35754     isOriginLeft : true,
35755     /**
35756      * @cfg {Boolean} isOriginTop = top align?
35757      */   
35758     isOriginTop : false,
35759     /**
35760      * @cfg {Boolean} isLayoutInstant = no animation?
35761      */   
35762     isLayoutInstant : false, // needed?
35763     /**
35764      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35765      */   
35766     isResizingContainer : true,
35767     /**
35768      * @cfg {Number} columnWidth  width of the columns 
35769      */   
35770     
35771     columnWidth : 0,
35772     
35773     /**
35774      * @cfg {Number} maxCols maximum number of columns
35775      */   
35776     
35777     maxCols: 0,
35778     /**
35779      * @cfg {Number} padHeight padding below box..
35780      */   
35781     
35782     padHeight : 10, 
35783     
35784     /**
35785      * @cfg {Boolean} isAutoInitial defalut true
35786      */   
35787     
35788     isAutoInitial : true, 
35789     
35790     // private?
35791     gutter : 0,
35792     
35793     containerWidth: 0,
35794     initialColumnWidth : 0,
35795     currentSize : null,
35796     
35797     colYs : null, // array.
35798     maxY : 0,
35799     padWidth: 10,
35800     
35801     
35802     tag: 'div',
35803     cls: '',
35804     bricks: null, //CompositeElement
35805     cols : 0, // array?
35806     // element : null, // wrapped now this.el
35807     _isLayoutInited : null, 
35808     
35809     
35810     getAutoCreate : function(){
35811         
35812         var cfg = {
35813             tag: this.tag,
35814             cls: 'blog-masonary-wrapper ' + this.cls,
35815             cn : {
35816                 cls : 'mas-boxes masonary'
35817             }
35818         };
35819         
35820         return cfg;
35821     },
35822     
35823     getChildContainer: function( )
35824     {
35825         if (this.boxesEl) {
35826             return this.boxesEl;
35827         }
35828         
35829         this.boxesEl = this.el.select('.mas-boxes').first();
35830         
35831         return this.boxesEl;
35832     },
35833     
35834     
35835     initEvents : function()
35836     {
35837         var _this = this;
35838         
35839         if(this.isAutoInitial){
35840             Roo.log('hook children rendered');
35841             this.on('childrenrendered', function() {
35842                 Roo.log('children rendered');
35843                 _this.initial();
35844             } ,this);
35845         }
35846         
35847     },
35848     
35849     initial : function()
35850     {
35851         this.reloadItems();
35852
35853         this.currentSize = this.el.getBox(true);
35854
35855         /// was window resize... - let's see if this works..
35856         Roo.EventManager.onWindowResize(this.resize, this); 
35857
35858         if(!this.isAutoInitial){
35859             this.layout();
35860             return;
35861         }
35862         
35863         this.layout.defer(500,this);
35864     },
35865     
35866     reloadItems: function()
35867     {
35868         this.bricks = this.el.select('.masonry-brick', true);
35869         
35870         this.bricks.each(function(b) {
35871             //Roo.log(b.getSize());
35872             if (!b.attr('originalwidth')) {
35873                 b.attr('originalwidth',  b.getSize().width);
35874             }
35875             
35876         });
35877         
35878         Roo.log(this.bricks.elements.length);
35879     },
35880     
35881     resize : function()
35882     {
35883         Roo.log('resize');
35884         var cs = this.el.getBox(true);
35885         
35886         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35887             Roo.log("no change in with or X");
35888             return;
35889         }
35890         this.currentSize = cs;
35891         this.layout();
35892     },
35893     
35894     layout : function()
35895     {
35896          Roo.log('layout');
35897         this._resetLayout();
35898         //this._manageStamps();
35899       
35900         // don't animate first layout
35901         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35902         this.layoutItems( isInstant );
35903       
35904         // flag for initalized
35905         this._isLayoutInited = true;
35906     },
35907     
35908     layoutItems : function( isInstant )
35909     {
35910         //var items = this._getItemsForLayout( this.items );
35911         // original code supports filtering layout items.. we just ignore it..
35912         
35913         this._layoutItems( this.bricks , isInstant );
35914       
35915         this._postLayout();
35916     },
35917     _layoutItems : function ( items , isInstant)
35918     {
35919        //this.fireEvent( 'layout', this, items );
35920     
35921
35922         if ( !items || !items.elements.length ) {
35923           // no items, emit event with empty array
35924             return;
35925         }
35926
35927         var queue = [];
35928         items.each(function(item) {
35929             Roo.log("layout item");
35930             Roo.log(item);
35931             // get x/y object from method
35932             var position = this._getItemLayoutPosition( item );
35933             // enqueue
35934             position.item = item;
35935             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35936             queue.push( position );
35937         }, this);
35938       
35939         this._processLayoutQueue( queue );
35940     },
35941     /** Sets position of item in DOM
35942     * @param {Element} item
35943     * @param {Number} x - horizontal position
35944     * @param {Number} y - vertical position
35945     * @param {Boolean} isInstant - disables transitions
35946     */
35947     _processLayoutQueue : function( queue )
35948     {
35949         for ( var i=0, len = queue.length; i < len; i++ ) {
35950             var obj = queue[i];
35951             obj.item.position('absolute');
35952             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35953         }
35954     },
35955       
35956     
35957     /**
35958     * Any logic you want to do after each layout,
35959     * i.e. size the container
35960     */
35961     _postLayout : function()
35962     {
35963         this.resizeContainer();
35964     },
35965     
35966     resizeContainer : function()
35967     {
35968         if ( !this.isResizingContainer ) {
35969             return;
35970         }
35971         var size = this._getContainerSize();
35972         if ( size ) {
35973             this.el.setSize(size.width,size.height);
35974             this.boxesEl.setSize(size.width,size.height);
35975         }
35976     },
35977     
35978     
35979     
35980     _resetLayout : function()
35981     {
35982         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35983         this.colWidth = this.el.getWidth();
35984         //this.gutter = this.el.getWidth(); 
35985         
35986         this.measureColumns();
35987
35988         // reset column Y
35989         var i = this.cols;
35990         this.colYs = [];
35991         while (i--) {
35992             this.colYs.push( 0 );
35993         }
35994     
35995         this.maxY = 0;
35996     },
35997
35998     measureColumns : function()
35999     {
36000         this.getContainerWidth();
36001       // if columnWidth is 0, default to outerWidth of first item
36002         if ( !this.columnWidth ) {
36003             var firstItem = this.bricks.first();
36004             Roo.log(firstItem);
36005             this.columnWidth  = this.containerWidth;
36006             if (firstItem && firstItem.attr('originalwidth') ) {
36007                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
36008             }
36009             // columnWidth fall back to item of first element
36010             Roo.log("set column width?");
36011                         this.initialColumnWidth = this.columnWidth  ;
36012
36013             // if first elem has no width, default to size of container
36014             
36015         }
36016         
36017         
36018         if (this.initialColumnWidth) {
36019             this.columnWidth = this.initialColumnWidth;
36020         }
36021         
36022         
36023             
36024         // column width is fixed at the top - however if container width get's smaller we should
36025         // reduce it...
36026         
36027         // this bit calcs how man columns..
36028             
36029         var columnWidth = this.columnWidth += this.gutter;
36030       
36031         // calculate columns
36032         var containerWidth = this.containerWidth + this.gutter;
36033         
36034         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
36035         // fix rounding errors, typically with gutters
36036         var excess = columnWidth - containerWidth % columnWidth;
36037         
36038         
36039         // if overshoot is less than a pixel, round up, otherwise floor it
36040         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36041         cols = Math[ mathMethod ]( cols );
36042         this.cols = Math.max( cols, 1 );
36043         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36044         
36045          // padding positioning..
36046         var totalColWidth = this.cols * this.columnWidth;
36047         var padavail = this.containerWidth - totalColWidth;
36048         // so for 2 columns - we need 3 'pads'
36049         
36050         var padNeeded = (1+this.cols) * this.padWidth;
36051         
36052         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36053         
36054         this.columnWidth += padExtra
36055         //this.padWidth = Math.floor(padavail /  ( this.cols));
36056         
36057         // adjust colum width so that padding is fixed??
36058         
36059         // we have 3 columns ... total = width * 3
36060         // we have X left over... that should be used by 
36061         
36062         //if (this.expandC) {
36063             
36064         //}
36065         
36066         
36067         
36068     },
36069     
36070     getContainerWidth : function()
36071     {
36072        /* // container is parent if fit width
36073         var container = this.isFitWidth ? this.element.parentNode : this.element;
36074         // check that this.size and size are there
36075         // IE8 triggers resize on body size change, so they might not be
36076         
36077         var size = getSize( container );  //FIXME
36078         this.containerWidth = size && size.innerWidth; //FIXME
36079         */
36080          
36081         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36082         
36083     },
36084     
36085     _getItemLayoutPosition : function( item )  // what is item?
36086     {
36087         // we resize the item to our columnWidth..
36088       
36089         item.setWidth(this.columnWidth);
36090         item.autoBoxAdjust  = false;
36091         
36092         var sz = item.getSize();
36093  
36094         // how many columns does this brick span
36095         var remainder = this.containerWidth % this.columnWidth;
36096         
36097         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36098         // round if off by 1 pixel, otherwise use ceil
36099         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36100         colSpan = Math.min( colSpan, this.cols );
36101         
36102         // normally this should be '1' as we dont' currently allow multi width columns..
36103         
36104         var colGroup = this._getColGroup( colSpan );
36105         // get the minimum Y value from the columns
36106         var minimumY = Math.min.apply( Math, colGroup );
36107         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36108         
36109         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36110          
36111         // position the brick
36112         var position = {
36113             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36114             y: this.currentSize.y + minimumY + this.padHeight
36115         };
36116         
36117         Roo.log(position);
36118         // apply setHeight to necessary columns
36119         var setHeight = minimumY + sz.height + this.padHeight;
36120         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36121         
36122         var setSpan = this.cols + 1 - colGroup.length;
36123         for ( var i = 0; i < setSpan; i++ ) {
36124           this.colYs[ shortColIndex + i ] = setHeight ;
36125         }
36126       
36127         return position;
36128     },
36129     
36130     /**
36131      * @param {Number} colSpan - number of columns the element spans
36132      * @returns {Array} colGroup
36133      */
36134     _getColGroup : function( colSpan )
36135     {
36136         if ( colSpan < 2 ) {
36137           // if brick spans only one column, use all the column Ys
36138           return this.colYs;
36139         }
36140       
36141         var colGroup = [];
36142         // how many different places could this brick fit horizontally
36143         var groupCount = this.cols + 1 - colSpan;
36144         // for each group potential horizontal position
36145         for ( var i = 0; i < groupCount; i++ ) {
36146           // make an array of colY values for that one group
36147           var groupColYs = this.colYs.slice( i, i + colSpan );
36148           // and get the max value of the array
36149           colGroup[i] = Math.max.apply( Math, groupColYs );
36150         }
36151         return colGroup;
36152     },
36153     /*
36154     _manageStamp : function( stamp )
36155     {
36156         var stampSize =  stamp.getSize();
36157         var offset = stamp.getBox();
36158         // get the columns that this stamp affects
36159         var firstX = this.isOriginLeft ? offset.x : offset.right;
36160         var lastX = firstX + stampSize.width;
36161         var firstCol = Math.floor( firstX / this.columnWidth );
36162         firstCol = Math.max( 0, firstCol );
36163         
36164         var lastCol = Math.floor( lastX / this.columnWidth );
36165         // lastCol should not go over if multiple of columnWidth #425
36166         lastCol -= lastX % this.columnWidth ? 0 : 1;
36167         lastCol = Math.min( this.cols - 1, lastCol );
36168         
36169         // set colYs to bottom of the stamp
36170         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36171             stampSize.height;
36172             
36173         for ( var i = firstCol; i <= lastCol; i++ ) {
36174           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36175         }
36176     },
36177     */
36178     
36179     _getContainerSize : function()
36180     {
36181         this.maxY = Math.max.apply( Math, this.colYs );
36182         var size = {
36183             height: this.maxY
36184         };
36185       
36186         if ( this.isFitWidth ) {
36187             size.width = this._getContainerFitWidth();
36188         }
36189       
36190         return size;
36191     },
36192     
36193     _getContainerFitWidth : function()
36194     {
36195         var unusedCols = 0;
36196         // count unused columns
36197         var i = this.cols;
36198         while ( --i ) {
36199           if ( this.colYs[i] !== 0 ) {
36200             break;
36201           }
36202           unusedCols++;
36203         }
36204         // fit container to columns that have been used
36205         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36206     },
36207     
36208     needsResizeLayout : function()
36209     {
36210         var previousWidth = this.containerWidth;
36211         this.getContainerWidth();
36212         return previousWidth !== this.containerWidth;
36213     }
36214  
36215 });
36216
36217  
36218
36219  /*
36220  * - LGPL
36221  *
36222  * element
36223  * 
36224  */
36225
36226 /**
36227  * @class Roo.bootstrap.MasonryBrick
36228  * @extends Roo.bootstrap.Component
36229  * Bootstrap MasonryBrick class
36230  * 
36231  * @constructor
36232  * Create a new MasonryBrick
36233  * @param {Object} config The config object
36234  */
36235
36236 Roo.bootstrap.MasonryBrick = function(config){
36237     
36238     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36239     
36240     Roo.bootstrap.MasonryBrick.register(this);
36241     
36242     this.addEvents({
36243         // raw events
36244         /**
36245          * @event click
36246          * When a MasonryBrick is clcik
36247          * @param {Roo.bootstrap.MasonryBrick} this
36248          * @param {Roo.EventObject} e
36249          */
36250         "click" : true
36251     });
36252 };
36253
36254 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36255     
36256     /**
36257      * @cfg {String} title
36258      */   
36259     title : '',
36260     /**
36261      * @cfg {String} html
36262      */   
36263     html : '',
36264     /**
36265      * @cfg {String} bgimage
36266      */   
36267     bgimage : '',
36268     /**
36269      * @cfg {String} videourl
36270      */   
36271     videourl : '',
36272     /**
36273      * @cfg {String} cls
36274      */   
36275     cls : '',
36276     /**
36277      * @cfg {String} href
36278      */   
36279     href : '',
36280     /**
36281      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36282      */   
36283     size : 'xs',
36284     
36285     /**
36286      * @cfg {String} placetitle (center|bottom)
36287      */   
36288     placetitle : '',
36289     
36290     /**
36291      * @cfg {Boolean} isFitContainer defalut true
36292      */   
36293     isFitContainer : true, 
36294     
36295     /**
36296      * @cfg {Boolean} preventDefault defalut false
36297      */   
36298     preventDefault : false, 
36299     
36300     /**
36301      * @cfg {Boolean} inverse defalut false
36302      */   
36303     maskInverse : false, 
36304     
36305     getAutoCreate : function()
36306     {
36307         if(!this.isFitContainer){
36308             return this.getSplitAutoCreate();
36309         }
36310         
36311         var cls = 'masonry-brick masonry-brick-full';
36312         
36313         if(this.href.length){
36314             cls += ' masonry-brick-link';
36315         }
36316         
36317         if(this.bgimage.length){
36318             cls += ' masonry-brick-image';
36319         }
36320         
36321         if(this.maskInverse){
36322             cls += ' mask-inverse';
36323         }
36324         
36325         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36326             cls += ' enable-mask';
36327         }
36328         
36329         if(this.size){
36330             cls += ' masonry-' + this.size + '-brick';
36331         }
36332         
36333         if(this.placetitle.length){
36334             
36335             switch (this.placetitle) {
36336                 case 'center' :
36337                     cls += ' masonry-center-title';
36338                     break;
36339                 case 'bottom' :
36340                     cls += ' masonry-bottom-title';
36341                     break;
36342                 default:
36343                     break;
36344             }
36345             
36346         } else {
36347             if(!this.html.length && !this.bgimage.length){
36348                 cls += ' masonry-center-title';
36349             }
36350
36351             if(!this.html.length && this.bgimage.length){
36352                 cls += ' masonry-bottom-title';
36353             }
36354         }
36355         
36356         if(this.cls){
36357             cls += ' ' + this.cls;
36358         }
36359         
36360         var cfg = {
36361             tag: (this.href.length) ? 'a' : 'div',
36362             cls: cls,
36363             cn: [
36364                 {
36365                     tag: 'div',
36366                     cls: 'masonry-brick-mask'
36367                 },
36368                 {
36369                     tag: 'div',
36370                     cls: 'masonry-brick-paragraph',
36371                     cn: []
36372                 }
36373             ]
36374         };
36375         
36376         if(this.href.length){
36377             cfg.href = this.href;
36378         }
36379         
36380         var cn = cfg.cn[1].cn;
36381         
36382         if(this.title.length){
36383             cn.push({
36384                 tag: 'h4',
36385                 cls: 'masonry-brick-title',
36386                 html: this.title
36387             });
36388         }
36389         
36390         if(this.html.length){
36391             cn.push({
36392                 tag: 'p',
36393                 cls: 'masonry-brick-text',
36394                 html: this.html
36395             });
36396         }
36397         
36398         if (!this.title.length && !this.html.length) {
36399             cfg.cn[1].cls += ' hide';
36400         }
36401         
36402         if(this.bgimage.length){
36403             cfg.cn.push({
36404                 tag: 'img',
36405                 cls: 'masonry-brick-image-view',
36406                 src: this.bgimage
36407             });
36408         }
36409         
36410         if(this.videourl.length){
36411             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36412             // youtube support only?
36413             cfg.cn.push({
36414                 tag: 'iframe',
36415                 cls: 'masonry-brick-image-view',
36416                 src: vurl,
36417                 frameborder : 0,
36418                 allowfullscreen : true
36419             });
36420         }
36421         
36422         return cfg;
36423         
36424     },
36425     
36426     getSplitAutoCreate : function()
36427     {
36428         var cls = 'masonry-brick masonry-brick-split';
36429         
36430         if(this.href.length){
36431             cls += ' masonry-brick-link';
36432         }
36433         
36434         if(this.bgimage.length){
36435             cls += ' masonry-brick-image';
36436         }
36437         
36438         if(this.size){
36439             cls += ' masonry-' + this.size + '-brick';
36440         }
36441         
36442         switch (this.placetitle) {
36443             case 'center' :
36444                 cls += ' masonry-center-title';
36445                 break;
36446             case 'bottom' :
36447                 cls += ' masonry-bottom-title';
36448                 break;
36449             default:
36450                 if(!this.bgimage.length){
36451                     cls += ' masonry-center-title';
36452                 }
36453
36454                 if(this.bgimage.length){
36455                     cls += ' masonry-bottom-title';
36456                 }
36457                 break;
36458         }
36459         
36460         if(this.cls){
36461             cls += ' ' + this.cls;
36462         }
36463         
36464         var cfg = {
36465             tag: (this.href.length) ? 'a' : 'div',
36466             cls: cls,
36467             cn: [
36468                 {
36469                     tag: 'div',
36470                     cls: 'masonry-brick-split-head',
36471                     cn: [
36472                         {
36473                             tag: 'div',
36474                             cls: 'masonry-brick-paragraph',
36475                             cn: []
36476                         }
36477                     ]
36478                 },
36479                 {
36480                     tag: 'div',
36481                     cls: 'masonry-brick-split-body',
36482                     cn: []
36483                 }
36484             ]
36485         };
36486         
36487         if(this.href.length){
36488             cfg.href = this.href;
36489         }
36490         
36491         if(this.title.length){
36492             cfg.cn[0].cn[0].cn.push({
36493                 tag: 'h4',
36494                 cls: 'masonry-brick-title',
36495                 html: this.title
36496             });
36497         }
36498         
36499         if(this.html.length){
36500             cfg.cn[1].cn.push({
36501                 tag: 'p',
36502                 cls: 'masonry-brick-text',
36503                 html: this.html
36504             });
36505         }
36506
36507         if(this.bgimage.length){
36508             cfg.cn[0].cn.push({
36509                 tag: 'img',
36510                 cls: 'masonry-brick-image-view',
36511                 src: this.bgimage
36512             });
36513         }
36514         
36515         if(this.videourl.length){
36516             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36517             // youtube support only?
36518             cfg.cn[0].cn.cn.push({
36519                 tag: 'iframe',
36520                 cls: 'masonry-brick-image-view',
36521                 src: vurl,
36522                 frameborder : 0,
36523                 allowfullscreen : true
36524             });
36525         }
36526         
36527         return cfg;
36528     },
36529     
36530     initEvents: function() 
36531     {
36532         switch (this.size) {
36533             case 'xs' :
36534                 this.x = 1;
36535                 this.y = 1;
36536                 break;
36537             case 'sm' :
36538                 this.x = 2;
36539                 this.y = 2;
36540                 break;
36541             case 'md' :
36542             case 'md-left' :
36543             case 'md-right' :
36544                 this.x = 3;
36545                 this.y = 3;
36546                 break;
36547             case 'tall' :
36548                 this.x = 2;
36549                 this.y = 3;
36550                 break;
36551             case 'wide' :
36552                 this.x = 3;
36553                 this.y = 2;
36554                 break;
36555             case 'wide-thin' :
36556                 this.x = 3;
36557                 this.y = 1;
36558                 break;
36559                         
36560             default :
36561                 break;
36562         }
36563         
36564         if(Roo.isTouch){
36565             this.el.on('touchstart', this.onTouchStart, this);
36566             this.el.on('touchmove', this.onTouchMove, this);
36567             this.el.on('touchend', this.onTouchEnd, this);
36568             this.el.on('contextmenu', this.onContextMenu, this);
36569         } else {
36570             this.el.on('mouseenter'  ,this.enter, this);
36571             this.el.on('mouseleave', this.leave, this);
36572             this.el.on('click', this.onClick, this);
36573         }
36574         
36575         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36576             this.parent().bricks.push(this);   
36577         }
36578         
36579     },
36580     
36581     onClick: function(e, el)
36582     {
36583         var time = this.endTimer - this.startTimer;
36584         // Roo.log(e.preventDefault());
36585         if(Roo.isTouch){
36586             if(time > 1000){
36587                 e.preventDefault();
36588                 return;
36589             }
36590         }
36591         
36592         if(!this.preventDefault){
36593             return;
36594         }
36595         
36596         e.preventDefault();
36597         
36598         if (this.activeClass != '') {
36599             this.selectBrick();
36600         }
36601         
36602         this.fireEvent('click', this, e);
36603     },
36604     
36605     enter: function(e, el)
36606     {
36607         e.preventDefault();
36608         
36609         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36610             return;
36611         }
36612         
36613         if(this.bgimage.length && this.html.length){
36614             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36615         }
36616     },
36617     
36618     leave: function(e, el)
36619     {
36620         e.preventDefault();
36621         
36622         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36623             return;
36624         }
36625         
36626         if(this.bgimage.length && this.html.length){
36627             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36628         }
36629     },
36630     
36631     onTouchStart: function(e, el)
36632     {
36633 //        e.preventDefault();
36634         
36635         this.touchmoved = false;
36636         
36637         if(!this.isFitContainer){
36638             return;
36639         }
36640         
36641         if(!this.bgimage.length || !this.html.length){
36642             return;
36643         }
36644         
36645         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36646         
36647         this.timer = new Date().getTime();
36648         
36649     },
36650     
36651     onTouchMove: function(e, el)
36652     {
36653         this.touchmoved = true;
36654     },
36655     
36656     onContextMenu : function(e,el)
36657     {
36658         e.preventDefault();
36659         e.stopPropagation();
36660         return false;
36661     },
36662     
36663     onTouchEnd: function(e, el)
36664     {
36665 //        e.preventDefault();
36666         
36667         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36668         
36669             this.leave(e,el);
36670             
36671             return;
36672         }
36673         
36674         if(!this.bgimage.length || !this.html.length){
36675             
36676             if(this.href.length){
36677                 window.location.href = this.href;
36678             }
36679             
36680             return;
36681         }
36682         
36683         if(!this.isFitContainer){
36684             return;
36685         }
36686         
36687         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36688         
36689         window.location.href = this.href;
36690     },
36691     
36692     //selection on single brick only
36693     selectBrick : function() {
36694         
36695         if (!this.parentId) {
36696             return;
36697         }
36698         
36699         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36700         var index = m.selectedBrick.indexOf(this.id);
36701         
36702         if ( index > -1) {
36703             m.selectedBrick.splice(index,1);
36704             this.el.removeClass(this.activeClass);
36705             return;
36706         }
36707         
36708         for(var i = 0; i < m.selectedBrick.length; i++) {
36709             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36710             b.el.removeClass(b.activeClass);
36711         }
36712         
36713         m.selectedBrick = [];
36714         
36715         m.selectedBrick.push(this.id);
36716         this.el.addClass(this.activeClass);
36717         return;
36718     },
36719     
36720     isSelected : function(){
36721         return this.el.hasClass(this.activeClass);
36722         
36723     }
36724 });
36725
36726 Roo.apply(Roo.bootstrap.MasonryBrick, {
36727     
36728     //groups: {},
36729     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36730      /**
36731     * register a Masonry Brick
36732     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36733     */
36734     
36735     register : function(brick)
36736     {
36737         //this.groups[brick.id] = brick;
36738         this.groups.add(brick.id, brick);
36739     },
36740     /**
36741     * fetch a  masonry brick based on the masonry brick ID
36742     * @param {string} the masonry brick to add
36743     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36744     */
36745     
36746     get: function(brick_id) 
36747     {
36748         // if (typeof(this.groups[brick_id]) == 'undefined') {
36749         //     return false;
36750         // }
36751         // return this.groups[brick_id] ;
36752         
36753         if(this.groups.key(brick_id)) {
36754             return this.groups.key(brick_id);
36755         }
36756         
36757         return false;
36758     }
36759     
36760     
36761     
36762 });
36763
36764  /*
36765  * - LGPL
36766  *
36767  * element
36768  * 
36769  */
36770
36771 /**
36772  * @class Roo.bootstrap.Brick
36773  * @extends Roo.bootstrap.Component
36774  * Bootstrap Brick class
36775  * 
36776  * @constructor
36777  * Create a new Brick
36778  * @param {Object} config The config object
36779  */
36780
36781 Roo.bootstrap.Brick = function(config){
36782     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36783     
36784     this.addEvents({
36785         // raw events
36786         /**
36787          * @event click
36788          * When a Brick is click
36789          * @param {Roo.bootstrap.Brick} this
36790          * @param {Roo.EventObject} e
36791          */
36792         "click" : true
36793     });
36794 };
36795
36796 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36797     
36798     /**
36799      * @cfg {String} title
36800      */   
36801     title : '',
36802     /**
36803      * @cfg {String} html
36804      */   
36805     html : '',
36806     /**
36807      * @cfg {String} bgimage
36808      */   
36809     bgimage : '',
36810     /**
36811      * @cfg {String} cls
36812      */   
36813     cls : '',
36814     /**
36815      * @cfg {String} href
36816      */   
36817     href : '',
36818     /**
36819      * @cfg {String} video
36820      */   
36821     video : '',
36822     /**
36823      * @cfg {Boolean} square
36824      */   
36825     square : true,
36826     
36827     getAutoCreate : function()
36828     {
36829         var cls = 'roo-brick';
36830         
36831         if(this.href.length){
36832             cls += ' roo-brick-link';
36833         }
36834         
36835         if(this.bgimage.length){
36836             cls += ' roo-brick-image';
36837         }
36838         
36839         if(!this.html.length && !this.bgimage.length){
36840             cls += ' roo-brick-center-title';
36841         }
36842         
36843         if(!this.html.length && this.bgimage.length){
36844             cls += ' roo-brick-bottom-title';
36845         }
36846         
36847         if(this.cls){
36848             cls += ' ' + this.cls;
36849         }
36850         
36851         var cfg = {
36852             tag: (this.href.length) ? 'a' : 'div',
36853             cls: cls,
36854             cn: [
36855                 {
36856                     tag: 'div',
36857                     cls: 'roo-brick-paragraph',
36858                     cn: []
36859                 }
36860             ]
36861         };
36862         
36863         if(this.href.length){
36864             cfg.href = this.href;
36865         }
36866         
36867         var cn = cfg.cn[0].cn;
36868         
36869         if(this.title.length){
36870             cn.push({
36871                 tag: 'h4',
36872                 cls: 'roo-brick-title',
36873                 html: this.title
36874             });
36875         }
36876         
36877         if(this.html.length){
36878             cn.push({
36879                 tag: 'p',
36880                 cls: 'roo-brick-text',
36881                 html: this.html
36882             });
36883         } else {
36884             cn.cls += ' hide';
36885         }
36886         
36887         if(this.bgimage.length){
36888             cfg.cn.push({
36889                 tag: 'img',
36890                 cls: 'roo-brick-image-view',
36891                 src: this.bgimage
36892             });
36893         }
36894         
36895         return cfg;
36896     },
36897     
36898     initEvents: function() 
36899     {
36900         if(this.title.length || this.html.length){
36901             this.el.on('mouseenter'  ,this.enter, this);
36902             this.el.on('mouseleave', this.leave, this);
36903         }
36904         
36905         Roo.EventManager.onWindowResize(this.resize, this); 
36906         
36907         if(this.bgimage.length){
36908             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36909             this.imageEl.on('load', this.onImageLoad, this);
36910             return;
36911         }
36912         
36913         this.resize();
36914     },
36915     
36916     onImageLoad : function()
36917     {
36918         this.resize();
36919     },
36920     
36921     resize : function()
36922     {
36923         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36924         
36925         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36926         
36927         if(this.bgimage.length){
36928             var image = this.el.select('.roo-brick-image-view', true).first();
36929             
36930             image.setWidth(paragraph.getWidth());
36931             
36932             if(this.square){
36933                 image.setHeight(paragraph.getWidth());
36934             }
36935             
36936             this.el.setHeight(image.getHeight());
36937             paragraph.setHeight(image.getHeight());
36938             
36939         }
36940         
36941     },
36942     
36943     enter: function(e, el)
36944     {
36945         e.preventDefault();
36946         
36947         if(this.bgimage.length){
36948             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36949             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36950         }
36951     },
36952     
36953     leave: function(e, el)
36954     {
36955         e.preventDefault();
36956         
36957         if(this.bgimage.length){
36958             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36959             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36960         }
36961     }
36962     
36963 });
36964
36965  
36966
36967  /*
36968  * - LGPL
36969  *
36970  * Number field 
36971  */
36972
36973 /**
36974  * @class Roo.bootstrap.NumberField
36975  * @extends Roo.bootstrap.Input
36976  * Bootstrap NumberField class
36977  * 
36978  * 
36979  * 
36980  * 
36981  * @constructor
36982  * Create a new NumberField
36983  * @param {Object} config The config object
36984  */
36985
36986 Roo.bootstrap.NumberField = function(config){
36987     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36988 };
36989
36990 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36991     
36992     /**
36993      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36994      */
36995     allowDecimals : true,
36996     /**
36997      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36998      */
36999     decimalSeparator : ".",
37000     /**
37001      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37002      */
37003     decimalPrecision : 2,
37004     /**
37005      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
37006      */
37007     allowNegative : true,
37008     
37009     /**
37010      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
37011      */
37012     allowZero: true,
37013     /**
37014      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
37015      */
37016     minValue : Number.NEGATIVE_INFINITY,
37017     /**
37018      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
37019      */
37020     maxValue : Number.MAX_VALUE,
37021     /**
37022      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
37023      */
37024     minText : "The minimum value for this field is {0}",
37025     /**
37026      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
37027      */
37028     maxText : "The maximum value for this field is {0}",
37029     /**
37030      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
37031      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
37032      */
37033     nanText : "{0} is not a valid number",
37034     /**
37035      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37036      */
37037     thousandsDelimiter : false,
37038     /**
37039      * @cfg {String} valueAlign alignment of value
37040      */
37041     valueAlign : "left",
37042
37043     getAutoCreate : function()
37044     {
37045         var hiddenInput = {
37046             tag: 'input',
37047             type: 'hidden',
37048             id: Roo.id(),
37049             cls: 'hidden-number-input'
37050         };
37051         
37052         if (this.name) {
37053             hiddenInput.name = this.name;
37054         }
37055         
37056         this.name = '';
37057         
37058         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37059         
37060         this.name = hiddenInput.name;
37061         
37062         if(cfg.cn.length > 0) {
37063             cfg.cn.push(hiddenInput);
37064         }
37065         
37066         return cfg;
37067     },
37068
37069     // private
37070     initEvents : function()
37071     {   
37072         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37073         
37074         var allowed = "0123456789";
37075         
37076         if(this.allowDecimals){
37077             allowed += this.decimalSeparator;
37078         }
37079         
37080         if(this.allowNegative){
37081             allowed += "-";
37082         }
37083         
37084         if(this.thousandsDelimiter) {
37085             allowed += ",";
37086         }
37087         
37088         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37089         
37090         var keyPress = function(e){
37091             
37092             var k = e.getKey();
37093             
37094             var c = e.getCharCode();
37095             
37096             if(
37097                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37098                     allowed.indexOf(String.fromCharCode(c)) === -1
37099             ){
37100                 e.stopEvent();
37101                 return;
37102             }
37103             
37104             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37105                 return;
37106             }
37107             
37108             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37109                 e.stopEvent();
37110             }
37111         };
37112         
37113         this.el.on("keypress", keyPress, this);
37114     },
37115     
37116     validateValue : function(value)
37117     {
37118         
37119         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37120             return false;
37121         }
37122         
37123         var num = this.parseValue(value);
37124         
37125         if(isNaN(num)){
37126             this.markInvalid(String.format(this.nanText, value));
37127             return false;
37128         }
37129         
37130         if(num < this.minValue){
37131             this.markInvalid(String.format(this.minText, this.minValue));
37132             return false;
37133         }
37134         
37135         if(num > this.maxValue){
37136             this.markInvalid(String.format(this.maxText, this.maxValue));
37137             return false;
37138         }
37139         
37140         return true;
37141     },
37142
37143     getValue : function()
37144     {
37145         var v = this.hiddenEl().getValue();
37146         
37147         return this.fixPrecision(this.parseValue(v));
37148     },
37149
37150     parseValue : function(value)
37151     {
37152         if(this.thousandsDelimiter) {
37153             value += "";
37154             r = new RegExp(",", "g");
37155             value = value.replace(r, "");
37156         }
37157         
37158         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37159         return isNaN(value) ? '' : value;
37160     },
37161
37162     fixPrecision : function(value)
37163     {
37164         if(this.thousandsDelimiter) {
37165             value += "";
37166             r = new RegExp(",", "g");
37167             value = value.replace(r, "");
37168         }
37169         
37170         var nan = isNaN(value);
37171         
37172         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37173             return nan ? '' : value;
37174         }
37175         return parseFloat(value).toFixed(this.decimalPrecision);
37176     },
37177
37178     setValue : function(v)
37179     {
37180         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37181         
37182         this.value = v;
37183         
37184         if(this.rendered){
37185             
37186             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37187             
37188             this.inputEl().dom.value = (v == '') ? '' :
37189                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37190             
37191             if(!this.allowZero && v === '0') {
37192                 this.hiddenEl().dom.value = '';
37193                 this.inputEl().dom.value = '';
37194             }
37195             
37196             this.validate();
37197         }
37198     },
37199
37200     decimalPrecisionFcn : function(v)
37201     {
37202         return Math.floor(v);
37203     },
37204
37205     beforeBlur : function()
37206     {
37207         var v = this.parseValue(this.getRawValue());
37208         
37209         if(v || v === 0 || v === ''){
37210             this.setValue(v);
37211         }
37212     },
37213     
37214     hiddenEl : function()
37215     {
37216         return this.el.select('input.hidden-number-input',true).first();
37217     }
37218     
37219 });
37220
37221  
37222
37223 /*
37224 * Licence: LGPL
37225 */
37226
37227 /**
37228  * @class Roo.bootstrap.DocumentSlider
37229  * @extends Roo.bootstrap.Component
37230  * Bootstrap DocumentSlider class
37231  * 
37232  * @constructor
37233  * Create a new DocumentViewer
37234  * @param {Object} config The config object
37235  */
37236
37237 Roo.bootstrap.DocumentSlider = function(config){
37238     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37239     
37240     this.files = [];
37241     
37242     this.addEvents({
37243         /**
37244          * @event initial
37245          * Fire after initEvent
37246          * @param {Roo.bootstrap.DocumentSlider} this
37247          */
37248         "initial" : true,
37249         /**
37250          * @event update
37251          * Fire after update
37252          * @param {Roo.bootstrap.DocumentSlider} this
37253          */
37254         "update" : true,
37255         /**
37256          * @event click
37257          * Fire after click
37258          * @param {Roo.bootstrap.DocumentSlider} this
37259          */
37260         "click" : true
37261     });
37262 };
37263
37264 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37265     
37266     files : false,
37267     
37268     indicator : 0,
37269     
37270     getAutoCreate : function()
37271     {
37272         var cfg = {
37273             tag : 'div',
37274             cls : 'roo-document-slider',
37275             cn : [
37276                 {
37277                     tag : 'div',
37278                     cls : 'roo-document-slider-header',
37279                     cn : [
37280                         {
37281                             tag : 'div',
37282                             cls : 'roo-document-slider-header-title'
37283                         }
37284                     ]
37285                 },
37286                 {
37287                     tag : 'div',
37288                     cls : 'roo-document-slider-body',
37289                     cn : [
37290                         {
37291                             tag : 'div',
37292                             cls : 'roo-document-slider-prev',
37293                             cn : [
37294                                 {
37295                                     tag : 'i',
37296                                     cls : 'fa fa-chevron-left'
37297                                 }
37298                             ]
37299                         },
37300                         {
37301                             tag : 'div',
37302                             cls : 'roo-document-slider-thumb',
37303                             cn : [
37304                                 {
37305                                     tag : 'img',
37306                                     cls : 'roo-document-slider-image'
37307                                 }
37308                             ]
37309                         },
37310                         {
37311                             tag : 'div',
37312                             cls : 'roo-document-slider-next',
37313                             cn : [
37314                                 {
37315                                     tag : 'i',
37316                                     cls : 'fa fa-chevron-right'
37317                                 }
37318                             ]
37319                         }
37320                     ]
37321                 }
37322             ]
37323         };
37324         
37325         return cfg;
37326     },
37327     
37328     initEvents : function()
37329     {
37330         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37331         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37332         
37333         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37334         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37335         
37336         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37337         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37338         
37339         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37340         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37341         
37342         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37343         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37344         
37345         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37346         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37347         
37348         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37349         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37350         
37351         this.thumbEl.on('click', this.onClick, this);
37352         
37353         this.prevIndicator.on('click', this.prev, this);
37354         
37355         this.nextIndicator.on('click', this.next, this);
37356         
37357     },
37358     
37359     initial : function()
37360     {
37361         if(this.files.length){
37362             this.indicator = 1;
37363             this.update()
37364         }
37365         
37366         this.fireEvent('initial', this);
37367     },
37368     
37369     update : function()
37370     {
37371         this.imageEl.attr('src', this.files[this.indicator - 1]);
37372         
37373         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37374         
37375         this.prevIndicator.show();
37376         
37377         if(this.indicator == 1){
37378             this.prevIndicator.hide();
37379         }
37380         
37381         this.nextIndicator.show();
37382         
37383         if(this.indicator == this.files.length){
37384             this.nextIndicator.hide();
37385         }
37386         
37387         this.thumbEl.scrollTo('top');
37388         
37389         this.fireEvent('update', this);
37390     },
37391     
37392     onClick : function(e)
37393     {
37394         e.preventDefault();
37395         
37396         this.fireEvent('click', this);
37397     },
37398     
37399     prev : function(e)
37400     {
37401         e.preventDefault();
37402         
37403         this.indicator = Math.max(1, this.indicator - 1);
37404         
37405         this.update();
37406     },
37407     
37408     next : function(e)
37409     {
37410         e.preventDefault();
37411         
37412         this.indicator = Math.min(this.files.length, this.indicator + 1);
37413         
37414         this.update();
37415     }
37416 });
37417 /*
37418  * - LGPL
37419  *
37420  * RadioSet
37421  *
37422  *
37423  */
37424
37425 /**
37426  * @class Roo.bootstrap.RadioSet
37427  * @extends Roo.bootstrap.Input
37428  * @children Roo.bootstrap.Radio
37429  * Bootstrap RadioSet class
37430  * @cfg {String} indicatorpos (left|right) default left
37431  * @cfg {Boolean} inline (true|false) inline the element (default true)
37432  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37433  * @constructor
37434  * Create a new RadioSet
37435  * @param {Object} config The config object
37436  */
37437
37438 Roo.bootstrap.RadioSet = function(config){
37439     
37440     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37441     
37442     this.radioes = [];
37443     
37444     Roo.bootstrap.RadioSet.register(this);
37445     
37446     this.addEvents({
37447         /**
37448         * @event check
37449         * Fires when the element is checked or unchecked.
37450         * @param {Roo.bootstrap.RadioSet} this This radio
37451         * @param {Roo.bootstrap.Radio} item The checked item
37452         */
37453        check : true,
37454        /**
37455         * @event click
37456         * Fires when the element is click.
37457         * @param {Roo.bootstrap.RadioSet} this This radio set
37458         * @param {Roo.bootstrap.Radio} item The checked item
37459         * @param {Roo.EventObject} e The event object
37460         */
37461        click : true
37462     });
37463     
37464 };
37465
37466 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37467
37468     radioes : false,
37469     
37470     inline : true,
37471     
37472     weight : '',
37473     
37474     indicatorpos : 'left',
37475     
37476     getAutoCreate : function()
37477     {
37478         var label = {
37479             tag : 'label',
37480             cls : 'roo-radio-set-label',
37481             cn : [
37482                 {
37483                     tag : 'span',
37484                     html : this.fieldLabel
37485                 }
37486             ]
37487         };
37488         if (Roo.bootstrap.version == 3) {
37489             
37490             
37491             if(this.indicatorpos == 'left'){
37492                 label.cn.unshift({
37493                     tag : 'i',
37494                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37495                     tooltip : 'This field is required'
37496                 });
37497             } else {
37498                 label.cn.push({
37499                     tag : 'i',
37500                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37501                     tooltip : 'This field is required'
37502                 });
37503             }
37504         }
37505         var items = {
37506             tag : 'div',
37507             cls : 'roo-radio-set-items'
37508         };
37509         
37510         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37511         
37512         if (align === 'left' && this.fieldLabel.length) {
37513             
37514             items = {
37515                 cls : "roo-radio-set-right", 
37516                 cn: [
37517                     items
37518                 ]
37519             };
37520             
37521             if(this.labelWidth > 12){
37522                 label.style = "width: " + this.labelWidth + 'px';
37523             }
37524             
37525             if(this.labelWidth < 13 && this.labelmd == 0){
37526                 this.labelmd = this.labelWidth;
37527             }
37528             
37529             if(this.labellg > 0){
37530                 label.cls += ' col-lg-' + this.labellg;
37531                 items.cls += ' col-lg-' + (12 - this.labellg);
37532             }
37533             
37534             if(this.labelmd > 0){
37535                 label.cls += ' col-md-' + this.labelmd;
37536                 items.cls += ' col-md-' + (12 - this.labelmd);
37537             }
37538             
37539             if(this.labelsm > 0){
37540                 label.cls += ' col-sm-' + this.labelsm;
37541                 items.cls += ' col-sm-' + (12 - this.labelsm);
37542             }
37543             
37544             if(this.labelxs > 0){
37545                 label.cls += ' col-xs-' + this.labelxs;
37546                 items.cls += ' col-xs-' + (12 - this.labelxs);
37547             }
37548         }
37549         
37550         var cfg = {
37551             tag : 'div',
37552             cls : 'roo-radio-set',
37553             cn : [
37554                 {
37555                     tag : 'input',
37556                     cls : 'roo-radio-set-input',
37557                     type : 'hidden',
37558                     name : this.name,
37559                     value : this.value ? this.value :  ''
37560                 },
37561                 label,
37562                 items
37563             ]
37564         };
37565         
37566         if(this.weight.length){
37567             cfg.cls += ' roo-radio-' + this.weight;
37568         }
37569         
37570         if(this.inline) {
37571             cfg.cls += ' roo-radio-set-inline';
37572         }
37573         
37574         var settings=this;
37575         ['xs','sm','md','lg'].map(function(size){
37576             if (settings[size]) {
37577                 cfg.cls += ' col-' + size + '-' + settings[size];
37578             }
37579         });
37580         
37581         return cfg;
37582         
37583     },
37584
37585     initEvents : function()
37586     {
37587         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37588         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37589         
37590         if(!this.fieldLabel.length){
37591             this.labelEl.hide();
37592         }
37593         
37594         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37595         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37596         
37597         this.indicator = this.indicatorEl();
37598         
37599         if(this.indicator){
37600             this.indicator.addClass('invisible');
37601         }
37602         
37603         this.originalValue = this.getValue();
37604         
37605     },
37606     
37607     inputEl: function ()
37608     {
37609         return this.el.select('.roo-radio-set-input', true).first();
37610     },
37611     
37612     getChildContainer : function()
37613     {
37614         return this.itemsEl;
37615     },
37616     
37617     register : function(item)
37618     {
37619         this.radioes.push(item);
37620         
37621     },
37622     
37623     validate : function()
37624     {   
37625         if(this.getVisibilityEl().hasClass('hidden')){
37626             return true;
37627         }
37628         
37629         var valid = false;
37630         
37631         Roo.each(this.radioes, function(i){
37632             if(!i.checked){
37633                 return;
37634             }
37635             
37636             valid = true;
37637             return false;
37638         });
37639         
37640         if(this.allowBlank) {
37641             return true;
37642         }
37643         
37644         if(this.disabled || valid){
37645             this.markValid();
37646             return true;
37647         }
37648         
37649         this.markInvalid();
37650         return false;
37651         
37652     },
37653     
37654     markValid : function()
37655     {
37656         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37657             this.indicatorEl().removeClass('visible');
37658             this.indicatorEl().addClass('invisible');
37659         }
37660         
37661         
37662         if (Roo.bootstrap.version == 3) {
37663             this.el.removeClass([this.invalidClass, this.validClass]);
37664             this.el.addClass(this.validClass);
37665         } else {
37666             this.el.removeClass(['is-invalid','is-valid']);
37667             this.el.addClass(['is-valid']);
37668         }
37669         this.fireEvent('valid', this);
37670     },
37671     
37672     markInvalid : function(msg)
37673     {
37674         if(this.allowBlank || this.disabled){
37675             return;
37676         }
37677         
37678         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37679             this.indicatorEl().removeClass('invisible');
37680             this.indicatorEl().addClass('visible');
37681         }
37682         if (Roo.bootstrap.version == 3) {
37683             this.el.removeClass([this.invalidClass, this.validClass]);
37684             this.el.addClass(this.invalidClass);
37685         } else {
37686             this.el.removeClass(['is-invalid','is-valid']);
37687             this.el.addClass(['is-invalid']);
37688         }
37689         
37690         this.fireEvent('invalid', this, msg);
37691         
37692     },
37693     
37694     setValue : function(v, suppressEvent)
37695     {   
37696         if(this.value === v){
37697             return;
37698         }
37699         
37700         this.value = v;
37701         
37702         if(this.rendered){
37703             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37704         }
37705         
37706         Roo.each(this.radioes, function(i){
37707             i.checked = false;
37708             i.el.removeClass('checked');
37709         });
37710         
37711         Roo.each(this.radioes, function(i){
37712             
37713             if(i.value === v || i.value.toString() === v.toString()){
37714                 i.checked = true;
37715                 i.el.addClass('checked');
37716                 
37717                 if(suppressEvent !== true){
37718                     this.fireEvent('check', this, i);
37719                 }
37720                 
37721                 return false;
37722             }
37723             
37724         }, this);
37725         
37726         this.validate();
37727     },
37728     
37729     clearInvalid : function(){
37730         
37731         if(!this.el || this.preventMark){
37732             return;
37733         }
37734         
37735         this.el.removeClass([this.invalidClass]);
37736         
37737         this.fireEvent('valid', this);
37738     }
37739     
37740 });
37741
37742 Roo.apply(Roo.bootstrap.RadioSet, {
37743     
37744     groups: {},
37745     
37746     register : function(set)
37747     {
37748         this.groups[set.name] = set;
37749     },
37750     
37751     get: function(name) 
37752     {
37753         if (typeof(this.groups[name]) == 'undefined') {
37754             return false;
37755         }
37756         
37757         return this.groups[name] ;
37758     }
37759     
37760 });
37761 /*
37762  * Based on:
37763  * Ext JS Library 1.1.1
37764  * Copyright(c) 2006-2007, Ext JS, LLC.
37765  *
37766  * Originally Released Under LGPL - original licence link has changed is not relivant.
37767  *
37768  * Fork - LGPL
37769  * <script type="text/javascript">
37770  */
37771
37772
37773 /**
37774  * @class Roo.bootstrap.SplitBar
37775  * @extends Roo.util.Observable
37776  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37777  * <br><br>
37778  * Usage:
37779  * <pre><code>
37780 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37781                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37782 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37783 split.minSize = 100;
37784 split.maxSize = 600;
37785 split.animate = true;
37786 split.on('moved', splitterMoved);
37787 </code></pre>
37788  * @constructor
37789  * Create a new SplitBar
37790  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37791  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37792  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37793  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37794                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37795                         position of the SplitBar).
37796  */
37797 Roo.bootstrap.SplitBar = function(cfg){
37798     
37799     /** @private */
37800     
37801     //{
37802     //  dragElement : elm
37803     //  resizingElement: el,
37804         // optional..
37805     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37806     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37807         // existingProxy ???
37808     //}
37809     
37810     this.el = Roo.get(cfg.dragElement, true);
37811     this.el.dom.unselectable = "on";
37812     /** @private */
37813     this.resizingEl = Roo.get(cfg.resizingElement, true);
37814
37815     /**
37816      * @private
37817      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37818      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37819      * @type Number
37820      */
37821     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37822     
37823     /**
37824      * The minimum size of the resizing element. (Defaults to 0)
37825      * @type Number
37826      */
37827     this.minSize = 0;
37828     
37829     /**
37830      * The maximum size of the resizing element. (Defaults to 2000)
37831      * @type Number
37832      */
37833     this.maxSize = 2000;
37834     
37835     /**
37836      * Whether to animate the transition to the new size
37837      * @type Boolean
37838      */
37839     this.animate = false;
37840     
37841     /**
37842      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37843      * @type Boolean
37844      */
37845     this.useShim = false;
37846     
37847     /** @private */
37848     this.shim = null;
37849     
37850     if(!cfg.existingProxy){
37851         /** @private */
37852         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37853     }else{
37854         this.proxy = Roo.get(cfg.existingProxy).dom;
37855     }
37856     /** @private */
37857     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37858     
37859     /** @private */
37860     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37861     
37862     /** @private */
37863     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37864     
37865     /** @private */
37866     this.dragSpecs = {};
37867     
37868     /**
37869      * @private The adapter to use to positon and resize elements
37870      */
37871     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37872     this.adapter.init(this);
37873     
37874     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37875         /** @private */
37876         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37877         this.el.addClass("roo-splitbar-h");
37878     }else{
37879         /** @private */
37880         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37881         this.el.addClass("roo-splitbar-v");
37882     }
37883     
37884     this.addEvents({
37885         /**
37886          * @event resize
37887          * Fires when the splitter is moved (alias for {@link #event-moved})
37888          * @param {Roo.bootstrap.SplitBar} this
37889          * @param {Number} newSize the new width or height
37890          */
37891         "resize" : true,
37892         /**
37893          * @event moved
37894          * Fires when the splitter is moved
37895          * @param {Roo.bootstrap.SplitBar} this
37896          * @param {Number} newSize the new width or height
37897          */
37898         "moved" : true,
37899         /**
37900          * @event beforeresize
37901          * Fires before the splitter is dragged
37902          * @param {Roo.bootstrap.SplitBar} this
37903          */
37904         "beforeresize" : true,
37905
37906         "beforeapply" : true
37907     });
37908
37909     Roo.util.Observable.call(this);
37910 };
37911
37912 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37913     onStartProxyDrag : function(x, y){
37914         this.fireEvent("beforeresize", this);
37915         if(!this.overlay){
37916             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37917             o.unselectable();
37918             o.enableDisplayMode("block");
37919             // all splitbars share the same overlay
37920             Roo.bootstrap.SplitBar.prototype.overlay = o;
37921         }
37922         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37923         this.overlay.show();
37924         Roo.get(this.proxy).setDisplayed("block");
37925         var size = this.adapter.getElementSize(this);
37926         this.activeMinSize = this.getMinimumSize();;
37927         this.activeMaxSize = this.getMaximumSize();;
37928         var c1 = size - this.activeMinSize;
37929         var c2 = Math.max(this.activeMaxSize - size, 0);
37930         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37931             this.dd.resetConstraints();
37932             this.dd.setXConstraint(
37933                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37934                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37935             );
37936             this.dd.setYConstraint(0, 0);
37937         }else{
37938             this.dd.resetConstraints();
37939             this.dd.setXConstraint(0, 0);
37940             this.dd.setYConstraint(
37941                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37942                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37943             );
37944          }
37945         this.dragSpecs.startSize = size;
37946         this.dragSpecs.startPoint = [x, y];
37947         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37948     },
37949     
37950     /** 
37951      * @private Called after the drag operation by the DDProxy
37952      */
37953     onEndProxyDrag : function(e){
37954         Roo.get(this.proxy).setDisplayed(false);
37955         var endPoint = Roo.lib.Event.getXY(e);
37956         if(this.overlay){
37957             this.overlay.hide();
37958         }
37959         var newSize;
37960         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37961             newSize = this.dragSpecs.startSize + 
37962                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37963                     endPoint[0] - this.dragSpecs.startPoint[0] :
37964                     this.dragSpecs.startPoint[0] - endPoint[0]
37965                 );
37966         }else{
37967             newSize = this.dragSpecs.startSize + 
37968                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37969                     endPoint[1] - this.dragSpecs.startPoint[1] :
37970                     this.dragSpecs.startPoint[1] - endPoint[1]
37971                 );
37972         }
37973         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37974         if(newSize != this.dragSpecs.startSize){
37975             if(this.fireEvent('beforeapply', this, newSize) !== false){
37976                 this.adapter.setElementSize(this, newSize);
37977                 this.fireEvent("moved", this, newSize);
37978                 this.fireEvent("resize", this, newSize);
37979             }
37980         }
37981     },
37982     
37983     /**
37984      * Get the adapter this SplitBar uses
37985      * @return The adapter object
37986      */
37987     getAdapter : function(){
37988         return this.adapter;
37989     },
37990     
37991     /**
37992      * Set the adapter this SplitBar uses
37993      * @param {Object} adapter A SplitBar adapter object
37994      */
37995     setAdapter : function(adapter){
37996         this.adapter = adapter;
37997         this.adapter.init(this);
37998     },
37999     
38000     /**
38001      * Gets the minimum size for the resizing element
38002      * @return {Number} The minimum size
38003      */
38004     getMinimumSize : function(){
38005         return this.minSize;
38006     },
38007     
38008     /**
38009      * Sets the minimum size for the resizing element
38010      * @param {Number} minSize The minimum size
38011      */
38012     setMinimumSize : function(minSize){
38013         this.minSize = minSize;
38014     },
38015     
38016     /**
38017      * Gets the maximum size for the resizing element
38018      * @return {Number} The maximum size
38019      */
38020     getMaximumSize : function(){
38021         return this.maxSize;
38022     },
38023     
38024     /**
38025      * Sets the maximum size for the resizing element
38026      * @param {Number} maxSize The maximum size
38027      */
38028     setMaximumSize : function(maxSize){
38029         this.maxSize = maxSize;
38030     },
38031     
38032     /**
38033      * Sets the initialize size for the resizing element
38034      * @param {Number} size The initial size
38035      */
38036     setCurrentSize : function(size){
38037         var oldAnimate = this.animate;
38038         this.animate = false;
38039         this.adapter.setElementSize(this, size);
38040         this.animate = oldAnimate;
38041     },
38042     
38043     /**
38044      * Destroy this splitbar. 
38045      * @param {Boolean} removeEl True to remove the element
38046      */
38047     destroy : function(removeEl){
38048         if(this.shim){
38049             this.shim.remove();
38050         }
38051         this.dd.unreg();
38052         this.proxy.parentNode.removeChild(this.proxy);
38053         if(removeEl){
38054             this.el.remove();
38055         }
38056     }
38057 });
38058
38059 /**
38060  * @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.
38061  */
38062 Roo.bootstrap.SplitBar.createProxy = function(dir){
38063     var proxy = new Roo.Element(document.createElement("div"));
38064     proxy.unselectable();
38065     var cls = 'roo-splitbar-proxy';
38066     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38067     document.body.appendChild(proxy.dom);
38068     return proxy.dom;
38069 };
38070
38071 /** 
38072  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38073  * Default Adapter. It assumes the splitter and resizing element are not positioned
38074  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38075  */
38076 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38077 };
38078
38079 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38080     // do nothing for now
38081     init : function(s){
38082     
38083     },
38084     /**
38085      * Called before drag operations to get the current size of the resizing element. 
38086      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38087      */
38088      getElementSize : function(s){
38089         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38090             return s.resizingEl.getWidth();
38091         }else{
38092             return s.resizingEl.getHeight();
38093         }
38094     },
38095     
38096     /**
38097      * Called after drag operations to set the size of the resizing element.
38098      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38099      * @param {Number} newSize The new size to set
38100      * @param {Function} onComplete A function to be invoked when resizing is complete
38101      */
38102     setElementSize : function(s, newSize, onComplete){
38103         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38104             if(!s.animate){
38105                 s.resizingEl.setWidth(newSize);
38106                 if(onComplete){
38107                     onComplete(s, newSize);
38108                 }
38109             }else{
38110                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38111             }
38112         }else{
38113             
38114             if(!s.animate){
38115                 s.resizingEl.setHeight(newSize);
38116                 if(onComplete){
38117                     onComplete(s, newSize);
38118                 }
38119             }else{
38120                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38121             }
38122         }
38123     }
38124 };
38125
38126 /** 
38127  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38128  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38129  * Adapter that  moves the splitter element to align with the resized sizing element. 
38130  * Used with an absolute positioned SplitBar.
38131  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38132  * document.body, make sure you assign an id to the body element.
38133  */
38134 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38135     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38136     this.container = Roo.get(container);
38137 };
38138
38139 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38140     init : function(s){
38141         this.basic.init(s);
38142     },
38143     
38144     getElementSize : function(s){
38145         return this.basic.getElementSize(s);
38146     },
38147     
38148     setElementSize : function(s, newSize, onComplete){
38149         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38150     },
38151     
38152     moveSplitter : function(s){
38153         var yes = Roo.bootstrap.SplitBar;
38154         switch(s.placement){
38155             case yes.LEFT:
38156                 s.el.setX(s.resizingEl.getRight());
38157                 break;
38158             case yes.RIGHT:
38159                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38160                 break;
38161             case yes.TOP:
38162                 s.el.setY(s.resizingEl.getBottom());
38163                 break;
38164             case yes.BOTTOM:
38165                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38166                 break;
38167         }
38168     }
38169 };
38170
38171 /**
38172  * Orientation constant - Create a vertical SplitBar
38173  * @static
38174  * @type Number
38175  */
38176 Roo.bootstrap.SplitBar.VERTICAL = 1;
38177
38178 /**
38179  * Orientation constant - Create a horizontal SplitBar
38180  * @static
38181  * @type Number
38182  */
38183 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38184
38185 /**
38186  * Placement constant - The resizing element is to the left of the splitter element
38187  * @static
38188  * @type Number
38189  */
38190 Roo.bootstrap.SplitBar.LEFT = 1;
38191
38192 /**
38193  * Placement constant - The resizing element is to the right of the splitter element
38194  * @static
38195  * @type Number
38196  */
38197 Roo.bootstrap.SplitBar.RIGHT = 2;
38198
38199 /**
38200  * Placement constant - The resizing element is positioned above the splitter element
38201  * @static
38202  * @type Number
38203  */
38204 Roo.bootstrap.SplitBar.TOP = 3;
38205
38206 /**
38207  * Placement constant - The resizing element is positioned under splitter element
38208  * @static
38209  * @type Number
38210  */
38211 Roo.bootstrap.SplitBar.BOTTOM = 4;
38212 Roo.namespace("Roo.bootstrap.layout");/*
38213  * Based on:
38214  * Ext JS Library 1.1.1
38215  * Copyright(c) 2006-2007, Ext JS, LLC.
38216  *
38217  * Originally Released Under LGPL - original licence link has changed is not relivant.
38218  *
38219  * Fork - LGPL
38220  * <script type="text/javascript">
38221  */
38222
38223 /**
38224  * @class Roo.bootstrap.layout.Manager
38225  * @extends Roo.bootstrap.Component
38226  * Base class for layout managers.
38227  */
38228 Roo.bootstrap.layout.Manager = function(config)
38229 {
38230     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38231
38232
38233
38234
38235
38236     /** false to disable window resize monitoring @type Boolean */
38237     this.monitorWindowResize = true;
38238     this.regions = {};
38239     this.addEvents({
38240         /**
38241          * @event layout
38242          * Fires when a layout is performed.
38243          * @param {Roo.LayoutManager} this
38244          */
38245         "layout" : true,
38246         /**
38247          * @event regionresized
38248          * Fires when the user resizes a region.
38249          * @param {Roo.LayoutRegion} region The resized region
38250          * @param {Number} newSize The new size (width for east/west, height for north/south)
38251          */
38252         "regionresized" : true,
38253         /**
38254          * @event regioncollapsed
38255          * Fires when a region is collapsed.
38256          * @param {Roo.LayoutRegion} region The collapsed region
38257          */
38258         "regioncollapsed" : true,
38259         /**
38260          * @event regionexpanded
38261          * Fires when a region is expanded.
38262          * @param {Roo.LayoutRegion} region The expanded region
38263          */
38264         "regionexpanded" : true
38265     });
38266     this.updating = false;
38267
38268     if (config.el) {
38269         this.el = Roo.get(config.el);
38270         this.initEvents();
38271     }
38272
38273 };
38274
38275 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38276
38277
38278     regions : null,
38279
38280     monitorWindowResize : true,
38281
38282
38283     updating : false,
38284
38285
38286     onRender : function(ct, position)
38287     {
38288         if(!this.el){
38289             this.el = Roo.get(ct);
38290             this.initEvents();
38291         }
38292         //this.fireEvent('render',this);
38293     },
38294
38295
38296     initEvents: function()
38297     {
38298
38299
38300         // ie scrollbar fix
38301         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38302             document.body.scroll = "no";
38303         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38304             this.el.position('relative');
38305         }
38306         this.id = this.el.id;
38307         this.el.addClass("roo-layout-container");
38308         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38309         if(this.el.dom != document.body ) {
38310             this.el.on('resize', this.layout,this);
38311             this.el.on('show', this.layout,this);
38312         }
38313
38314     },
38315
38316     /**
38317      * Returns true if this layout is currently being updated
38318      * @return {Boolean}
38319      */
38320     isUpdating : function(){
38321         return this.updating;
38322     },
38323
38324     /**
38325      * Suspend the LayoutManager from doing auto-layouts while
38326      * making multiple add or remove calls
38327      */
38328     beginUpdate : function(){
38329         this.updating = true;
38330     },
38331
38332     /**
38333      * Restore auto-layouts and optionally disable the manager from performing a layout
38334      * @param {Boolean} noLayout true to disable a layout update
38335      */
38336     endUpdate : function(noLayout){
38337         this.updating = false;
38338         if(!noLayout){
38339             this.layout();
38340         }
38341     },
38342
38343     layout: function(){
38344         // abstract...
38345     },
38346
38347     onRegionResized : function(region, newSize){
38348         this.fireEvent("regionresized", region, newSize);
38349         this.layout();
38350     },
38351
38352     onRegionCollapsed : function(region){
38353         this.fireEvent("regioncollapsed", region);
38354     },
38355
38356     onRegionExpanded : function(region){
38357         this.fireEvent("regionexpanded", region);
38358     },
38359
38360     /**
38361      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38362      * performs box-model adjustments.
38363      * @return {Object} The size as an object {width: (the width), height: (the height)}
38364      */
38365     getViewSize : function()
38366     {
38367         var size;
38368         if(this.el.dom != document.body){
38369             size = this.el.getSize();
38370         }else{
38371             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38372         }
38373         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38374         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38375         return size;
38376     },
38377
38378     /**
38379      * Returns the Element this layout is bound to.
38380      * @return {Roo.Element}
38381      */
38382     getEl : function(){
38383         return this.el;
38384     },
38385
38386     /**
38387      * Returns the specified region.
38388      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38389      * @return {Roo.LayoutRegion}
38390      */
38391     getRegion : function(target){
38392         return this.regions[target.toLowerCase()];
38393     },
38394
38395     onWindowResize : function(){
38396         if(this.monitorWindowResize){
38397             this.layout();
38398         }
38399     }
38400 });
38401 /*
38402  * Based on:
38403  * Ext JS Library 1.1.1
38404  * Copyright(c) 2006-2007, Ext JS, LLC.
38405  *
38406  * Originally Released Under LGPL - original licence link has changed is not relivant.
38407  *
38408  * Fork - LGPL
38409  * <script type="text/javascript">
38410  */
38411 /**
38412  * @class Roo.bootstrap.layout.Border
38413  * @extends Roo.bootstrap.layout.Manager
38414  * @builder-top
38415  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38416  * please see: examples/bootstrap/nested.html<br><br>
38417  
38418 <b>The container the layout is rendered into can be either the body element or any other element.
38419 If it is not the body element, the container needs to either be an absolute positioned element,
38420 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38421 the container size if it is not the body element.</b>
38422
38423 * @constructor
38424 * Create a new Border
38425 * @param {Object} config Configuration options
38426  */
38427 Roo.bootstrap.layout.Border = function(config){
38428     config = config || {};
38429     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38430     
38431     
38432     
38433     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38434         if(config[region]){
38435             config[region].region = region;
38436             this.addRegion(config[region]);
38437         }
38438     },this);
38439     
38440 };
38441
38442 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38443
38444 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38445     
38446     parent : false, // this might point to a 'nest' or a ???
38447     
38448     /**
38449      * Creates and adds a new region if it doesn't already exist.
38450      * @param {String} target The target region key (north, south, east, west or center).
38451      * @param {Object} config The regions config object
38452      * @return {BorderLayoutRegion} The new region
38453      */
38454     addRegion : function(config)
38455     {
38456         if(!this.regions[config.region]){
38457             var r = this.factory(config);
38458             this.bindRegion(r);
38459         }
38460         return this.regions[config.region];
38461     },
38462
38463     // private (kinda)
38464     bindRegion : function(r){
38465         this.regions[r.config.region] = r;
38466         
38467         r.on("visibilitychange",    this.layout, this);
38468         r.on("paneladded",          this.layout, this);
38469         r.on("panelremoved",        this.layout, this);
38470         r.on("invalidated",         this.layout, this);
38471         r.on("resized",             this.onRegionResized, this);
38472         r.on("collapsed",           this.onRegionCollapsed, this);
38473         r.on("expanded",            this.onRegionExpanded, this);
38474     },
38475
38476     /**
38477      * Performs a layout update.
38478      */
38479     layout : function()
38480     {
38481         if(this.updating) {
38482             return;
38483         }
38484         
38485         // render all the rebions if they have not been done alreayd?
38486         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38487             if(this.regions[region] && !this.regions[region].bodyEl){
38488                 this.regions[region].onRender(this.el)
38489             }
38490         },this);
38491         
38492         var size = this.getViewSize();
38493         var w = size.width;
38494         var h = size.height;
38495         var centerW = w;
38496         var centerH = h;
38497         var centerY = 0;
38498         var centerX = 0;
38499         //var x = 0, y = 0;
38500
38501         var rs = this.regions;
38502         var north = rs["north"];
38503         var south = rs["south"]; 
38504         var west = rs["west"];
38505         var east = rs["east"];
38506         var center = rs["center"];
38507         //if(this.hideOnLayout){ // not supported anymore
38508             //c.el.setStyle("display", "none");
38509         //}
38510         if(north && north.isVisible()){
38511             var b = north.getBox();
38512             var m = north.getMargins();
38513             b.width = w - (m.left+m.right);
38514             b.x = m.left;
38515             b.y = m.top;
38516             centerY = b.height + b.y + m.bottom;
38517             centerH -= centerY;
38518             north.updateBox(this.safeBox(b));
38519         }
38520         if(south && south.isVisible()){
38521             var b = south.getBox();
38522             var m = south.getMargins();
38523             b.width = w - (m.left+m.right);
38524             b.x = m.left;
38525             var totalHeight = (b.height + m.top + m.bottom);
38526             b.y = h - totalHeight + m.top;
38527             centerH -= totalHeight;
38528             south.updateBox(this.safeBox(b));
38529         }
38530         if(west && west.isVisible()){
38531             var b = west.getBox();
38532             var m = west.getMargins();
38533             b.height = centerH - (m.top+m.bottom);
38534             b.x = m.left;
38535             b.y = centerY + m.top;
38536             var totalWidth = (b.width + m.left + m.right);
38537             centerX += totalWidth;
38538             centerW -= totalWidth;
38539             west.updateBox(this.safeBox(b));
38540         }
38541         if(east && east.isVisible()){
38542             var b = east.getBox();
38543             var m = east.getMargins();
38544             b.height = centerH - (m.top+m.bottom);
38545             var totalWidth = (b.width + m.left + m.right);
38546             b.x = w - totalWidth + m.left;
38547             b.y = centerY + m.top;
38548             centerW -= totalWidth;
38549             east.updateBox(this.safeBox(b));
38550         }
38551         if(center){
38552             var m = center.getMargins();
38553             var centerBox = {
38554                 x: centerX + m.left,
38555                 y: centerY + m.top,
38556                 width: centerW - (m.left+m.right),
38557                 height: centerH - (m.top+m.bottom)
38558             };
38559             //if(this.hideOnLayout){
38560                 //center.el.setStyle("display", "block");
38561             //}
38562             center.updateBox(this.safeBox(centerBox));
38563         }
38564         this.el.repaint();
38565         this.fireEvent("layout", this);
38566     },
38567
38568     // private
38569     safeBox : function(box){
38570         box.width = Math.max(0, box.width);
38571         box.height = Math.max(0, box.height);
38572         return box;
38573     },
38574
38575     /**
38576      * Adds a ContentPanel (or subclass) to this layout.
38577      * @param {String} target The target region key (north, south, east, west or center).
38578      * @param {Roo.ContentPanel} panel The panel to add
38579      * @return {Roo.ContentPanel} The added panel
38580      */
38581     add : function(target, panel){
38582          
38583         target = target.toLowerCase();
38584         return this.regions[target].add(panel);
38585     },
38586
38587     /**
38588      * Remove a ContentPanel (or subclass) to this layout.
38589      * @param {String} target The target region key (north, south, east, west or center).
38590      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38591      * @return {Roo.ContentPanel} The removed panel
38592      */
38593     remove : function(target, panel){
38594         target = target.toLowerCase();
38595         return this.regions[target].remove(panel);
38596     },
38597
38598     /**
38599      * Searches all regions for a panel with the specified id
38600      * @param {String} panelId
38601      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38602      */
38603     findPanel : function(panelId){
38604         var rs = this.regions;
38605         for(var target in rs){
38606             if(typeof rs[target] != "function"){
38607                 var p = rs[target].getPanel(panelId);
38608                 if(p){
38609                     return p;
38610                 }
38611             }
38612         }
38613         return null;
38614     },
38615
38616     /**
38617      * Searches all regions for a panel with the specified id and activates (shows) it.
38618      * @param {String/ContentPanel} panelId The panels id or the panel itself
38619      * @return {Roo.ContentPanel} The shown panel or null
38620      */
38621     showPanel : function(panelId) {
38622       var rs = this.regions;
38623       for(var target in rs){
38624          var r = rs[target];
38625          if(typeof r != "function"){
38626             if(r.hasPanel(panelId)){
38627                return r.showPanel(panelId);
38628             }
38629          }
38630       }
38631       return null;
38632    },
38633
38634    /**
38635      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38636      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38637      */
38638    /*
38639     restoreState : function(provider){
38640         if(!provider){
38641             provider = Roo.state.Manager;
38642         }
38643         var sm = new Roo.LayoutStateManager();
38644         sm.init(this, provider);
38645     },
38646 */
38647  
38648  
38649     /**
38650      * Adds a xtype elements to the layout.
38651      * <pre><code>
38652
38653 layout.addxtype({
38654        xtype : 'ContentPanel',
38655        region: 'west',
38656        items: [ .... ]
38657    }
38658 );
38659
38660 layout.addxtype({
38661         xtype : 'NestedLayoutPanel',
38662         region: 'west',
38663         layout: {
38664            center: { },
38665            west: { }   
38666         },
38667         items : [ ... list of content panels or nested layout panels.. ]
38668    }
38669 );
38670 </code></pre>
38671      * @param {Object} cfg Xtype definition of item to add.
38672      */
38673     addxtype : function(cfg)
38674     {
38675         // basically accepts a pannel...
38676         // can accept a layout region..!?!?
38677         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38678         
38679         
38680         // theory?  children can only be panels??
38681         
38682         //if (!cfg.xtype.match(/Panel$/)) {
38683         //    return false;
38684         //}
38685         var ret = false;
38686         
38687         if (typeof(cfg.region) == 'undefined') {
38688             Roo.log("Failed to add Panel, region was not set");
38689             Roo.log(cfg);
38690             return false;
38691         }
38692         var region = cfg.region;
38693         delete cfg.region;
38694         
38695           
38696         var xitems = [];
38697         if (cfg.items) {
38698             xitems = cfg.items;
38699             delete cfg.items;
38700         }
38701         var nb = false;
38702         
38703         if ( region == 'center') {
38704             Roo.log("Center: " + cfg.title);
38705         }
38706         
38707         
38708         switch(cfg.xtype) 
38709         {
38710             case 'Content':  // ContentPanel (el, cfg)
38711             case 'Scroll':  // ContentPanel (el, cfg)
38712             case 'View': 
38713                 cfg.autoCreate = cfg.autoCreate || true;
38714                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38715                 //} else {
38716                 //    var el = this.el.createChild();
38717                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38718                 //}
38719                 
38720                 this.add(region, ret);
38721                 break;
38722             
38723             /*
38724             case 'TreePanel': // our new panel!
38725                 cfg.el = this.el.createChild();
38726                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38727                 this.add(region, ret);
38728                 break;
38729             */
38730             
38731             case 'Nest': 
38732                 // create a new Layout (which is  a Border Layout...
38733                 
38734                 var clayout = cfg.layout;
38735                 clayout.el  = this.el.createChild();
38736                 clayout.items   = clayout.items  || [];
38737                 
38738                 delete cfg.layout;
38739                 
38740                 // replace this exitems with the clayout ones..
38741                 xitems = clayout.items;
38742                  
38743                 // force background off if it's in center...
38744                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38745                     cfg.background = false;
38746                 }
38747                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38748                 
38749                 
38750                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38751                 //console.log('adding nested layout panel '  + cfg.toSource());
38752                 this.add(region, ret);
38753                 nb = {}; /// find first...
38754                 break;
38755             
38756             case 'Grid':
38757                 
38758                 // needs grid and region
38759                 
38760                 //var el = this.getRegion(region).el.createChild();
38761                 /*
38762                  *var el = this.el.createChild();
38763                 // create the grid first...
38764                 cfg.grid.container = el;
38765                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38766                 */
38767                 
38768                 if (region == 'center' && this.active ) {
38769                     cfg.background = false;
38770                 }
38771                 
38772                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38773                 
38774                 this.add(region, ret);
38775                 /*
38776                 if (cfg.background) {
38777                     // render grid on panel activation (if panel background)
38778                     ret.on('activate', function(gp) {
38779                         if (!gp.grid.rendered) {
38780                     //        gp.grid.render(el);
38781                         }
38782                     });
38783                 } else {
38784                   //  cfg.grid.render(el);
38785                 }
38786                 */
38787                 break;
38788            
38789            
38790             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38791                 // it was the old xcomponent building that caused this before.
38792                 // espeically if border is the top element in the tree.
38793                 ret = this;
38794                 break; 
38795                 
38796                     
38797                 
38798                 
38799                 
38800             default:
38801                 /*
38802                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38803                     
38804                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38805                     this.add(region, ret);
38806                 } else {
38807                 */
38808                     Roo.log(cfg);
38809                     throw "Can not add '" + cfg.xtype + "' to Border";
38810                     return null;
38811              
38812                                 
38813              
38814         }
38815         this.beginUpdate();
38816         // add children..
38817         var region = '';
38818         var abn = {};
38819         Roo.each(xitems, function(i)  {
38820             region = nb && i.region ? i.region : false;
38821             
38822             var add = ret.addxtype(i);
38823            
38824             if (region) {
38825                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38826                 if (!i.background) {
38827                     abn[region] = nb[region] ;
38828                 }
38829             }
38830             
38831         });
38832         this.endUpdate();
38833
38834         // make the last non-background panel active..
38835         //if (nb) { Roo.log(abn); }
38836         if (nb) {
38837             
38838             for(var r in abn) {
38839                 region = this.getRegion(r);
38840                 if (region) {
38841                     // tried using nb[r], but it does not work..
38842                      
38843                     region.showPanel(abn[r]);
38844                    
38845                 }
38846             }
38847         }
38848         return ret;
38849         
38850     },
38851     
38852     
38853 // private
38854     factory : function(cfg)
38855     {
38856         
38857         var validRegions = Roo.bootstrap.layout.Border.regions;
38858
38859         var target = cfg.region;
38860         cfg.mgr = this;
38861         
38862         var r = Roo.bootstrap.layout;
38863         Roo.log(target);
38864         switch(target){
38865             case "north":
38866                 return new r.North(cfg);
38867             case "south":
38868                 return new r.South(cfg);
38869             case "east":
38870                 return new r.East(cfg);
38871             case "west":
38872                 return new r.West(cfg);
38873             case "center":
38874                 return new r.Center(cfg);
38875         }
38876         throw 'Layout region "'+target+'" not supported.';
38877     }
38878     
38879     
38880 });
38881  /*
38882  * Based on:
38883  * Ext JS Library 1.1.1
38884  * Copyright(c) 2006-2007, Ext JS, LLC.
38885  *
38886  * Originally Released Under LGPL - original licence link has changed is not relivant.
38887  *
38888  * Fork - LGPL
38889  * <script type="text/javascript">
38890  */
38891  
38892 /**
38893  * @class Roo.bootstrap.layout.Basic
38894  * @extends Roo.util.Observable
38895  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38896  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38897  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38898  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38899  * @cfg {string}   region  the region that it inhabits..
38900  * @cfg {bool}   skipConfig skip config?
38901  * 
38902
38903  */
38904 Roo.bootstrap.layout.Basic = function(config){
38905     
38906     this.mgr = config.mgr;
38907     
38908     this.position = config.region;
38909     
38910     var skipConfig = config.skipConfig;
38911     
38912     this.events = {
38913         /**
38914          * @scope Roo.BasicLayoutRegion
38915          */
38916         
38917         /**
38918          * @event beforeremove
38919          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38920          * @param {Roo.LayoutRegion} this
38921          * @param {Roo.ContentPanel} panel The panel
38922          * @param {Object} e The cancel event object
38923          */
38924         "beforeremove" : true,
38925         /**
38926          * @event invalidated
38927          * Fires when the layout for this region is changed.
38928          * @param {Roo.LayoutRegion} this
38929          */
38930         "invalidated" : true,
38931         /**
38932          * @event visibilitychange
38933          * Fires when this region is shown or hidden 
38934          * @param {Roo.LayoutRegion} this
38935          * @param {Boolean} visibility true or false
38936          */
38937         "visibilitychange" : true,
38938         /**
38939          * @event paneladded
38940          * Fires when a panel is added. 
38941          * @param {Roo.LayoutRegion} this
38942          * @param {Roo.ContentPanel} panel The panel
38943          */
38944         "paneladded" : true,
38945         /**
38946          * @event panelremoved
38947          * Fires when a panel is removed. 
38948          * @param {Roo.LayoutRegion} this
38949          * @param {Roo.ContentPanel} panel The panel
38950          */
38951         "panelremoved" : true,
38952         /**
38953          * @event beforecollapse
38954          * Fires when this region before collapse.
38955          * @param {Roo.LayoutRegion} this
38956          */
38957         "beforecollapse" : true,
38958         /**
38959          * @event collapsed
38960          * Fires when this region is collapsed.
38961          * @param {Roo.LayoutRegion} this
38962          */
38963         "collapsed" : true,
38964         /**
38965          * @event expanded
38966          * Fires when this region is expanded.
38967          * @param {Roo.LayoutRegion} this
38968          */
38969         "expanded" : true,
38970         /**
38971          * @event slideshow
38972          * Fires when this region is slid into view.
38973          * @param {Roo.LayoutRegion} this
38974          */
38975         "slideshow" : true,
38976         /**
38977          * @event slidehide
38978          * Fires when this region slides out of view. 
38979          * @param {Roo.LayoutRegion} this
38980          */
38981         "slidehide" : true,
38982         /**
38983          * @event panelactivated
38984          * Fires when a panel is activated. 
38985          * @param {Roo.LayoutRegion} this
38986          * @param {Roo.ContentPanel} panel The activated panel
38987          */
38988         "panelactivated" : true,
38989         /**
38990          * @event resized
38991          * Fires when the user resizes this region. 
38992          * @param {Roo.LayoutRegion} this
38993          * @param {Number} newSize The new size (width for east/west, height for north/south)
38994          */
38995         "resized" : true
38996     };
38997     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38998     this.panels = new Roo.util.MixedCollection();
38999     this.panels.getKey = this.getPanelId.createDelegate(this);
39000     this.box = null;
39001     this.activePanel = null;
39002     // ensure listeners are added...
39003     
39004     if (config.listeners || config.events) {
39005         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
39006             listeners : config.listeners || {},
39007             events : config.events || {}
39008         });
39009     }
39010     
39011     if(skipConfig !== true){
39012         this.applyConfig(config);
39013     }
39014 };
39015
39016 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
39017 {
39018     getPanelId : function(p){
39019         return p.getId();
39020     },
39021     
39022     applyConfig : function(config){
39023         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39024         this.config = config;
39025         
39026     },
39027     
39028     /**
39029      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
39030      * the width, for horizontal (north, south) the height.
39031      * @param {Number} newSize The new width or height
39032      */
39033     resizeTo : function(newSize){
39034         var el = this.el ? this.el :
39035                  (this.activePanel ? this.activePanel.getEl() : null);
39036         if(el){
39037             switch(this.position){
39038                 case "east":
39039                 case "west":
39040                     el.setWidth(newSize);
39041                     this.fireEvent("resized", this, newSize);
39042                 break;
39043                 case "north":
39044                 case "south":
39045                     el.setHeight(newSize);
39046                     this.fireEvent("resized", this, newSize);
39047                 break;                
39048             }
39049         }
39050     },
39051     
39052     getBox : function(){
39053         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39054     },
39055     
39056     getMargins : function(){
39057         return this.margins;
39058     },
39059     
39060     updateBox : function(box){
39061         this.box = box;
39062         var el = this.activePanel.getEl();
39063         el.dom.style.left = box.x + "px";
39064         el.dom.style.top = box.y + "px";
39065         this.activePanel.setSize(box.width, box.height);
39066     },
39067     
39068     /**
39069      * Returns the container element for this region.
39070      * @return {Roo.Element}
39071      */
39072     getEl : function(){
39073         return this.activePanel;
39074     },
39075     
39076     /**
39077      * Returns true if this region is currently visible.
39078      * @return {Boolean}
39079      */
39080     isVisible : function(){
39081         return this.activePanel ? true : false;
39082     },
39083     
39084     setActivePanel : function(panel){
39085         panel = this.getPanel(panel);
39086         if(this.activePanel && this.activePanel != panel){
39087             this.activePanel.setActiveState(false);
39088             this.activePanel.getEl().setLeftTop(-10000,-10000);
39089         }
39090         this.activePanel = panel;
39091         panel.setActiveState(true);
39092         if(this.box){
39093             panel.setSize(this.box.width, this.box.height);
39094         }
39095         this.fireEvent("panelactivated", this, panel);
39096         this.fireEvent("invalidated");
39097     },
39098     
39099     /**
39100      * Show the specified panel.
39101      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39102      * @return {Roo.ContentPanel} The shown panel or null
39103      */
39104     showPanel : function(panel){
39105         panel = this.getPanel(panel);
39106         if(panel){
39107             this.setActivePanel(panel);
39108         }
39109         return panel;
39110     },
39111     
39112     /**
39113      * Get the active panel for this region.
39114      * @return {Roo.ContentPanel} The active panel or null
39115      */
39116     getActivePanel : function(){
39117         return this.activePanel;
39118     },
39119     
39120     /**
39121      * Add the passed ContentPanel(s)
39122      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39123      * @return {Roo.ContentPanel} The panel added (if only one was added)
39124      */
39125     add : function(panel){
39126         if(arguments.length > 1){
39127             for(var i = 0, len = arguments.length; i < len; i++) {
39128                 this.add(arguments[i]);
39129             }
39130             return null;
39131         }
39132         if(this.hasPanel(panel)){
39133             this.showPanel(panel);
39134             return panel;
39135         }
39136         var el = panel.getEl();
39137         if(el.dom.parentNode != this.mgr.el.dom){
39138             this.mgr.el.dom.appendChild(el.dom);
39139         }
39140         if(panel.setRegion){
39141             panel.setRegion(this);
39142         }
39143         this.panels.add(panel);
39144         el.setStyle("position", "absolute");
39145         if(!panel.background){
39146             this.setActivePanel(panel);
39147             if(this.config.initialSize && this.panels.getCount()==1){
39148                 this.resizeTo(this.config.initialSize);
39149             }
39150         }
39151         this.fireEvent("paneladded", this, panel);
39152         return panel;
39153     },
39154     
39155     /**
39156      * Returns true if the panel is in this region.
39157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39158      * @return {Boolean}
39159      */
39160     hasPanel : function(panel){
39161         if(typeof panel == "object"){ // must be panel obj
39162             panel = panel.getId();
39163         }
39164         return this.getPanel(panel) ? true : false;
39165     },
39166     
39167     /**
39168      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39169      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39170      * @param {Boolean} preservePanel Overrides the config preservePanel option
39171      * @return {Roo.ContentPanel} The panel that was removed
39172      */
39173     remove : function(panel, preservePanel){
39174         panel = this.getPanel(panel);
39175         if(!panel){
39176             return null;
39177         }
39178         var e = {};
39179         this.fireEvent("beforeremove", this, panel, e);
39180         if(e.cancel === true){
39181             return null;
39182         }
39183         var panelId = panel.getId();
39184         this.panels.removeKey(panelId);
39185         return panel;
39186     },
39187     
39188     /**
39189      * Returns the panel specified or null if it's not in this region.
39190      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39191      * @return {Roo.ContentPanel}
39192      */
39193     getPanel : function(id){
39194         if(typeof id == "object"){ // must be panel obj
39195             return id;
39196         }
39197         return this.panels.get(id);
39198     },
39199     
39200     /**
39201      * Returns this regions position (north/south/east/west/center).
39202      * @return {String} 
39203      */
39204     getPosition: function(){
39205         return this.position;    
39206     }
39207 });/*
39208  * Based on:
39209  * Ext JS Library 1.1.1
39210  * Copyright(c) 2006-2007, Ext JS, LLC.
39211  *
39212  * Originally Released Under LGPL - original licence link has changed is not relivant.
39213  *
39214  * Fork - LGPL
39215  * <script type="text/javascript">
39216  */
39217  
39218 /**
39219  * @class Roo.bootstrap.layout.Region
39220  * @extends Roo.bootstrap.layout.Basic
39221  * This class represents a region in a layout manager.
39222  
39223  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39224  * @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})
39225  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39226  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39227  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39228  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39229  * @cfg {String}    title           The title for the region (overrides panel titles)
39230  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39231  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39232  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39233  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39234  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39235  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39236  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39237  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39238  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39239  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39240
39241  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39242  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39243  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39244  * @cfg {Number}    width           For East/West panels
39245  * @cfg {Number}    height          For North/South panels
39246  * @cfg {Boolean}   split           To show the splitter
39247  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39248  * 
39249  * @cfg {string}   cls             Extra CSS classes to add to region
39250  * 
39251  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39252  * @cfg {string}   region  the region that it inhabits..
39253  *
39254
39255  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39256  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39257
39258  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39259  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39260  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39261  */
39262 Roo.bootstrap.layout.Region = function(config)
39263 {
39264     this.applyConfig(config);
39265
39266     var mgr = config.mgr;
39267     var pos = config.region;
39268     config.skipConfig = true;
39269     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39270     
39271     if (mgr.el) {
39272         this.onRender(mgr.el);   
39273     }
39274      
39275     this.visible = true;
39276     this.collapsed = false;
39277     this.unrendered_panels = [];
39278 };
39279
39280 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39281
39282     position: '', // set by wrapper (eg. north/south etc..)
39283     unrendered_panels : null,  // unrendered panels.
39284     
39285     tabPosition : false,
39286     
39287     mgr: false, // points to 'Border'
39288     
39289     
39290     createBody : function(){
39291         /** This region's body element 
39292         * @type Roo.Element */
39293         this.bodyEl = this.el.createChild({
39294                 tag: "div",
39295                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39296         });
39297     },
39298
39299     onRender: function(ctr, pos)
39300     {
39301         var dh = Roo.DomHelper;
39302         /** This region's container element 
39303         * @type Roo.Element */
39304         this.el = dh.append(ctr.dom, {
39305                 tag: "div",
39306                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39307             }, true);
39308         /** This region's title element 
39309         * @type Roo.Element */
39310     
39311         this.titleEl = dh.append(this.el.dom,  {
39312                 tag: "div",
39313                 unselectable: "on",
39314                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39315                 children:[
39316                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39317                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39318                 ]
39319             }, true);
39320         
39321         this.titleEl.enableDisplayMode();
39322         /** This region's title text element 
39323         * @type HTMLElement */
39324         this.titleTextEl = this.titleEl.dom.firstChild;
39325         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39326         /*
39327         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39328         this.closeBtn.enableDisplayMode();
39329         this.closeBtn.on("click", this.closeClicked, this);
39330         this.closeBtn.hide();
39331     */
39332         this.createBody(this.config);
39333         if(this.config.hideWhenEmpty){
39334             this.hide();
39335             this.on("paneladded", this.validateVisibility, this);
39336             this.on("panelremoved", this.validateVisibility, this);
39337         }
39338         if(this.autoScroll){
39339             this.bodyEl.setStyle("overflow", "auto");
39340         }else{
39341             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39342         }
39343         //if(c.titlebar !== false){
39344             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39345                 this.titleEl.hide();
39346             }else{
39347                 this.titleEl.show();
39348                 if(this.config.title){
39349                     this.titleTextEl.innerHTML = this.config.title;
39350                 }
39351             }
39352         //}
39353         if(this.config.collapsed){
39354             this.collapse(true);
39355         }
39356         if(this.config.hidden){
39357             this.hide();
39358         }
39359         
39360         if (this.unrendered_panels && this.unrendered_panels.length) {
39361             for (var i =0;i< this.unrendered_panels.length; i++) {
39362                 this.add(this.unrendered_panels[i]);
39363             }
39364             this.unrendered_panels = null;
39365             
39366         }
39367         
39368     },
39369     
39370     applyConfig : function(c)
39371     {
39372         /*
39373          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39374             var dh = Roo.DomHelper;
39375             if(c.titlebar !== false){
39376                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39377                 this.collapseBtn.on("click", this.collapse, this);
39378                 this.collapseBtn.enableDisplayMode();
39379                 /*
39380                 if(c.showPin === true || this.showPin){
39381                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39382                     this.stickBtn.enableDisplayMode();
39383                     this.stickBtn.on("click", this.expand, this);
39384                     this.stickBtn.hide();
39385                 }
39386                 
39387             }
39388             */
39389             /** This region's collapsed element
39390             * @type Roo.Element */
39391             /*
39392              *
39393             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39394                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39395             ]}, true);
39396             
39397             if(c.floatable !== false){
39398                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39399                this.collapsedEl.on("click", this.collapseClick, this);
39400             }
39401
39402             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39403                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39404                    id: "message", unselectable: "on", style:{"float":"left"}});
39405                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39406              }
39407             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39408             this.expandBtn.on("click", this.expand, this);
39409             
39410         }
39411         
39412         if(this.collapseBtn){
39413             this.collapseBtn.setVisible(c.collapsible == true);
39414         }
39415         
39416         this.cmargins = c.cmargins || this.cmargins ||
39417                          (this.position == "west" || this.position == "east" ?
39418                              {top: 0, left: 2, right:2, bottom: 0} :
39419                              {top: 2, left: 0, right:0, bottom: 2});
39420         */
39421         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39422         
39423         
39424         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39425         
39426         this.autoScroll = c.autoScroll || false;
39427         
39428         
39429        
39430         
39431         this.duration = c.duration || .30;
39432         this.slideDuration = c.slideDuration || .45;
39433         this.config = c;
39434        
39435     },
39436     /**
39437      * Returns true if this region is currently visible.
39438      * @return {Boolean}
39439      */
39440     isVisible : function(){
39441         return this.visible;
39442     },
39443
39444     /**
39445      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39446      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39447      */
39448     //setCollapsedTitle : function(title){
39449     //    title = title || "&#160;";
39450      //   if(this.collapsedTitleTextEl){
39451       //      this.collapsedTitleTextEl.innerHTML = title;
39452        // }
39453     //},
39454
39455     getBox : function(){
39456         var b;
39457       //  if(!this.collapsed){
39458             b = this.el.getBox(false, true);
39459        // }else{
39460           //  b = this.collapsedEl.getBox(false, true);
39461         //}
39462         return b;
39463     },
39464
39465     getMargins : function(){
39466         return this.margins;
39467         //return this.collapsed ? this.cmargins : this.margins;
39468     },
39469 /*
39470     highlight : function(){
39471         this.el.addClass("x-layout-panel-dragover");
39472     },
39473
39474     unhighlight : function(){
39475         this.el.removeClass("x-layout-panel-dragover");
39476     },
39477 */
39478     updateBox : function(box)
39479     {
39480         if (!this.bodyEl) {
39481             return; // not rendered yet..
39482         }
39483         
39484         this.box = box;
39485         if(!this.collapsed){
39486             this.el.dom.style.left = box.x + "px";
39487             this.el.dom.style.top = box.y + "px";
39488             this.updateBody(box.width, box.height);
39489         }else{
39490             this.collapsedEl.dom.style.left = box.x + "px";
39491             this.collapsedEl.dom.style.top = box.y + "px";
39492             this.collapsedEl.setSize(box.width, box.height);
39493         }
39494         if(this.tabs){
39495             this.tabs.autoSizeTabs();
39496         }
39497     },
39498
39499     updateBody : function(w, h)
39500     {
39501         if(w !== null){
39502             this.el.setWidth(w);
39503             w -= this.el.getBorderWidth("rl");
39504             if(this.config.adjustments){
39505                 w += this.config.adjustments[0];
39506             }
39507         }
39508         if(h !== null && h > 0){
39509             this.el.setHeight(h);
39510             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39511             h -= this.el.getBorderWidth("tb");
39512             if(this.config.adjustments){
39513                 h += this.config.adjustments[1];
39514             }
39515             this.bodyEl.setHeight(h);
39516             if(this.tabs){
39517                 h = this.tabs.syncHeight(h);
39518             }
39519         }
39520         if(this.panelSize){
39521             w = w !== null ? w : this.panelSize.width;
39522             h = h !== null ? h : this.panelSize.height;
39523         }
39524         if(this.activePanel){
39525             var el = this.activePanel.getEl();
39526             w = w !== null ? w : el.getWidth();
39527             h = h !== null ? h : el.getHeight();
39528             this.panelSize = {width: w, height: h};
39529             this.activePanel.setSize(w, h);
39530         }
39531         if(Roo.isIE && this.tabs){
39532             this.tabs.el.repaint();
39533         }
39534     },
39535
39536     /**
39537      * Returns the container element for this region.
39538      * @return {Roo.Element}
39539      */
39540     getEl : function(){
39541         return this.el;
39542     },
39543
39544     /**
39545      * Hides this region.
39546      */
39547     hide : function(){
39548         //if(!this.collapsed){
39549             this.el.dom.style.left = "-2000px";
39550             this.el.hide();
39551         //}else{
39552          //   this.collapsedEl.dom.style.left = "-2000px";
39553          //   this.collapsedEl.hide();
39554        // }
39555         this.visible = false;
39556         this.fireEvent("visibilitychange", this, false);
39557     },
39558
39559     /**
39560      * Shows this region if it was previously hidden.
39561      */
39562     show : function(){
39563         //if(!this.collapsed){
39564             this.el.show();
39565         //}else{
39566         //    this.collapsedEl.show();
39567        // }
39568         this.visible = true;
39569         this.fireEvent("visibilitychange", this, true);
39570     },
39571 /*
39572     closeClicked : function(){
39573         if(this.activePanel){
39574             this.remove(this.activePanel);
39575         }
39576     },
39577
39578     collapseClick : function(e){
39579         if(this.isSlid){
39580            e.stopPropagation();
39581            this.slideIn();
39582         }else{
39583            e.stopPropagation();
39584            this.slideOut();
39585         }
39586     },
39587 */
39588     /**
39589      * Collapses this region.
39590      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39591      */
39592     /*
39593     collapse : function(skipAnim, skipCheck = false){
39594         if(this.collapsed) {
39595             return;
39596         }
39597         
39598         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39599             
39600             this.collapsed = true;
39601             if(this.split){
39602                 this.split.el.hide();
39603             }
39604             if(this.config.animate && skipAnim !== true){
39605                 this.fireEvent("invalidated", this);
39606                 this.animateCollapse();
39607             }else{
39608                 this.el.setLocation(-20000,-20000);
39609                 this.el.hide();
39610                 this.collapsedEl.show();
39611                 this.fireEvent("collapsed", this);
39612                 this.fireEvent("invalidated", this);
39613             }
39614         }
39615         
39616     },
39617 */
39618     animateCollapse : function(){
39619         // overridden
39620     },
39621
39622     /**
39623      * Expands this region if it was previously collapsed.
39624      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39625      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39626      */
39627     /*
39628     expand : function(e, skipAnim){
39629         if(e) {
39630             e.stopPropagation();
39631         }
39632         if(!this.collapsed || this.el.hasActiveFx()) {
39633             return;
39634         }
39635         if(this.isSlid){
39636             this.afterSlideIn();
39637             skipAnim = true;
39638         }
39639         this.collapsed = false;
39640         if(this.config.animate && skipAnim !== true){
39641             this.animateExpand();
39642         }else{
39643             this.el.show();
39644             if(this.split){
39645                 this.split.el.show();
39646             }
39647             this.collapsedEl.setLocation(-2000,-2000);
39648             this.collapsedEl.hide();
39649             this.fireEvent("invalidated", this);
39650             this.fireEvent("expanded", this);
39651         }
39652     },
39653 */
39654     animateExpand : function(){
39655         // overridden
39656     },
39657
39658     initTabs : function()
39659     {
39660         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39661         
39662         var ts = new Roo.bootstrap.panel.Tabs({
39663             el: this.bodyEl.dom,
39664             region : this,
39665             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39666             disableTooltips: this.config.disableTabTips,
39667             toolbar : this.config.toolbar
39668         });
39669         
39670         if(this.config.hideTabs){
39671             ts.stripWrap.setDisplayed(false);
39672         }
39673         this.tabs = ts;
39674         ts.resizeTabs = this.config.resizeTabs === true;
39675         ts.minTabWidth = this.config.minTabWidth || 40;
39676         ts.maxTabWidth = this.config.maxTabWidth || 250;
39677         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39678         ts.monitorResize = false;
39679         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39680         ts.bodyEl.addClass('roo-layout-tabs-body');
39681         this.panels.each(this.initPanelAsTab, this);
39682     },
39683
39684     initPanelAsTab : function(panel){
39685         var ti = this.tabs.addTab(
39686             panel.getEl().id,
39687             panel.getTitle(),
39688             null,
39689             this.config.closeOnTab && panel.isClosable(),
39690             panel.tpl
39691         );
39692         if(panel.tabTip !== undefined){
39693             ti.setTooltip(panel.tabTip);
39694         }
39695         ti.on("activate", function(){
39696               this.setActivePanel(panel);
39697         }, this);
39698         
39699         if(this.config.closeOnTab){
39700             ti.on("beforeclose", function(t, e){
39701                 e.cancel = true;
39702                 this.remove(panel);
39703             }, this);
39704         }
39705         
39706         panel.tabItem = ti;
39707         
39708         return ti;
39709     },
39710
39711     updatePanelTitle : function(panel, title)
39712     {
39713         if(this.activePanel == panel){
39714             this.updateTitle(title);
39715         }
39716         if(this.tabs){
39717             var ti = this.tabs.getTab(panel.getEl().id);
39718             ti.setText(title);
39719             if(panel.tabTip !== undefined){
39720                 ti.setTooltip(panel.tabTip);
39721             }
39722         }
39723     },
39724
39725     updateTitle : function(title){
39726         if(this.titleTextEl && !this.config.title){
39727             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39728         }
39729     },
39730
39731     setActivePanel : function(panel)
39732     {
39733         panel = this.getPanel(panel);
39734         if(this.activePanel && this.activePanel != panel){
39735             if(this.activePanel.setActiveState(false) === false){
39736                 return;
39737             }
39738         }
39739         this.activePanel = panel;
39740         panel.setActiveState(true);
39741         if(this.panelSize){
39742             panel.setSize(this.panelSize.width, this.panelSize.height);
39743         }
39744         if(this.closeBtn){
39745             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39746         }
39747         this.updateTitle(panel.getTitle());
39748         if(this.tabs){
39749             this.fireEvent("invalidated", this);
39750         }
39751         this.fireEvent("panelactivated", this, panel);
39752     },
39753
39754     /**
39755      * Shows the specified panel.
39756      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39757      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39758      */
39759     showPanel : function(panel)
39760     {
39761         panel = this.getPanel(panel);
39762         if(panel){
39763             if(this.tabs){
39764                 var tab = this.tabs.getTab(panel.getEl().id);
39765                 if(tab.isHidden()){
39766                     this.tabs.unhideTab(tab.id);
39767                 }
39768                 tab.activate();
39769             }else{
39770                 this.setActivePanel(panel);
39771             }
39772         }
39773         return panel;
39774     },
39775
39776     /**
39777      * Get the active panel for this region.
39778      * @return {Roo.ContentPanel} The active panel or null
39779      */
39780     getActivePanel : function(){
39781         return this.activePanel;
39782     },
39783
39784     validateVisibility : function(){
39785         if(this.panels.getCount() < 1){
39786             this.updateTitle("&#160;");
39787             this.closeBtn.hide();
39788             this.hide();
39789         }else{
39790             if(!this.isVisible()){
39791                 this.show();
39792             }
39793         }
39794     },
39795
39796     /**
39797      * Adds the passed ContentPanel(s) to this region.
39798      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39799      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39800      */
39801     add : function(panel)
39802     {
39803         if(arguments.length > 1){
39804             for(var i = 0, len = arguments.length; i < len; i++) {
39805                 this.add(arguments[i]);
39806             }
39807             return null;
39808         }
39809         
39810         // if we have not been rendered yet, then we can not really do much of this..
39811         if (!this.bodyEl) {
39812             this.unrendered_panels.push(panel);
39813             return panel;
39814         }
39815         
39816         
39817         
39818         
39819         if(this.hasPanel(panel)){
39820             this.showPanel(panel);
39821             return panel;
39822         }
39823         panel.setRegion(this);
39824         this.panels.add(panel);
39825        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39826             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39827             // and hide them... ???
39828             this.bodyEl.dom.appendChild(panel.getEl().dom);
39829             if(panel.background !== true){
39830                 this.setActivePanel(panel);
39831             }
39832             this.fireEvent("paneladded", this, panel);
39833             return panel;
39834         }
39835         */
39836         if(!this.tabs){
39837             this.initTabs();
39838         }else{
39839             this.initPanelAsTab(panel);
39840         }
39841         
39842         
39843         if(panel.background !== true){
39844             this.tabs.activate(panel.getEl().id);
39845         }
39846         this.fireEvent("paneladded", this, panel);
39847         return panel;
39848     },
39849
39850     /**
39851      * Hides the tab for the specified panel.
39852      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39853      */
39854     hidePanel : function(panel){
39855         if(this.tabs && (panel = this.getPanel(panel))){
39856             this.tabs.hideTab(panel.getEl().id);
39857         }
39858     },
39859
39860     /**
39861      * Unhides the tab for a previously hidden panel.
39862      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39863      */
39864     unhidePanel : function(panel){
39865         if(this.tabs && (panel = this.getPanel(panel))){
39866             this.tabs.unhideTab(panel.getEl().id);
39867         }
39868     },
39869
39870     clearPanels : function(){
39871         while(this.panels.getCount() > 0){
39872              this.remove(this.panels.first());
39873         }
39874     },
39875
39876     /**
39877      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39878      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39879      * @param {Boolean} preservePanel Overrides the config preservePanel option
39880      * @return {Roo.ContentPanel} The panel that was removed
39881      */
39882     remove : function(panel, preservePanel)
39883     {
39884         panel = this.getPanel(panel);
39885         if(!panel){
39886             return null;
39887         }
39888         var e = {};
39889         this.fireEvent("beforeremove", this, panel, e);
39890         if(e.cancel === true){
39891             return null;
39892         }
39893         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39894         var panelId = panel.getId();
39895         this.panels.removeKey(panelId);
39896         if(preservePanel){
39897             document.body.appendChild(panel.getEl().dom);
39898         }
39899         if(this.tabs){
39900             this.tabs.removeTab(panel.getEl().id);
39901         }else if (!preservePanel){
39902             this.bodyEl.dom.removeChild(panel.getEl().dom);
39903         }
39904         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39905             var p = this.panels.first();
39906             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39907             tempEl.appendChild(p.getEl().dom);
39908             this.bodyEl.update("");
39909             this.bodyEl.dom.appendChild(p.getEl().dom);
39910             tempEl = null;
39911             this.updateTitle(p.getTitle());
39912             this.tabs = null;
39913             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39914             this.setActivePanel(p);
39915         }
39916         panel.setRegion(null);
39917         if(this.activePanel == panel){
39918             this.activePanel = null;
39919         }
39920         if(this.config.autoDestroy !== false && preservePanel !== true){
39921             try{panel.destroy();}catch(e){}
39922         }
39923         this.fireEvent("panelremoved", this, panel);
39924         return panel;
39925     },
39926
39927     /**
39928      * Returns the TabPanel component used by this region
39929      * @return {Roo.TabPanel}
39930      */
39931     getTabs : function(){
39932         return this.tabs;
39933     },
39934
39935     createTool : function(parentEl, className){
39936         var btn = Roo.DomHelper.append(parentEl, {
39937             tag: "div",
39938             cls: "x-layout-tools-button",
39939             children: [ {
39940                 tag: "div",
39941                 cls: "roo-layout-tools-button-inner " + className,
39942                 html: "&#160;"
39943             }]
39944         }, true);
39945         btn.addClassOnOver("roo-layout-tools-button-over");
39946         return btn;
39947     }
39948 });/*
39949  * Based on:
39950  * Ext JS Library 1.1.1
39951  * Copyright(c) 2006-2007, Ext JS, LLC.
39952  *
39953  * Originally Released Under LGPL - original licence link has changed is not relivant.
39954  *
39955  * Fork - LGPL
39956  * <script type="text/javascript">
39957  */
39958  
39959
39960
39961 /**
39962  * @class Roo.SplitLayoutRegion
39963  * @extends Roo.LayoutRegion
39964  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39965  */
39966 Roo.bootstrap.layout.Split = function(config){
39967     this.cursor = config.cursor;
39968     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39969 };
39970
39971 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39972 {
39973     splitTip : "Drag to resize.",
39974     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39975     useSplitTips : false,
39976
39977     applyConfig : function(config){
39978         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39979     },
39980     
39981     onRender : function(ctr,pos) {
39982         
39983         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39984         if(!this.config.split){
39985             return;
39986         }
39987         if(!this.split){
39988             
39989             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39990                             tag: "div",
39991                             id: this.el.id + "-split",
39992                             cls: "roo-layout-split roo-layout-split-"+this.position,
39993                             html: "&#160;"
39994             });
39995             /** The SplitBar for this region 
39996             * @type Roo.SplitBar */
39997             // does not exist yet...
39998             Roo.log([this.position, this.orientation]);
39999             
40000             this.split = new Roo.bootstrap.SplitBar({
40001                 dragElement : splitEl,
40002                 resizingElement: this.el,
40003                 orientation : this.orientation
40004             });
40005             
40006             this.split.on("moved", this.onSplitMove, this);
40007             this.split.useShim = this.config.useShim === true;
40008             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
40009             if(this.useSplitTips){
40010                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
40011             }
40012             //if(config.collapsible){
40013             //    this.split.el.on("dblclick", this.collapse,  this);
40014             //}
40015         }
40016         if(typeof this.config.minSize != "undefined"){
40017             this.split.minSize = this.config.minSize;
40018         }
40019         if(typeof this.config.maxSize != "undefined"){
40020             this.split.maxSize = this.config.maxSize;
40021         }
40022         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
40023             this.hideSplitter();
40024         }
40025         
40026     },
40027
40028     getHMaxSize : function(){
40029          var cmax = this.config.maxSize || 10000;
40030          var center = this.mgr.getRegion("center");
40031          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
40032     },
40033
40034     getVMaxSize : function(){
40035          var cmax = this.config.maxSize || 10000;
40036          var center = this.mgr.getRegion("center");
40037          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40038     },
40039
40040     onSplitMove : function(split, newSize){
40041         this.fireEvent("resized", this, newSize);
40042     },
40043     
40044     /** 
40045      * Returns the {@link Roo.SplitBar} for this region.
40046      * @return {Roo.SplitBar}
40047      */
40048     getSplitBar : function(){
40049         return this.split;
40050     },
40051     
40052     hide : function(){
40053         this.hideSplitter();
40054         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40055     },
40056
40057     hideSplitter : function(){
40058         if(this.split){
40059             this.split.el.setLocation(-2000,-2000);
40060             this.split.el.hide();
40061         }
40062     },
40063
40064     show : function(){
40065         if(this.split){
40066             this.split.el.show();
40067         }
40068         Roo.bootstrap.layout.Split.superclass.show.call(this);
40069     },
40070     
40071     beforeSlide: function(){
40072         if(Roo.isGecko){// firefox overflow auto bug workaround
40073             this.bodyEl.clip();
40074             if(this.tabs) {
40075                 this.tabs.bodyEl.clip();
40076             }
40077             if(this.activePanel){
40078                 this.activePanel.getEl().clip();
40079                 
40080                 if(this.activePanel.beforeSlide){
40081                     this.activePanel.beforeSlide();
40082                 }
40083             }
40084         }
40085     },
40086     
40087     afterSlide : function(){
40088         if(Roo.isGecko){// firefox overflow auto bug workaround
40089             this.bodyEl.unclip();
40090             if(this.tabs) {
40091                 this.tabs.bodyEl.unclip();
40092             }
40093             if(this.activePanel){
40094                 this.activePanel.getEl().unclip();
40095                 if(this.activePanel.afterSlide){
40096                     this.activePanel.afterSlide();
40097                 }
40098             }
40099         }
40100     },
40101
40102     initAutoHide : function(){
40103         if(this.autoHide !== false){
40104             if(!this.autoHideHd){
40105                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40106                 this.autoHideHd = {
40107                     "mouseout": function(e){
40108                         if(!e.within(this.el, true)){
40109                             st.delay(500);
40110                         }
40111                     },
40112                     "mouseover" : function(e){
40113                         st.cancel();
40114                     },
40115                     scope : this
40116                 };
40117             }
40118             this.el.on(this.autoHideHd);
40119         }
40120     },
40121
40122     clearAutoHide : function(){
40123         if(this.autoHide !== false){
40124             this.el.un("mouseout", this.autoHideHd.mouseout);
40125             this.el.un("mouseover", this.autoHideHd.mouseover);
40126         }
40127     },
40128
40129     clearMonitor : function(){
40130         Roo.get(document).un("click", this.slideInIf, this);
40131     },
40132
40133     // these names are backwards but not changed for compat
40134     slideOut : function(){
40135         if(this.isSlid || this.el.hasActiveFx()){
40136             return;
40137         }
40138         this.isSlid = true;
40139         if(this.collapseBtn){
40140             this.collapseBtn.hide();
40141         }
40142         this.closeBtnState = this.closeBtn.getStyle('display');
40143         this.closeBtn.hide();
40144         if(this.stickBtn){
40145             this.stickBtn.show();
40146         }
40147         this.el.show();
40148         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40149         this.beforeSlide();
40150         this.el.setStyle("z-index", 10001);
40151         this.el.slideIn(this.getSlideAnchor(), {
40152             callback: function(){
40153                 this.afterSlide();
40154                 this.initAutoHide();
40155                 Roo.get(document).on("click", this.slideInIf, this);
40156                 this.fireEvent("slideshow", this);
40157             },
40158             scope: this,
40159             block: true
40160         });
40161     },
40162
40163     afterSlideIn : function(){
40164         this.clearAutoHide();
40165         this.isSlid = false;
40166         this.clearMonitor();
40167         this.el.setStyle("z-index", "");
40168         if(this.collapseBtn){
40169             this.collapseBtn.show();
40170         }
40171         this.closeBtn.setStyle('display', this.closeBtnState);
40172         if(this.stickBtn){
40173             this.stickBtn.hide();
40174         }
40175         this.fireEvent("slidehide", this);
40176     },
40177
40178     slideIn : function(cb){
40179         if(!this.isSlid || this.el.hasActiveFx()){
40180             Roo.callback(cb);
40181             return;
40182         }
40183         this.isSlid = false;
40184         this.beforeSlide();
40185         this.el.slideOut(this.getSlideAnchor(), {
40186             callback: function(){
40187                 this.el.setLeftTop(-10000, -10000);
40188                 this.afterSlide();
40189                 this.afterSlideIn();
40190                 Roo.callback(cb);
40191             },
40192             scope: this,
40193             block: true
40194         });
40195     },
40196     
40197     slideInIf : function(e){
40198         if(!e.within(this.el)){
40199             this.slideIn();
40200         }
40201     },
40202
40203     animateCollapse : function(){
40204         this.beforeSlide();
40205         this.el.setStyle("z-index", 20000);
40206         var anchor = this.getSlideAnchor();
40207         this.el.slideOut(anchor, {
40208             callback : function(){
40209                 this.el.setStyle("z-index", "");
40210                 this.collapsedEl.slideIn(anchor, {duration:.3});
40211                 this.afterSlide();
40212                 this.el.setLocation(-10000,-10000);
40213                 this.el.hide();
40214                 this.fireEvent("collapsed", this);
40215             },
40216             scope: this,
40217             block: true
40218         });
40219     },
40220
40221     animateExpand : function(){
40222         this.beforeSlide();
40223         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40224         this.el.setStyle("z-index", 20000);
40225         this.collapsedEl.hide({
40226             duration:.1
40227         });
40228         this.el.slideIn(this.getSlideAnchor(), {
40229             callback : function(){
40230                 this.el.setStyle("z-index", "");
40231                 this.afterSlide();
40232                 if(this.split){
40233                     this.split.el.show();
40234                 }
40235                 this.fireEvent("invalidated", this);
40236                 this.fireEvent("expanded", this);
40237             },
40238             scope: this,
40239             block: true
40240         });
40241     },
40242
40243     anchors : {
40244         "west" : "left",
40245         "east" : "right",
40246         "north" : "top",
40247         "south" : "bottom"
40248     },
40249
40250     sanchors : {
40251         "west" : "l",
40252         "east" : "r",
40253         "north" : "t",
40254         "south" : "b"
40255     },
40256
40257     canchors : {
40258         "west" : "tl-tr",
40259         "east" : "tr-tl",
40260         "north" : "tl-bl",
40261         "south" : "bl-tl"
40262     },
40263
40264     getAnchor : function(){
40265         return this.anchors[this.position];
40266     },
40267
40268     getCollapseAnchor : function(){
40269         return this.canchors[this.position];
40270     },
40271
40272     getSlideAnchor : function(){
40273         return this.sanchors[this.position];
40274     },
40275
40276     getAlignAdj : function(){
40277         var cm = this.cmargins;
40278         switch(this.position){
40279             case "west":
40280                 return [0, 0];
40281             break;
40282             case "east":
40283                 return [0, 0];
40284             break;
40285             case "north":
40286                 return [0, 0];
40287             break;
40288             case "south":
40289                 return [0, 0];
40290             break;
40291         }
40292     },
40293
40294     getExpandAdj : function(){
40295         var c = this.collapsedEl, cm = this.cmargins;
40296         switch(this.position){
40297             case "west":
40298                 return [-(cm.right+c.getWidth()+cm.left), 0];
40299             break;
40300             case "east":
40301                 return [cm.right+c.getWidth()+cm.left, 0];
40302             break;
40303             case "north":
40304                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40305             break;
40306             case "south":
40307                 return [0, cm.top+cm.bottom+c.getHeight()];
40308             break;
40309         }
40310     }
40311 });/*
40312  * Based on:
40313  * Ext JS Library 1.1.1
40314  * Copyright(c) 2006-2007, Ext JS, LLC.
40315  *
40316  * Originally Released Under LGPL - original licence link has changed is not relivant.
40317  *
40318  * Fork - LGPL
40319  * <script type="text/javascript">
40320  */
40321 /*
40322  * These classes are private internal classes
40323  */
40324 Roo.bootstrap.layout.Center = function(config){
40325     config.region = "center";
40326     Roo.bootstrap.layout.Region.call(this, config);
40327     this.visible = true;
40328     this.minWidth = config.minWidth || 20;
40329     this.minHeight = config.minHeight || 20;
40330 };
40331
40332 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40333     hide : function(){
40334         // center panel can't be hidden
40335     },
40336     
40337     show : function(){
40338         // center panel can't be hidden
40339     },
40340     
40341     getMinWidth: function(){
40342         return this.minWidth;
40343     },
40344     
40345     getMinHeight: function(){
40346         return this.minHeight;
40347     }
40348 });
40349
40350
40351
40352
40353  
40354
40355
40356
40357
40358
40359
40360 Roo.bootstrap.layout.North = function(config)
40361 {
40362     config.region = 'north';
40363     config.cursor = 'n-resize';
40364     
40365     Roo.bootstrap.layout.Split.call(this, config);
40366     
40367     
40368     if(this.split){
40369         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40370         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40371         this.split.el.addClass("roo-layout-split-v");
40372     }
40373     //var size = config.initialSize || config.height;
40374     //if(this.el && typeof size != "undefined"){
40375     //    this.el.setHeight(size);
40376     //}
40377 };
40378 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40379 {
40380     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40381      
40382      
40383     onRender : function(ctr, pos)
40384     {
40385         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40386         var size = this.config.initialSize || this.config.height;
40387         if(this.el && typeof size != "undefined"){
40388             this.el.setHeight(size);
40389         }
40390     
40391     },
40392     
40393     getBox : function(){
40394         if(this.collapsed){
40395             return this.collapsedEl.getBox();
40396         }
40397         var box = this.el.getBox();
40398         if(this.split){
40399             box.height += this.split.el.getHeight();
40400         }
40401         return box;
40402     },
40403     
40404     updateBox : function(box){
40405         if(this.split && !this.collapsed){
40406             box.height -= this.split.el.getHeight();
40407             this.split.el.setLeft(box.x);
40408             this.split.el.setTop(box.y+box.height);
40409             this.split.el.setWidth(box.width);
40410         }
40411         if(this.collapsed){
40412             this.updateBody(box.width, null);
40413         }
40414         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40415     }
40416 });
40417
40418
40419
40420
40421
40422 Roo.bootstrap.layout.South = function(config){
40423     config.region = 'south';
40424     config.cursor = 's-resize';
40425     Roo.bootstrap.layout.Split.call(this, config);
40426     if(this.split){
40427         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40428         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40429         this.split.el.addClass("roo-layout-split-v");
40430     }
40431     
40432 };
40433
40434 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40435     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40436     
40437     onRender : function(ctr, pos)
40438     {
40439         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40440         var size = this.config.initialSize || this.config.height;
40441         if(this.el && typeof size != "undefined"){
40442             this.el.setHeight(size);
40443         }
40444     
40445     },
40446     
40447     getBox : function(){
40448         if(this.collapsed){
40449             return this.collapsedEl.getBox();
40450         }
40451         var box = this.el.getBox();
40452         if(this.split){
40453             var sh = this.split.el.getHeight();
40454             box.height += sh;
40455             box.y -= sh;
40456         }
40457         return box;
40458     },
40459     
40460     updateBox : function(box){
40461         if(this.split && !this.collapsed){
40462             var sh = this.split.el.getHeight();
40463             box.height -= sh;
40464             box.y += sh;
40465             this.split.el.setLeft(box.x);
40466             this.split.el.setTop(box.y-sh);
40467             this.split.el.setWidth(box.width);
40468         }
40469         if(this.collapsed){
40470             this.updateBody(box.width, null);
40471         }
40472         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40473     }
40474 });
40475
40476 Roo.bootstrap.layout.East = function(config){
40477     config.region = "east";
40478     config.cursor = "e-resize";
40479     Roo.bootstrap.layout.Split.call(this, config);
40480     if(this.split){
40481         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40482         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40483         this.split.el.addClass("roo-layout-split-h");
40484     }
40485     
40486 };
40487 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40488     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40489     
40490     onRender : function(ctr, pos)
40491     {
40492         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40493         var size = this.config.initialSize || this.config.width;
40494         if(this.el && typeof size != "undefined"){
40495             this.el.setWidth(size);
40496         }
40497     
40498     },
40499     
40500     getBox : function(){
40501         if(this.collapsed){
40502             return this.collapsedEl.getBox();
40503         }
40504         var box = this.el.getBox();
40505         if(this.split){
40506             var sw = this.split.el.getWidth();
40507             box.width += sw;
40508             box.x -= sw;
40509         }
40510         return box;
40511     },
40512
40513     updateBox : function(box){
40514         if(this.split && !this.collapsed){
40515             var sw = this.split.el.getWidth();
40516             box.width -= sw;
40517             this.split.el.setLeft(box.x);
40518             this.split.el.setTop(box.y);
40519             this.split.el.setHeight(box.height);
40520             box.x += sw;
40521         }
40522         if(this.collapsed){
40523             this.updateBody(null, box.height);
40524         }
40525         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40526     }
40527 });
40528
40529 Roo.bootstrap.layout.West = function(config){
40530     config.region = "west";
40531     config.cursor = "w-resize";
40532     
40533     Roo.bootstrap.layout.Split.call(this, config);
40534     if(this.split){
40535         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40536         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40537         this.split.el.addClass("roo-layout-split-h");
40538     }
40539     
40540 };
40541 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40542     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40543     
40544     onRender: function(ctr, pos)
40545     {
40546         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40547         var size = this.config.initialSize || this.config.width;
40548         if(typeof size != "undefined"){
40549             this.el.setWidth(size);
40550         }
40551     },
40552     
40553     getBox : function(){
40554         if(this.collapsed){
40555             return this.collapsedEl.getBox();
40556         }
40557         var box = this.el.getBox();
40558         if (box.width == 0) {
40559             box.width = this.config.width; // kludge?
40560         }
40561         if(this.split){
40562             box.width += this.split.el.getWidth();
40563         }
40564         return box;
40565     },
40566     
40567     updateBox : function(box){
40568         if(this.split && !this.collapsed){
40569             var sw = this.split.el.getWidth();
40570             box.width -= sw;
40571             this.split.el.setLeft(box.x+box.width);
40572             this.split.el.setTop(box.y);
40573             this.split.el.setHeight(box.height);
40574         }
40575         if(this.collapsed){
40576             this.updateBody(null, box.height);
40577         }
40578         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40579     }
40580 });Roo.namespace("Roo.bootstrap.panel");/*
40581  * Based on:
40582  * Ext JS Library 1.1.1
40583  * Copyright(c) 2006-2007, Ext JS, LLC.
40584  *
40585  * Originally Released Under LGPL - original licence link has changed is not relivant.
40586  *
40587  * Fork - LGPL
40588  * <script type="text/javascript">
40589  */
40590 /**
40591  * @class Roo.bootstrap.paenl.Content
40592  * @extends Roo.util.Observable
40593  * @builder-top
40594  * @children Roo.bootstrap.Component
40595  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40596  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40597  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40598  * @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
40599  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40600  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40601  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40602  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40603  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40604  * @cfg {String} title          The title for this panel
40605  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40606  * @cfg {String} url            Calls {@link #setUrl} with this value
40607  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40608  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40609  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40610  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40611  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40612  * @cfg {Boolean} badges render the badges
40613  * @cfg {String} cls  extra classes to use  
40614  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40615  
40616  * @constructor
40617  * Create a new ContentPanel.
40618  * @param {String/Object} config A string to set only the title or a config object
40619  
40620  */
40621 Roo.bootstrap.panel.Content = function( config){
40622     
40623     this.tpl = config.tpl || false;
40624     
40625     var el = config.el;
40626     var content = config.content;
40627
40628     if(config.autoCreate){ // xtype is available if this is called from factory
40629         el = Roo.id();
40630     }
40631     this.el = Roo.get(el);
40632     if(!this.el && config && config.autoCreate){
40633         if(typeof config.autoCreate == "object"){
40634             if(!config.autoCreate.id){
40635                 config.autoCreate.id = config.id||el;
40636             }
40637             this.el = Roo.DomHelper.append(document.body,
40638                         config.autoCreate, true);
40639         }else{
40640             var elcfg =  {
40641                 tag: "div",
40642                 cls: (config.cls || '') +
40643                     (config.background ? ' bg-' + config.background : '') +
40644                     " roo-layout-inactive-content",
40645                 id: config.id||el
40646             };
40647             if (config.iframe) {
40648                 elcfg.cn = [
40649                     {
40650                         tag : 'iframe',
40651                         style : 'border: 0px',
40652                         src : 'about:blank'
40653                     }
40654                 ];
40655             }
40656               
40657             if (config.html) {
40658                 elcfg.html = config.html;
40659                 
40660             }
40661                         
40662             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40663             if (config.iframe) {
40664                 this.iframeEl = this.el.select('iframe',true).first();
40665             }
40666             
40667         }
40668     } 
40669     this.closable = false;
40670     this.loaded = false;
40671     this.active = false;
40672    
40673       
40674     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40675         
40676         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40677         
40678         this.wrapEl = this.el; //this.el.wrap();
40679         var ti = [];
40680         if (config.toolbar.items) {
40681             ti = config.toolbar.items ;
40682             delete config.toolbar.items ;
40683         }
40684         
40685         var nitems = [];
40686         this.toolbar.render(this.wrapEl, 'before');
40687         for(var i =0;i < ti.length;i++) {
40688           //  Roo.log(['add child', items[i]]);
40689             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40690         }
40691         this.toolbar.items = nitems;
40692         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40693         delete config.toolbar;
40694         
40695     }
40696     /*
40697     // xtype created footer. - not sure if will work as we normally have to render first..
40698     if (this.footer && !this.footer.el && this.footer.xtype) {
40699         if (!this.wrapEl) {
40700             this.wrapEl = this.el.wrap();
40701         }
40702     
40703         this.footer.container = this.wrapEl.createChild();
40704          
40705         this.footer = Roo.factory(this.footer, Roo);
40706         
40707     }
40708     */
40709     
40710      if(typeof config == "string"){
40711         this.title = config;
40712     }else{
40713         Roo.apply(this, config);
40714     }
40715     
40716     if(this.resizeEl){
40717         this.resizeEl = Roo.get(this.resizeEl, true);
40718     }else{
40719         this.resizeEl = this.el;
40720     }
40721     // handle view.xtype
40722     
40723  
40724     
40725     
40726     this.addEvents({
40727         /**
40728          * @event activate
40729          * Fires when this panel is activated. 
40730          * @param {Roo.ContentPanel} this
40731          */
40732         "activate" : true,
40733         /**
40734          * @event deactivate
40735          * Fires when this panel is activated. 
40736          * @param {Roo.ContentPanel} this
40737          */
40738         "deactivate" : true,
40739
40740         /**
40741          * @event resize
40742          * Fires when this panel is resized if fitToFrame is true.
40743          * @param {Roo.ContentPanel} this
40744          * @param {Number} width The width after any component adjustments
40745          * @param {Number} height The height after any component adjustments
40746          */
40747         "resize" : true,
40748         
40749          /**
40750          * @event render
40751          * Fires when this tab is created
40752          * @param {Roo.ContentPanel} this
40753          */
40754         "render" : true,
40755         
40756           /**
40757          * @event scroll
40758          * Fires when this content is scrolled
40759          * @param {Roo.ContentPanel} this
40760          * @param {Event} scrollEvent
40761          */
40762         "scroll" : true
40763         
40764         
40765         
40766     });
40767     
40768
40769     
40770     
40771     if(this.autoScroll && !this.iframe){
40772         this.resizeEl.setStyle("overflow", "auto");
40773         this.resizeEl.on('scroll', this.onScroll, this);
40774     } else {
40775         // fix randome scrolling
40776         //this.el.on('scroll', function() {
40777         //    Roo.log('fix random scolling');
40778         //    this.scrollTo('top',0); 
40779         //});
40780     }
40781     content = content || this.content;
40782     if(content){
40783         this.setContent(content);
40784     }
40785     if(config && config.url){
40786         this.setUrl(this.url, this.params, this.loadOnce);
40787     }
40788     
40789     
40790     
40791     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40792     
40793     if (this.view && typeof(this.view.xtype) != 'undefined') {
40794         this.view.el = this.el.appendChild(document.createElement("div"));
40795         this.view = Roo.factory(this.view); 
40796         this.view.render  &&  this.view.render(false, '');  
40797     }
40798     
40799     
40800     this.fireEvent('render', this);
40801 };
40802
40803 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40804     
40805     cls : '',
40806     background : '',
40807     
40808     tabTip : '',
40809     
40810     iframe : false,
40811     iframeEl : false,
40812     
40813     /* Resize Element - use this to work out scroll etc. */
40814     resizeEl : false,
40815     
40816     setRegion : function(region){
40817         this.region = region;
40818         this.setActiveClass(region && !this.background);
40819     },
40820     
40821     
40822     setActiveClass: function(state)
40823     {
40824         if(state){
40825            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40826            this.el.setStyle('position','relative');
40827         }else{
40828            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40829            this.el.setStyle('position', 'absolute');
40830         } 
40831     },
40832     
40833     /**
40834      * Returns the toolbar for this Panel if one was configured. 
40835      * @return {Roo.Toolbar} 
40836      */
40837     getToolbar : function(){
40838         return this.toolbar;
40839     },
40840     
40841     setActiveState : function(active)
40842     {
40843         this.active = active;
40844         this.setActiveClass(active);
40845         if(!active){
40846             if(this.fireEvent("deactivate", this) === false){
40847                 return false;
40848             }
40849             return true;
40850         }
40851         this.fireEvent("activate", this);
40852         return true;
40853     },
40854     /**
40855      * Updates this panel's element (not for iframe)
40856      * @param {String} content The new content
40857      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40858     */
40859     setContent : function(content, loadScripts){
40860         if (this.iframe) {
40861             return;
40862         }
40863         
40864         this.el.update(content, loadScripts);
40865     },
40866
40867     ignoreResize : function(w, h){
40868         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40869             return true;
40870         }else{
40871             this.lastSize = {width: w, height: h};
40872             return false;
40873         }
40874     },
40875     /**
40876      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40877      * @return {Roo.UpdateManager} The UpdateManager
40878      */
40879     getUpdateManager : function(){
40880         if (this.iframe) {
40881             return false;
40882         }
40883         return this.el.getUpdateManager();
40884     },
40885      /**
40886      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40887      * Does not work with IFRAME contents
40888      * @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:
40889 <pre><code>
40890 panel.load({
40891     url: "your-url.php",
40892     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40893     callback: yourFunction,
40894     scope: yourObject, //(optional scope)
40895     discardUrl: false,
40896     nocache: false,
40897     text: "Loading...",
40898     timeout: 30,
40899     scripts: false
40900 });
40901 </code></pre>
40902      
40903      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40904      * 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.
40905      * @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}
40906      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40907      * @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.
40908      * @return {Roo.ContentPanel} this
40909      */
40910     load : function(){
40911         
40912         if (this.iframe) {
40913             return this;
40914         }
40915         
40916         var um = this.el.getUpdateManager();
40917         um.update.apply(um, arguments);
40918         return this;
40919     },
40920
40921
40922     /**
40923      * 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.
40924      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40925      * @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)
40926      * @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)
40927      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40928      */
40929     setUrl : function(url, params, loadOnce){
40930         if (this.iframe) {
40931             this.iframeEl.dom.src = url;
40932             return false;
40933         }
40934         
40935         if(this.refreshDelegate){
40936             this.removeListener("activate", this.refreshDelegate);
40937         }
40938         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40939         this.on("activate", this.refreshDelegate);
40940         return this.el.getUpdateManager();
40941     },
40942     
40943     _handleRefresh : function(url, params, loadOnce){
40944         if(!loadOnce || !this.loaded){
40945             var updater = this.el.getUpdateManager();
40946             updater.update(url, params, this._setLoaded.createDelegate(this));
40947         }
40948     },
40949     
40950     _setLoaded : function(){
40951         this.loaded = true;
40952     }, 
40953     
40954     /**
40955      * Returns this panel's id
40956      * @return {String} 
40957      */
40958     getId : function(){
40959         return this.el.id;
40960     },
40961     
40962     /** 
40963      * Returns this panel's element - used by regiosn to add.
40964      * @return {Roo.Element} 
40965      */
40966     getEl : function(){
40967         return this.wrapEl || this.el;
40968     },
40969     
40970    
40971     
40972     adjustForComponents : function(width, height)
40973     {
40974         //Roo.log('adjustForComponents ');
40975         if(this.resizeEl != this.el){
40976             width -= this.el.getFrameWidth('lr');
40977             height -= this.el.getFrameWidth('tb');
40978         }
40979         if(this.toolbar){
40980             var te = this.toolbar.getEl();
40981             te.setWidth(width);
40982             height -= te.getHeight();
40983         }
40984         if(this.footer){
40985             var te = this.footer.getEl();
40986             te.setWidth(width);
40987             height -= te.getHeight();
40988         }
40989         
40990         
40991         if(this.adjustments){
40992             width += this.adjustments[0];
40993             height += this.adjustments[1];
40994         }
40995         return {"width": width, "height": height};
40996     },
40997     
40998     setSize : function(width, height){
40999         if(this.fitToFrame && !this.ignoreResize(width, height)){
41000             if(this.fitContainer && this.resizeEl != this.el){
41001                 this.el.setSize(width, height);
41002             }
41003             var size = this.adjustForComponents(width, height);
41004             if (this.iframe) {
41005                 this.iframeEl.setSize(width,height);
41006             }
41007             
41008             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
41009             this.fireEvent('resize', this, size.width, size.height);
41010             
41011             
41012         }
41013     },
41014     
41015     /**
41016      * Returns this panel's title
41017      * @return {String} 
41018      */
41019     getTitle : function(){
41020         
41021         if (typeof(this.title) != 'object') {
41022             return this.title;
41023         }
41024         
41025         var t = '';
41026         for (var k in this.title) {
41027             if (!this.title.hasOwnProperty(k)) {
41028                 continue;
41029             }
41030             
41031             if (k.indexOf('-') >= 0) {
41032                 var s = k.split('-');
41033                 for (var i = 0; i<s.length; i++) {
41034                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
41035                 }
41036             } else {
41037                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41038             }
41039         }
41040         return t;
41041     },
41042     
41043     /**
41044      * Set this panel's title
41045      * @param {String} title
41046      */
41047     setTitle : function(title){
41048         this.title = title;
41049         if(this.region){
41050             this.region.updatePanelTitle(this, title);
41051         }
41052     },
41053     
41054     /**
41055      * Returns true is this panel was configured to be closable
41056      * @return {Boolean} 
41057      */
41058     isClosable : function(){
41059         return this.closable;
41060     },
41061     
41062     beforeSlide : function(){
41063         this.el.clip();
41064         this.resizeEl.clip();
41065     },
41066     
41067     afterSlide : function(){
41068         this.el.unclip();
41069         this.resizeEl.unclip();
41070     },
41071     
41072     /**
41073      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41074      *   Will fail silently if the {@link #setUrl} method has not been called.
41075      *   This does not activate the panel, just updates its content.
41076      */
41077     refresh : function(){
41078         if(this.refreshDelegate){
41079            this.loaded = false;
41080            this.refreshDelegate();
41081         }
41082     },
41083     
41084     /**
41085      * Destroys this panel
41086      */
41087     destroy : function(){
41088         this.el.removeAllListeners();
41089         var tempEl = document.createElement("span");
41090         tempEl.appendChild(this.el.dom);
41091         tempEl.innerHTML = "";
41092         this.el.remove();
41093         this.el = null;
41094     },
41095     
41096     /**
41097      * form - if the content panel contains a form - this is a reference to it.
41098      * @type {Roo.form.Form}
41099      */
41100     form : false,
41101     /**
41102      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41103      *    This contains a reference to it.
41104      * @type {Roo.View}
41105      */
41106     view : false,
41107     
41108       /**
41109      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41110      * <pre><code>
41111
41112 layout.addxtype({
41113        xtype : 'Form',
41114        items: [ .... ]
41115    }
41116 );
41117
41118 </code></pre>
41119      * @param {Object} cfg Xtype definition of item to add.
41120      */
41121     
41122     
41123     getChildContainer: function () {
41124         return this.getEl();
41125     },
41126     
41127     
41128     onScroll : function(e)
41129     {
41130         this.fireEvent('scroll', this, e);
41131     }
41132     
41133     
41134     /*
41135         var  ret = new Roo.factory(cfg);
41136         return ret;
41137         
41138         
41139         // add form..
41140         if (cfg.xtype.match(/^Form$/)) {
41141             
41142             var el;
41143             //if (this.footer) {
41144             //    el = this.footer.container.insertSibling(false, 'before');
41145             //} else {
41146                 el = this.el.createChild();
41147             //}
41148
41149             this.form = new  Roo.form.Form(cfg);
41150             
41151             
41152             if ( this.form.allItems.length) {
41153                 this.form.render(el.dom);
41154             }
41155             return this.form;
41156         }
41157         // should only have one of theses..
41158         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41159             // views.. should not be just added - used named prop 'view''
41160             
41161             cfg.el = this.el.appendChild(document.createElement("div"));
41162             // factory?
41163             
41164             var ret = new Roo.factory(cfg);
41165              
41166              ret.render && ret.render(false, ''); // render blank..
41167             this.view = ret;
41168             return ret;
41169         }
41170         return false;
41171     }
41172     \*/
41173 });
41174  
41175 /**
41176  * @class Roo.bootstrap.panel.Grid
41177  * @extends Roo.bootstrap.panel.Content
41178  * @constructor
41179  * Create a new GridPanel.
41180  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41181  * @param {Object} config A the config object
41182   
41183  */
41184
41185
41186
41187 Roo.bootstrap.panel.Grid = function(config)
41188 {
41189     
41190       
41191     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41192         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41193
41194     config.el = this.wrapper;
41195     //this.el = this.wrapper;
41196     
41197       if (config.container) {
41198         // ctor'ed from a Border/panel.grid
41199         
41200         
41201         this.wrapper.setStyle("overflow", "hidden");
41202         this.wrapper.addClass('roo-grid-container');
41203
41204     }
41205     
41206     
41207     if(config.toolbar){
41208         var tool_el = this.wrapper.createChild();    
41209         this.toolbar = Roo.factory(config.toolbar);
41210         var ti = [];
41211         if (config.toolbar.items) {
41212             ti = config.toolbar.items ;
41213             delete config.toolbar.items ;
41214         }
41215         
41216         var nitems = [];
41217         this.toolbar.render(tool_el);
41218         for(var i =0;i < ti.length;i++) {
41219           //  Roo.log(['add child', items[i]]);
41220             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41221         }
41222         this.toolbar.items = nitems;
41223         
41224         delete config.toolbar;
41225     }
41226     
41227     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41228     config.grid.scrollBody = true;;
41229     config.grid.monitorWindowResize = false; // turn off autosizing
41230     config.grid.autoHeight = false;
41231     config.grid.autoWidth = false;
41232     
41233     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41234     
41235     if (config.background) {
41236         // render grid on panel activation (if panel background)
41237         this.on('activate', function(gp) {
41238             if (!gp.grid.rendered) {
41239                 gp.grid.render(this.wrapper);
41240                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41241             }
41242         });
41243             
41244     } else {
41245         this.grid.render(this.wrapper);
41246         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41247
41248     }
41249     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41250     // ??? needed ??? config.el = this.wrapper;
41251     
41252     
41253     
41254   
41255     // xtype created footer. - not sure if will work as we normally have to render first..
41256     if (this.footer && !this.footer.el && this.footer.xtype) {
41257         
41258         var ctr = this.grid.getView().getFooterPanel(true);
41259         this.footer.dataSource = this.grid.dataSource;
41260         this.footer = Roo.factory(this.footer, Roo);
41261         this.footer.render(ctr);
41262         
41263     }
41264     
41265     
41266     
41267     
41268      
41269 };
41270
41271 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41272     getId : function(){
41273         return this.grid.id;
41274     },
41275     
41276     /**
41277      * Returns the grid for this panel
41278      * @return {Roo.bootstrap.Table} 
41279      */
41280     getGrid : function(){
41281         return this.grid;    
41282     },
41283     
41284     setSize : function(width, height){
41285         if(!this.ignoreResize(width, height)){
41286             var grid = this.grid;
41287             var size = this.adjustForComponents(width, height);
41288             // tfoot is not a footer?
41289           
41290             
41291             var gridel = grid.getGridEl();
41292             gridel.setSize(size.width, size.height);
41293             
41294             var tbd = grid.getGridEl().select('tbody', true).first();
41295             var thd = grid.getGridEl().select('thead',true).first();
41296             var tbf= grid.getGridEl().select('tfoot', true).first();
41297
41298             if (tbf) {
41299                 size.height -= tbf.getHeight();
41300             }
41301             if (thd) {
41302                 size.height -= thd.getHeight();
41303             }
41304             
41305             tbd.setSize(size.width, size.height );
41306             // this is for the account management tab -seems to work there.
41307             var thd = grid.getGridEl().select('thead',true).first();
41308             //if (tbd) {
41309             //    tbd.setSize(size.width, size.height - thd.getHeight());
41310             //}
41311              
41312             grid.autoSize();
41313         }
41314     },
41315      
41316     
41317     
41318     beforeSlide : function(){
41319         this.grid.getView().scroller.clip();
41320     },
41321     
41322     afterSlide : function(){
41323         this.grid.getView().scroller.unclip();
41324     },
41325     
41326     destroy : function(){
41327         this.grid.destroy();
41328         delete this.grid;
41329         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41330     }
41331 });
41332
41333 /**
41334  * @class Roo.bootstrap.panel.Nest
41335  * @extends Roo.bootstrap.panel.Content
41336  * @constructor
41337  * Create a new Panel, that can contain a layout.Border.
41338  * 
41339  * 
41340  * @param {Roo.BorderLayout} layout The layout for this panel
41341  * @param {String/Object} config A string to set only the title or a config object
41342  */
41343 Roo.bootstrap.panel.Nest = function(config)
41344 {
41345     // construct with only one argument..
41346     /* FIXME - implement nicer consturctors
41347     if (layout.layout) {
41348         config = layout;
41349         layout = config.layout;
41350         delete config.layout;
41351     }
41352     if (layout.xtype && !layout.getEl) {
41353         // then layout needs constructing..
41354         layout = Roo.factory(layout, Roo);
41355     }
41356     */
41357     
41358     config.el =  config.layout.getEl();
41359     
41360     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41361     
41362     config.layout.monitorWindowResize = false; // turn off autosizing
41363     this.layout = config.layout;
41364     this.layout.getEl().addClass("roo-layout-nested-layout");
41365     this.layout.parent = this;
41366     
41367     
41368     
41369     
41370 };
41371
41372 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41373
41374     setSize : function(width, height){
41375         if(!this.ignoreResize(width, height)){
41376             var size = this.adjustForComponents(width, height);
41377             var el = this.layout.getEl();
41378             if (size.height < 1) {
41379                 el.setWidth(size.width);   
41380             } else {
41381                 el.setSize(size.width, size.height);
41382             }
41383             var touch = el.dom.offsetWidth;
41384             this.layout.layout();
41385             // ie requires a double layout on the first pass
41386             if(Roo.isIE && !this.initialized){
41387                 this.initialized = true;
41388                 this.layout.layout();
41389             }
41390         }
41391     },
41392     
41393     // activate all subpanels if not currently active..
41394     
41395     setActiveState : function(active){
41396         this.active = active;
41397         this.setActiveClass(active);
41398         
41399         if(!active){
41400             this.fireEvent("deactivate", this);
41401             return;
41402         }
41403         
41404         this.fireEvent("activate", this);
41405         // not sure if this should happen before or after..
41406         if (!this.layout) {
41407             return; // should not happen..
41408         }
41409         var reg = false;
41410         for (var r in this.layout.regions) {
41411             reg = this.layout.getRegion(r);
41412             if (reg.getActivePanel()) {
41413                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41414                 reg.setActivePanel(reg.getActivePanel());
41415                 continue;
41416             }
41417             if (!reg.panels.length) {
41418                 continue;
41419             }
41420             reg.showPanel(reg.getPanel(0));
41421         }
41422         
41423         
41424         
41425         
41426     },
41427     
41428     /**
41429      * Returns the nested BorderLayout for this panel
41430      * @return {Roo.BorderLayout} 
41431      */
41432     getLayout : function(){
41433         return this.layout;
41434     },
41435     
41436      /**
41437      * Adds a xtype elements to the layout of the nested panel
41438      * <pre><code>
41439
41440 panel.addxtype({
41441        xtype : 'ContentPanel',
41442        region: 'west',
41443        items: [ .... ]
41444    }
41445 );
41446
41447 panel.addxtype({
41448         xtype : 'NestedLayoutPanel',
41449         region: 'west',
41450         layout: {
41451            center: { },
41452            west: { }   
41453         },
41454         items : [ ... list of content panels or nested layout panels.. ]
41455    }
41456 );
41457 </code></pre>
41458      * @param {Object} cfg Xtype definition of item to add.
41459      */
41460     addxtype : function(cfg) {
41461         return this.layout.addxtype(cfg);
41462     
41463     }
41464 });/*
41465  * Based on:
41466  * Ext JS Library 1.1.1
41467  * Copyright(c) 2006-2007, Ext JS, LLC.
41468  *
41469  * Originally Released Under LGPL - original licence link has changed is not relivant.
41470  *
41471  * Fork - LGPL
41472  * <script type="text/javascript">
41473  */
41474 /**
41475  * @class Roo.TabPanel
41476  * @extends Roo.util.Observable
41477  * A lightweight tab container.
41478  * <br><br>
41479  * Usage:
41480  * <pre><code>
41481 // basic tabs 1, built from existing content
41482 var tabs = new Roo.TabPanel("tabs1");
41483 tabs.addTab("script", "View Script");
41484 tabs.addTab("markup", "View Markup");
41485 tabs.activate("script");
41486
41487 // more advanced tabs, built from javascript
41488 var jtabs = new Roo.TabPanel("jtabs");
41489 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41490
41491 // set up the UpdateManager
41492 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41493 var updater = tab2.getUpdateManager();
41494 updater.setDefaultUrl("ajax1.htm");
41495 tab2.on('activate', updater.refresh, updater, true);
41496
41497 // Use setUrl for Ajax loading
41498 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41499 tab3.setUrl("ajax2.htm", null, true);
41500
41501 // Disabled tab
41502 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41503 tab4.disable();
41504
41505 jtabs.activate("jtabs-1");
41506  * </code></pre>
41507  * @constructor
41508  * Create a new TabPanel.
41509  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41510  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41511  */
41512 Roo.bootstrap.panel.Tabs = function(config){
41513     /**
41514     * The container element for this TabPanel.
41515     * @type Roo.Element
41516     */
41517     this.el = Roo.get(config.el);
41518     delete config.el;
41519     if(config){
41520         if(typeof config == "boolean"){
41521             this.tabPosition = config ? "bottom" : "top";
41522         }else{
41523             Roo.apply(this, config);
41524         }
41525     }
41526     
41527     if(this.tabPosition == "bottom"){
41528         // if tabs are at the bottom = create the body first.
41529         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41530         this.el.addClass("roo-tabs-bottom");
41531     }
41532     // next create the tabs holders
41533     
41534     if (this.tabPosition == "west"){
41535         
41536         var reg = this.region; // fake it..
41537         while (reg) {
41538             if (!reg.mgr.parent) {
41539                 break;
41540             }
41541             reg = reg.mgr.parent.region;
41542         }
41543         Roo.log("got nest?");
41544         Roo.log(reg);
41545         if (reg.mgr.getRegion('west')) {
41546             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41547             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41548             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41549             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41550             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41551         
41552             
41553         }
41554         
41555         
41556     } else {
41557      
41558         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41559         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41560         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41561         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41562     }
41563     
41564     
41565     if(Roo.isIE){
41566         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41567     }
41568     
41569     // finally - if tabs are at the top, then create the body last..
41570     if(this.tabPosition != "bottom"){
41571         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41572          * @type Roo.Element
41573          */
41574         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41575         this.el.addClass("roo-tabs-top");
41576     }
41577     this.items = [];
41578
41579     this.bodyEl.setStyle("position", "relative");
41580
41581     this.active = null;
41582     this.activateDelegate = this.activate.createDelegate(this);
41583
41584     this.addEvents({
41585         /**
41586          * @event tabchange
41587          * Fires when the active tab changes
41588          * @param {Roo.TabPanel} this
41589          * @param {Roo.TabPanelItem} activePanel The new active tab
41590          */
41591         "tabchange": true,
41592         /**
41593          * @event beforetabchange
41594          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41595          * @param {Roo.TabPanel} this
41596          * @param {Object} e Set cancel to true on this object to cancel the tab change
41597          * @param {Roo.TabPanelItem} tab The tab being changed to
41598          */
41599         "beforetabchange" : true
41600     });
41601
41602     Roo.EventManager.onWindowResize(this.onResize, this);
41603     this.cpad = this.el.getPadding("lr");
41604     this.hiddenCount = 0;
41605
41606
41607     // toolbar on the tabbar support...
41608     if (this.toolbar) {
41609         alert("no toolbar support yet");
41610         this.toolbar  = false;
41611         /*
41612         var tcfg = this.toolbar;
41613         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41614         this.toolbar = new Roo.Toolbar(tcfg);
41615         if (Roo.isSafari) {
41616             var tbl = tcfg.container.child('table', true);
41617             tbl.setAttribute('width', '100%');
41618         }
41619         */
41620         
41621     }
41622    
41623
41624
41625     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41626 };
41627
41628 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41629     /*
41630      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41631      */
41632     tabPosition : "top",
41633     /*
41634      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41635      */
41636     currentTabWidth : 0,
41637     /*
41638      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41639      */
41640     minTabWidth : 40,
41641     /*
41642      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41643      */
41644     maxTabWidth : 250,
41645     /*
41646      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41647      */
41648     preferredTabWidth : 175,
41649     /*
41650      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41651      */
41652     resizeTabs : false,
41653     /*
41654      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41655      */
41656     monitorResize : true,
41657     /*
41658      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41659      */
41660     toolbar : false,  // set by caller..
41661     
41662     region : false, /// set by caller
41663     
41664     disableTooltips : true, // not used yet...
41665
41666     /**
41667      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41668      * @param {String} id The id of the div to use <b>or create</b>
41669      * @param {String} text The text for the tab
41670      * @param {String} content (optional) Content to put in the TabPanelItem body
41671      * @param {Boolean} closable (optional) True to create a close icon on the tab
41672      * @return {Roo.TabPanelItem} The created TabPanelItem
41673      */
41674     addTab : function(id, text, content, closable, tpl)
41675     {
41676         var item = new Roo.bootstrap.panel.TabItem({
41677             panel: this,
41678             id : id,
41679             text : text,
41680             closable : closable,
41681             tpl : tpl
41682         });
41683         this.addTabItem(item);
41684         if(content){
41685             item.setContent(content);
41686         }
41687         return item;
41688     },
41689
41690     /**
41691      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41692      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41693      * @return {Roo.TabPanelItem}
41694      */
41695     getTab : function(id){
41696         return this.items[id];
41697     },
41698
41699     /**
41700      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41701      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41702      */
41703     hideTab : function(id){
41704         var t = this.items[id];
41705         if(!t.isHidden()){
41706            t.setHidden(true);
41707            this.hiddenCount++;
41708            this.autoSizeTabs();
41709         }
41710     },
41711
41712     /**
41713      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41714      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41715      */
41716     unhideTab : function(id){
41717         var t = this.items[id];
41718         if(t.isHidden()){
41719            t.setHidden(false);
41720            this.hiddenCount--;
41721            this.autoSizeTabs();
41722         }
41723     },
41724
41725     /**
41726      * Adds an existing {@link Roo.TabPanelItem}.
41727      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41728      */
41729     addTabItem : function(item)
41730     {
41731         this.items[item.id] = item;
41732         this.items.push(item);
41733         this.autoSizeTabs();
41734       //  if(this.resizeTabs){
41735     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41736   //         this.autoSizeTabs();
41737 //        }else{
41738 //            item.autoSize();
41739        // }
41740     },
41741
41742     /**
41743      * Removes a {@link Roo.TabPanelItem}.
41744      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41745      */
41746     removeTab : function(id){
41747         var items = this.items;
41748         var tab = items[id];
41749         if(!tab) { return; }
41750         var index = items.indexOf(tab);
41751         if(this.active == tab && items.length > 1){
41752             var newTab = this.getNextAvailable(index);
41753             if(newTab) {
41754                 newTab.activate();
41755             }
41756         }
41757         this.stripEl.dom.removeChild(tab.pnode.dom);
41758         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41759             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41760         }
41761         items.splice(index, 1);
41762         delete this.items[tab.id];
41763         tab.fireEvent("close", tab);
41764         tab.purgeListeners();
41765         this.autoSizeTabs();
41766     },
41767
41768     getNextAvailable : function(start){
41769         var items = this.items;
41770         var index = start;
41771         // look for a next tab that will slide over to
41772         // replace the one being removed
41773         while(index < items.length){
41774             var item = items[++index];
41775             if(item && !item.isHidden()){
41776                 return item;
41777             }
41778         }
41779         // if one isn't found select the previous tab (on the left)
41780         index = start;
41781         while(index >= 0){
41782             var item = items[--index];
41783             if(item && !item.isHidden()){
41784                 return item;
41785             }
41786         }
41787         return null;
41788     },
41789
41790     /**
41791      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41792      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41793      */
41794     disableTab : function(id){
41795         var tab = this.items[id];
41796         if(tab && this.active != tab){
41797             tab.disable();
41798         }
41799     },
41800
41801     /**
41802      * Enables a {@link Roo.TabPanelItem} that is disabled.
41803      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41804      */
41805     enableTab : function(id){
41806         var tab = this.items[id];
41807         tab.enable();
41808     },
41809
41810     /**
41811      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41812      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41813      * @return {Roo.TabPanelItem} The TabPanelItem.
41814      */
41815     activate : function(id)
41816     {
41817         //Roo.log('activite:'  + id);
41818         
41819         var tab = this.items[id];
41820         if(!tab){
41821             return null;
41822         }
41823         if(tab == this.active || tab.disabled){
41824             return tab;
41825         }
41826         var e = {};
41827         this.fireEvent("beforetabchange", this, e, tab);
41828         if(e.cancel !== true && !tab.disabled){
41829             if(this.active){
41830                 this.active.hide();
41831             }
41832             this.active = this.items[id];
41833             this.active.show();
41834             this.fireEvent("tabchange", this, this.active);
41835         }
41836         return tab;
41837     },
41838
41839     /**
41840      * Gets the active {@link Roo.TabPanelItem}.
41841      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41842      */
41843     getActiveTab : function(){
41844         return this.active;
41845     },
41846
41847     /**
41848      * Updates the tab body element to fit the height of the container element
41849      * for overflow scrolling
41850      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41851      */
41852     syncHeight : function(targetHeight){
41853         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41854         var bm = this.bodyEl.getMargins();
41855         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41856         this.bodyEl.setHeight(newHeight);
41857         return newHeight;
41858     },
41859
41860     onResize : function(){
41861         if(this.monitorResize){
41862             this.autoSizeTabs();
41863         }
41864     },
41865
41866     /**
41867      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41868      */
41869     beginUpdate : function(){
41870         this.updating = true;
41871     },
41872
41873     /**
41874      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41875      */
41876     endUpdate : function(){
41877         this.updating = false;
41878         this.autoSizeTabs();
41879     },
41880
41881     /**
41882      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41883      */
41884     autoSizeTabs : function()
41885     {
41886         var count = this.items.length;
41887         var vcount = count - this.hiddenCount;
41888         
41889         if (vcount < 2) {
41890             this.stripEl.hide();
41891         } else {
41892             this.stripEl.show();
41893         }
41894         
41895         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41896             return;
41897         }
41898         
41899         
41900         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41901         var availWidth = Math.floor(w / vcount);
41902         var b = this.stripBody;
41903         if(b.getWidth() > w){
41904             var tabs = this.items;
41905             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41906             if(availWidth < this.minTabWidth){
41907                 /*if(!this.sleft){    // incomplete scrolling code
41908                     this.createScrollButtons();
41909                 }
41910                 this.showScroll();
41911                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41912             }
41913         }else{
41914             if(this.currentTabWidth < this.preferredTabWidth){
41915                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41916             }
41917         }
41918     },
41919
41920     /**
41921      * Returns the number of tabs in this TabPanel.
41922      * @return {Number}
41923      */
41924      getCount : function(){
41925          return this.items.length;
41926      },
41927
41928     /**
41929      * Resizes all the tabs to the passed width
41930      * @param {Number} The new width
41931      */
41932     setTabWidth : function(width){
41933         this.currentTabWidth = width;
41934         for(var i = 0, len = this.items.length; i < len; i++) {
41935                 if(!this.items[i].isHidden()) {
41936                 this.items[i].setWidth(width);
41937             }
41938         }
41939     },
41940
41941     /**
41942      * Destroys this TabPanel
41943      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41944      */
41945     destroy : function(removeEl){
41946         Roo.EventManager.removeResizeListener(this.onResize, this);
41947         for(var i = 0, len = this.items.length; i < len; i++){
41948             this.items[i].purgeListeners();
41949         }
41950         if(removeEl === true){
41951             this.el.update("");
41952             this.el.remove();
41953         }
41954     },
41955     
41956     createStrip : function(container)
41957     {
41958         var strip = document.createElement("nav");
41959         strip.className = Roo.bootstrap.version == 4 ?
41960             "navbar-light bg-light" : 
41961             "navbar navbar-default"; //"x-tabs-wrap";
41962         container.appendChild(strip);
41963         return strip;
41964     },
41965     
41966     createStripList : function(strip)
41967     {
41968         // div wrapper for retard IE
41969         // returns the "tr" element.
41970         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41971         //'<div class="x-tabs-strip-wrap">'+
41972           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41973           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41974         return strip.firstChild; //.firstChild.firstChild.firstChild;
41975     },
41976     createBody : function(container)
41977     {
41978         var body = document.createElement("div");
41979         Roo.id(body, "tab-body");
41980         //Roo.fly(body).addClass("x-tabs-body");
41981         Roo.fly(body).addClass("tab-content");
41982         container.appendChild(body);
41983         return body;
41984     },
41985     createItemBody :function(bodyEl, id){
41986         var body = Roo.getDom(id);
41987         if(!body){
41988             body = document.createElement("div");
41989             body.id = id;
41990         }
41991         //Roo.fly(body).addClass("x-tabs-item-body");
41992         Roo.fly(body).addClass("tab-pane");
41993          bodyEl.insertBefore(body, bodyEl.firstChild);
41994         return body;
41995     },
41996     /** @private */
41997     createStripElements :  function(stripEl, text, closable, tpl)
41998     {
41999         var td = document.createElement("li"); // was td..
42000         td.className = 'nav-item';
42001         
42002         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
42003         
42004         
42005         stripEl.appendChild(td);
42006         /*if(closable){
42007             td.className = "x-tabs-closable";
42008             if(!this.closeTpl){
42009                 this.closeTpl = new Roo.Template(
42010                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42011                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
42012                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
42013                 );
42014             }
42015             var el = this.closeTpl.overwrite(td, {"text": text});
42016             var close = el.getElementsByTagName("div")[0];
42017             var inner = el.getElementsByTagName("em")[0];
42018             return {"el": el, "close": close, "inner": inner};
42019         } else {
42020         */
42021         // not sure what this is..
42022 //            if(!this.tabTpl){
42023                 //this.tabTpl = new Roo.Template(
42024                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
42025                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
42026                 //);
42027 //                this.tabTpl = new Roo.Template(
42028 //                   '<a href="#">' +
42029 //                   '<span unselectable="on"' +
42030 //                            (this.disableTooltips ? '' : ' title="{text}"') +
42031 //                            ' >{text}</span></a>'
42032 //                );
42033 //                
42034 //            }
42035
42036
42037             var template = tpl || this.tabTpl || false;
42038             
42039             if(!template){
42040                 template =  new Roo.Template(
42041                         Roo.bootstrap.version == 4 ? 
42042                             (
42043                                 '<a class="nav-link" href="#" unselectable="on"' +
42044                                      (this.disableTooltips ? '' : ' title="{text}"') +
42045                                      ' >{text}</a>'
42046                             ) : (
42047                                 '<a class="nav-link" href="#">' +
42048                                 '<span unselectable="on"' +
42049                                          (this.disableTooltips ? '' : ' title="{text}"') +
42050                                     ' >{text}</span></a>'
42051                             )
42052                 );
42053             }
42054             
42055             switch (typeof(template)) {
42056                 case 'object' :
42057                     break;
42058                 case 'string' :
42059                     template = new Roo.Template(template);
42060                     break;
42061                 default :
42062                     break;
42063             }
42064             
42065             var el = template.overwrite(td, {"text": text});
42066             
42067             var inner = el.getElementsByTagName("span")[0];
42068             
42069             return {"el": el, "inner": inner};
42070             
42071     }
42072         
42073     
42074 });
42075
42076 /**
42077  * @class Roo.TabPanelItem
42078  * @extends Roo.util.Observable
42079  * Represents an individual item (tab plus body) in a TabPanel.
42080  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42081  * @param {String} id The id of this TabPanelItem
42082  * @param {String} text The text for the tab of this TabPanelItem
42083  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42084  */
42085 Roo.bootstrap.panel.TabItem = function(config){
42086     /**
42087      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42088      * @type Roo.TabPanel
42089      */
42090     this.tabPanel = config.panel;
42091     /**
42092      * The id for this TabPanelItem
42093      * @type String
42094      */
42095     this.id = config.id;
42096     /** @private */
42097     this.disabled = false;
42098     /** @private */
42099     this.text = config.text;
42100     /** @private */
42101     this.loaded = false;
42102     this.closable = config.closable;
42103
42104     /**
42105      * The body element for this TabPanelItem.
42106      * @type Roo.Element
42107      */
42108     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42109     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42110     this.bodyEl.setStyle("display", "block");
42111     this.bodyEl.setStyle("zoom", "1");
42112     //this.hideAction();
42113
42114     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42115     /** @private */
42116     this.el = Roo.get(els.el);
42117     this.inner = Roo.get(els.inner, true);
42118      this.textEl = Roo.bootstrap.version == 4 ?
42119         this.el : Roo.get(this.el.dom.firstChild, true);
42120
42121     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42122     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42123
42124     
42125 //    this.el.on("mousedown", this.onTabMouseDown, this);
42126     this.el.on("click", this.onTabClick, this);
42127     /** @private */
42128     if(config.closable){
42129         var c = Roo.get(els.close, true);
42130         c.dom.title = this.closeText;
42131         c.addClassOnOver("close-over");
42132         c.on("click", this.closeClick, this);
42133      }
42134
42135     this.addEvents({
42136          /**
42137          * @event activate
42138          * Fires when this tab becomes the active tab.
42139          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42140          * @param {Roo.TabPanelItem} this
42141          */
42142         "activate": true,
42143         /**
42144          * @event beforeclose
42145          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42146          * @param {Roo.TabPanelItem} this
42147          * @param {Object} e Set cancel to true on this object to cancel the close.
42148          */
42149         "beforeclose": true,
42150         /**
42151          * @event close
42152          * Fires when this tab is closed.
42153          * @param {Roo.TabPanelItem} this
42154          */
42155          "close": true,
42156         /**
42157          * @event deactivate
42158          * Fires when this tab is no longer the active tab.
42159          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42160          * @param {Roo.TabPanelItem} this
42161          */
42162          "deactivate" : true
42163     });
42164     this.hidden = false;
42165
42166     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42167 };
42168
42169 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42170            {
42171     purgeListeners : function(){
42172        Roo.util.Observable.prototype.purgeListeners.call(this);
42173        this.el.removeAllListeners();
42174     },
42175     /**
42176      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42177      */
42178     show : function(){
42179         this.status_node.addClass("active");
42180         this.showAction();
42181         if(Roo.isOpera){
42182             this.tabPanel.stripWrap.repaint();
42183         }
42184         this.fireEvent("activate", this.tabPanel, this);
42185     },
42186
42187     /**
42188      * Returns true if this tab is the active tab.
42189      * @return {Boolean}
42190      */
42191     isActive : function(){
42192         return this.tabPanel.getActiveTab() == this;
42193     },
42194
42195     /**
42196      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42197      */
42198     hide : function(){
42199         this.status_node.removeClass("active");
42200         this.hideAction();
42201         this.fireEvent("deactivate", this.tabPanel, this);
42202     },
42203
42204     hideAction : function(){
42205         this.bodyEl.hide();
42206         this.bodyEl.setStyle("position", "absolute");
42207         this.bodyEl.setLeft("-20000px");
42208         this.bodyEl.setTop("-20000px");
42209     },
42210
42211     showAction : function(){
42212         this.bodyEl.setStyle("position", "relative");
42213         this.bodyEl.setTop("");
42214         this.bodyEl.setLeft("");
42215         this.bodyEl.show();
42216     },
42217
42218     /**
42219      * Set the tooltip for the tab.
42220      * @param {String} tooltip The tab's tooltip
42221      */
42222     setTooltip : function(text){
42223         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42224             this.textEl.dom.qtip = text;
42225             this.textEl.dom.removeAttribute('title');
42226         }else{
42227             this.textEl.dom.title = text;
42228         }
42229     },
42230
42231     onTabClick : function(e){
42232         e.preventDefault();
42233         this.tabPanel.activate(this.id);
42234     },
42235
42236     onTabMouseDown : function(e){
42237         e.preventDefault();
42238         this.tabPanel.activate(this.id);
42239     },
42240 /*
42241     getWidth : function(){
42242         return this.inner.getWidth();
42243     },
42244
42245     setWidth : function(width){
42246         var iwidth = width - this.linode.getPadding("lr");
42247         this.inner.setWidth(iwidth);
42248         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42249         this.linode.setWidth(width);
42250     },
42251 */
42252     /**
42253      * Show or hide the tab
42254      * @param {Boolean} hidden True to hide or false to show.
42255      */
42256     setHidden : function(hidden){
42257         this.hidden = hidden;
42258         this.linode.setStyle("display", hidden ? "none" : "");
42259     },
42260
42261     /**
42262      * Returns true if this tab is "hidden"
42263      * @return {Boolean}
42264      */
42265     isHidden : function(){
42266         return this.hidden;
42267     },
42268
42269     /**
42270      * Returns the text for this tab
42271      * @return {String}
42272      */
42273     getText : function(){
42274         return this.text;
42275     },
42276     /*
42277     autoSize : function(){
42278         //this.el.beginMeasure();
42279         this.textEl.setWidth(1);
42280         /*
42281          *  #2804 [new] Tabs in Roojs
42282          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42283          */
42284         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42285         //this.el.endMeasure();
42286     //},
42287
42288     /**
42289      * Sets the text for the tab (Note: this also sets the tooltip text)
42290      * @param {String} text The tab's text and tooltip
42291      */
42292     setText : function(text){
42293         this.text = text;
42294         this.textEl.update(text);
42295         this.setTooltip(text);
42296         //if(!this.tabPanel.resizeTabs){
42297         //    this.autoSize();
42298         //}
42299     },
42300     /**
42301      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42302      */
42303     activate : function(){
42304         this.tabPanel.activate(this.id);
42305     },
42306
42307     /**
42308      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42309      */
42310     disable : function(){
42311         if(this.tabPanel.active != this){
42312             this.disabled = true;
42313             this.status_node.addClass("disabled");
42314         }
42315     },
42316
42317     /**
42318      * Enables this TabPanelItem if it was previously disabled.
42319      */
42320     enable : function(){
42321         this.disabled = false;
42322         this.status_node.removeClass("disabled");
42323     },
42324
42325     /**
42326      * Sets the content for this TabPanelItem.
42327      * @param {String} content The content
42328      * @param {Boolean} loadScripts true to look for and load scripts
42329      */
42330     setContent : function(content, loadScripts){
42331         this.bodyEl.update(content, loadScripts);
42332     },
42333
42334     /**
42335      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42336      * @return {Roo.UpdateManager} The UpdateManager
42337      */
42338     getUpdateManager : function(){
42339         return this.bodyEl.getUpdateManager();
42340     },
42341
42342     /**
42343      * Set a URL to be used to load the content for this TabPanelItem.
42344      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42345      * @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)
42346      * @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)
42347      * @return {Roo.UpdateManager} The UpdateManager
42348      */
42349     setUrl : function(url, params, loadOnce){
42350         if(this.refreshDelegate){
42351             this.un('activate', this.refreshDelegate);
42352         }
42353         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42354         this.on("activate", this.refreshDelegate);
42355         return this.bodyEl.getUpdateManager();
42356     },
42357
42358     /** @private */
42359     _handleRefresh : function(url, params, loadOnce){
42360         if(!loadOnce || !this.loaded){
42361             var updater = this.bodyEl.getUpdateManager();
42362             updater.update(url, params, this._setLoaded.createDelegate(this));
42363         }
42364     },
42365
42366     /**
42367      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42368      *   Will fail silently if the setUrl method has not been called.
42369      *   This does not activate the panel, just updates its content.
42370      */
42371     refresh : function(){
42372         if(this.refreshDelegate){
42373            this.loaded = false;
42374            this.refreshDelegate();
42375         }
42376     },
42377
42378     /** @private */
42379     _setLoaded : function(){
42380         this.loaded = true;
42381     },
42382
42383     /** @private */
42384     closeClick : function(e){
42385         var o = {};
42386         e.stopEvent();
42387         this.fireEvent("beforeclose", this, o);
42388         if(o.cancel !== true){
42389             this.tabPanel.removeTab(this.id);
42390         }
42391     },
42392     /**
42393      * The text displayed in the tooltip for the close icon.
42394      * @type String
42395      */
42396     closeText : "Close this tab"
42397 });
42398 /**
42399 *    This script refer to:
42400 *    Title: International Telephone Input
42401 *    Author: Jack O'Connor
42402 *    Code version:  v12.1.12
42403 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42404 **/
42405
42406 Roo.bootstrap.PhoneInputData = function() {
42407     var d = [
42408       [
42409         "Afghanistan (‫افغانستان‬‎)",
42410         "af",
42411         "93"
42412       ],
42413       [
42414         "Albania (Shqipëri)",
42415         "al",
42416         "355"
42417       ],
42418       [
42419         "Algeria (‫الجزائر‬‎)",
42420         "dz",
42421         "213"
42422       ],
42423       [
42424         "American Samoa",
42425         "as",
42426         "1684"
42427       ],
42428       [
42429         "Andorra",
42430         "ad",
42431         "376"
42432       ],
42433       [
42434         "Angola",
42435         "ao",
42436         "244"
42437       ],
42438       [
42439         "Anguilla",
42440         "ai",
42441         "1264"
42442       ],
42443       [
42444         "Antigua and Barbuda",
42445         "ag",
42446         "1268"
42447       ],
42448       [
42449         "Argentina",
42450         "ar",
42451         "54"
42452       ],
42453       [
42454         "Armenia (Հայաստան)",
42455         "am",
42456         "374"
42457       ],
42458       [
42459         "Aruba",
42460         "aw",
42461         "297"
42462       ],
42463       [
42464         "Australia",
42465         "au",
42466         "61",
42467         0
42468       ],
42469       [
42470         "Austria (Österreich)",
42471         "at",
42472         "43"
42473       ],
42474       [
42475         "Azerbaijan (Azərbaycan)",
42476         "az",
42477         "994"
42478       ],
42479       [
42480         "Bahamas",
42481         "bs",
42482         "1242"
42483       ],
42484       [
42485         "Bahrain (‫البحرين‬‎)",
42486         "bh",
42487         "973"
42488       ],
42489       [
42490         "Bangladesh (বাংলাদেশ)",
42491         "bd",
42492         "880"
42493       ],
42494       [
42495         "Barbados",
42496         "bb",
42497         "1246"
42498       ],
42499       [
42500         "Belarus (Беларусь)",
42501         "by",
42502         "375"
42503       ],
42504       [
42505         "Belgium (België)",
42506         "be",
42507         "32"
42508       ],
42509       [
42510         "Belize",
42511         "bz",
42512         "501"
42513       ],
42514       [
42515         "Benin (Bénin)",
42516         "bj",
42517         "229"
42518       ],
42519       [
42520         "Bermuda",
42521         "bm",
42522         "1441"
42523       ],
42524       [
42525         "Bhutan (འབྲུག)",
42526         "bt",
42527         "975"
42528       ],
42529       [
42530         "Bolivia",
42531         "bo",
42532         "591"
42533       ],
42534       [
42535         "Bosnia and Herzegovina (Босна и Херцеговина)",
42536         "ba",
42537         "387"
42538       ],
42539       [
42540         "Botswana",
42541         "bw",
42542         "267"
42543       ],
42544       [
42545         "Brazil (Brasil)",
42546         "br",
42547         "55"
42548       ],
42549       [
42550         "British Indian Ocean Territory",
42551         "io",
42552         "246"
42553       ],
42554       [
42555         "British Virgin Islands",
42556         "vg",
42557         "1284"
42558       ],
42559       [
42560         "Brunei",
42561         "bn",
42562         "673"
42563       ],
42564       [
42565         "Bulgaria (България)",
42566         "bg",
42567         "359"
42568       ],
42569       [
42570         "Burkina Faso",
42571         "bf",
42572         "226"
42573       ],
42574       [
42575         "Burundi (Uburundi)",
42576         "bi",
42577         "257"
42578       ],
42579       [
42580         "Cambodia (កម្ពុជា)",
42581         "kh",
42582         "855"
42583       ],
42584       [
42585         "Cameroon (Cameroun)",
42586         "cm",
42587         "237"
42588       ],
42589       [
42590         "Canada",
42591         "ca",
42592         "1",
42593         1,
42594         ["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"]
42595       ],
42596       [
42597         "Cape Verde (Kabu Verdi)",
42598         "cv",
42599         "238"
42600       ],
42601       [
42602         "Caribbean Netherlands",
42603         "bq",
42604         "599",
42605         1
42606       ],
42607       [
42608         "Cayman Islands",
42609         "ky",
42610         "1345"
42611       ],
42612       [
42613         "Central African Republic (République centrafricaine)",
42614         "cf",
42615         "236"
42616       ],
42617       [
42618         "Chad (Tchad)",
42619         "td",
42620         "235"
42621       ],
42622       [
42623         "Chile",
42624         "cl",
42625         "56"
42626       ],
42627       [
42628         "China (中国)",
42629         "cn",
42630         "86"
42631       ],
42632       [
42633         "Christmas Island",
42634         "cx",
42635         "61",
42636         2
42637       ],
42638       [
42639         "Cocos (Keeling) Islands",
42640         "cc",
42641         "61",
42642         1
42643       ],
42644       [
42645         "Colombia",
42646         "co",
42647         "57"
42648       ],
42649       [
42650         "Comoros (‫جزر القمر‬‎)",
42651         "km",
42652         "269"
42653       ],
42654       [
42655         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42656         "cd",
42657         "243"
42658       ],
42659       [
42660         "Congo (Republic) (Congo-Brazzaville)",
42661         "cg",
42662         "242"
42663       ],
42664       [
42665         "Cook Islands",
42666         "ck",
42667         "682"
42668       ],
42669       [
42670         "Costa Rica",
42671         "cr",
42672         "506"
42673       ],
42674       [
42675         "Côte d’Ivoire",
42676         "ci",
42677         "225"
42678       ],
42679       [
42680         "Croatia (Hrvatska)",
42681         "hr",
42682         "385"
42683       ],
42684       [
42685         "Cuba",
42686         "cu",
42687         "53"
42688       ],
42689       [
42690         "Curaçao",
42691         "cw",
42692         "599",
42693         0
42694       ],
42695       [
42696         "Cyprus (Κύπρος)",
42697         "cy",
42698         "357"
42699       ],
42700       [
42701         "Czech Republic (Česká republika)",
42702         "cz",
42703         "420"
42704       ],
42705       [
42706         "Denmark (Danmark)",
42707         "dk",
42708         "45"
42709       ],
42710       [
42711         "Djibouti",
42712         "dj",
42713         "253"
42714       ],
42715       [
42716         "Dominica",
42717         "dm",
42718         "1767"
42719       ],
42720       [
42721         "Dominican Republic (República Dominicana)",
42722         "do",
42723         "1",
42724         2,
42725         ["809", "829", "849"]
42726       ],
42727       [
42728         "Ecuador",
42729         "ec",
42730         "593"
42731       ],
42732       [
42733         "Egypt (‫مصر‬‎)",
42734         "eg",
42735         "20"
42736       ],
42737       [
42738         "El Salvador",
42739         "sv",
42740         "503"
42741       ],
42742       [
42743         "Equatorial Guinea (Guinea Ecuatorial)",
42744         "gq",
42745         "240"
42746       ],
42747       [
42748         "Eritrea",
42749         "er",
42750         "291"
42751       ],
42752       [
42753         "Estonia (Eesti)",
42754         "ee",
42755         "372"
42756       ],
42757       [
42758         "Ethiopia",
42759         "et",
42760         "251"
42761       ],
42762       [
42763         "Falkland Islands (Islas Malvinas)",
42764         "fk",
42765         "500"
42766       ],
42767       [
42768         "Faroe Islands (Føroyar)",
42769         "fo",
42770         "298"
42771       ],
42772       [
42773         "Fiji",
42774         "fj",
42775         "679"
42776       ],
42777       [
42778         "Finland (Suomi)",
42779         "fi",
42780         "358",
42781         0
42782       ],
42783       [
42784         "France",
42785         "fr",
42786         "33"
42787       ],
42788       [
42789         "French Guiana (Guyane française)",
42790         "gf",
42791         "594"
42792       ],
42793       [
42794         "French Polynesia (Polynésie française)",
42795         "pf",
42796         "689"
42797       ],
42798       [
42799         "Gabon",
42800         "ga",
42801         "241"
42802       ],
42803       [
42804         "Gambia",
42805         "gm",
42806         "220"
42807       ],
42808       [
42809         "Georgia (საქართველო)",
42810         "ge",
42811         "995"
42812       ],
42813       [
42814         "Germany (Deutschland)",
42815         "de",
42816         "49"
42817       ],
42818       [
42819         "Ghana (Gaana)",
42820         "gh",
42821         "233"
42822       ],
42823       [
42824         "Gibraltar",
42825         "gi",
42826         "350"
42827       ],
42828       [
42829         "Greece (Ελλάδα)",
42830         "gr",
42831         "30"
42832       ],
42833       [
42834         "Greenland (Kalaallit Nunaat)",
42835         "gl",
42836         "299"
42837       ],
42838       [
42839         "Grenada",
42840         "gd",
42841         "1473"
42842       ],
42843       [
42844         "Guadeloupe",
42845         "gp",
42846         "590",
42847         0
42848       ],
42849       [
42850         "Guam",
42851         "gu",
42852         "1671"
42853       ],
42854       [
42855         "Guatemala",
42856         "gt",
42857         "502"
42858       ],
42859       [
42860         "Guernsey",
42861         "gg",
42862         "44",
42863         1
42864       ],
42865       [
42866         "Guinea (Guinée)",
42867         "gn",
42868         "224"
42869       ],
42870       [
42871         "Guinea-Bissau (Guiné Bissau)",
42872         "gw",
42873         "245"
42874       ],
42875       [
42876         "Guyana",
42877         "gy",
42878         "592"
42879       ],
42880       [
42881         "Haiti",
42882         "ht",
42883         "509"
42884       ],
42885       [
42886         "Honduras",
42887         "hn",
42888         "504"
42889       ],
42890       [
42891         "Hong Kong (香港)",
42892         "hk",
42893         "852"
42894       ],
42895       [
42896         "Hungary (Magyarország)",
42897         "hu",
42898         "36"
42899       ],
42900       [
42901         "Iceland (Ísland)",
42902         "is",
42903         "354"
42904       ],
42905       [
42906         "India (भारत)",
42907         "in",
42908         "91"
42909       ],
42910       [
42911         "Indonesia",
42912         "id",
42913         "62"
42914       ],
42915       [
42916         "Iran (‫ایران‬‎)",
42917         "ir",
42918         "98"
42919       ],
42920       [
42921         "Iraq (‫العراق‬‎)",
42922         "iq",
42923         "964"
42924       ],
42925       [
42926         "Ireland",
42927         "ie",
42928         "353"
42929       ],
42930       [
42931         "Isle of Man",
42932         "im",
42933         "44",
42934         2
42935       ],
42936       [
42937         "Israel (‫ישראל‬‎)",
42938         "il",
42939         "972"
42940       ],
42941       [
42942         "Italy (Italia)",
42943         "it",
42944         "39",
42945         0
42946       ],
42947       [
42948         "Jamaica",
42949         "jm",
42950         "1876"
42951       ],
42952       [
42953         "Japan (日本)",
42954         "jp",
42955         "81"
42956       ],
42957       [
42958         "Jersey",
42959         "je",
42960         "44",
42961         3
42962       ],
42963       [
42964         "Jordan (‫الأردن‬‎)",
42965         "jo",
42966         "962"
42967       ],
42968       [
42969         "Kazakhstan (Казахстан)",
42970         "kz",
42971         "7",
42972         1
42973       ],
42974       [
42975         "Kenya",
42976         "ke",
42977         "254"
42978       ],
42979       [
42980         "Kiribati",
42981         "ki",
42982         "686"
42983       ],
42984       [
42985         "Kosovo",
42986         "xk",
42987         "383"
42988       ],
42989       [
42990         "Kuwait (‫الكويت‬‎)",
42991         "kw",
42992         "965"
42993       ],
42994       [
42995         "Kyrgyzstan (Кыргызстан)",
42996         "kg",
42997         "996"
42998       ],
42999       [
43000         "Laos (ລາວ)",
43001         "la",
43002         "856"
43003       ],
43004       [
43005         "Latvia (Latvija)",
43006         "lv",
43007         "371"
43008       ],
43009       [
43010         "Lebanon (‫لبنان‬‎)",
43011         "lb",
43012         "961"
43013       ],
43014       [
43015         "Lesotho",
43016         "ls",
43017         "266"
43018       ],
43019       [
43020         "Liberia",
43021         "lr",
43022         "231"
43023       ],
43024       [
43025         "Libya (‫ليبيا‬‎)",
43026         "ly",
43027         "218"
43028       ],
43029       [
43030         "Liechtenstein",
43031         "li",
43032         "423"
43033       ],
43034       [
43035         "Lithuania (Lietuva)",
43036         "lt",
43037         "370"
43038       ],
43039       [
43040         "Luxembourg",
43041         "lu",
43042         "352"
43043       ],
43044       [
43045         "Macau (澳門)",
43046         "mo",
43047         "853"
43048       ],
43049       [
43050         "Macedonia (FYROM) (Македонија)",
43051         "mk",
43052         "389"
43053       ],
43054       [
43055         "Madagascar (Madagasikara)",
43056         "mg",
43057         "261"
43058       ],
43059       [
43060         "Malawi",
43061         "mw",
43062         "265"
43063       ],
43064       [
43065         "Malaysia",
43066         "my",
43067         "60"
43068       ],
43069       [
43070         "Maldives",
43071         "mv",
43072         "960"
43073       ],
43074       [
43075         "Mali",
43076         "ml",
43077         "223"
43078       ],
43079       [
43080         "Malta",
43081         "mt",
43082         "356"
43083       ],
43084       [
43085         "Marshall Islands",
43086         "mh",
43087         "692"
43088       ],
43089       [
43090         "Martinique",
43091         "mq",
43092         "596"
43093       ],
43094       [
43095         "Mauritania (‫موريتانيا‬‎)",
43096         "mr",
43097         "222"
43098       ],
43099       [
43100         "Mauritius (Moris)",
43101         "mu",
43102         "230"
43103       ],
43104       [
43105         "Mayotte",
43106         "yt",
43107         "262",
43108         1
43109       ],
43110       [
43111         "Mexico (México)",
43112         "mx",
43113         "52"
43114       ],
43115       [
43116         "Micronesia",
43117         "fm",
43118         "691"
43119       ],
43120       [
43121         "Moldova (Republica Moldova)",
43122         "md",
43123         "373"
43124       ],
43125       [
43126         "Monaco",
43127         "mc",
43128         "377"
43129       ],
43130       [
43131         "Mongolia (Монгол)",
43132         "mn",
43133         "976"
43134       ],
43135       [
43136         "Montenegro (Crna Gora)",
43137         "me",
43138         "382"
43139       ],
43140       [
43141         "Montserrat",
43142         "ms",
43143         "1664"
43144       ],
43145       [
43146         "Morocco (‫المغرب‬‎)",
43147         "ma",
43148         "212",
43149         0
43150       ],
43151       [
43152         "Mozambique (Moçambique)",
43153         "mz",
43154         "258"
43155       ],
43156       [
43157         "Myanmar (Burma) (မြန်မာ)",
43158         "mm",
43159         "95"
43160       ],
43161       [
43162         "Namibia (Namibië)",
43163         "na",
43164         "264"
43165       ],
43166       [
43167         "Nauru",
43168         "nr",
43169         "674"
43170       ],
43171       [
43172         "Nepal (नेपाल)",
43173         "np",
43174         "977"
43175       ],
43176       [
43177         "Netherlands (Nederland)",
43178         "nl",
43179         "31"
43180       ],
43181       [
43182         "New Caledonia (Nouvelle-Calédonie)",
43183         "nc",
43184         "687"
43185       ],
43186       [
43187         "New Zealand",
43188         "nz",
43189         "64"
43190       ],
43191       [
43192         "Nicaragua",
43193         "ni",
43194         "505"
43195       ],
43196       [
43197         "Niger (Nijar)",
43198         "ne",
43199         "227"
43200       ],
43201       [
43202         "Nigeria",
43203         "ng",
43204         "234"
43205       ],
43206       [
43207         "Niue",
43208         "nu",
43209         "683"
43210       ],
43211       [
43212         "Norfolk Island",
43213         "nf",
43214         "672"
43215       ],
43216       [
43217         "North Korea (조선 민주주의 인민 공화국)",
43218         "kp",
43219         "850"
43220       ],
43221       [
43222         "Northern Mariana Islands",
43223         "mp",
43224         "1670"
43225       ],
43226       [
43227         "Norway (Norge)",
43228         "no",
43229         "47",
43230         0
43231       ],
43232       [
43233         "Oman (‫عُمان‬‎)",
43234         "om",
43235         "968"
43236       ],
43237       [
43238         "Pakistan (‫پاکستان‬‎)",
43239         "pk",
43240         "92"
43241       ],
43242       [
43243         "Palau",
43244         "pw",
43245         "680"
43246       ],
43247       [
43248         "Palestine (‫فلسطين‬‎)",
43249         "ps",
43250         "970"
43251       ],
43252       [
43253         "Panama (Panamá)",
43254         "pa",
43255         "507"
43256       ],
43257       [
43258         "Papua New Guinea",
43259         "pg",
43260         "675"
43261       ],
43262       [
43263         "Paraguay",
43264         "py",
43265         "595"
43266       ],
43267       [
43268         "Peru (Perú)",
43269         "pe",
43270         "51"
43271       ],
43272       [
43273         "Philippines",
43274         "ph",
43275         "63"
43276       ],
43277       [
43278         "Poland (Polska)",
43279         "pl",
43280         "48"
43281       ],
43282       [
43283         "Portugal",
43284         "pt",
43285         "351"
43286       ],
43287       [
43288         "Puerto Rico",
43289         "pr",
43290         "1",
43291         3,
43292         ["787", "939"]
43293       ],
43294       [
43295         "Qatar (‫قطر‬‎)",
43296         "qa",
43297         "974"
43298       ],
43299       [
43300         "Réunion (La Réunion)",
43301         "re",
43302         "262",
43303         0
43304       ],
43305       [
43306         "Romania (România)",
43307         "ro",
43308         "40"
43309       ],
43310       [
43311         "Russia (Россия)",
43312         "ru",
43313         "7",
43314         0
43315       ],
43316       [
43317         "Rwanda",
43318         "rw",
43319         "250"
43320       ],
43321       [
43322         "Saint Barthélemy",
43323         "bl",
43324         "590",
43325         1
43326       ],
43327       [
43328         "Saint Helena",
43329         "sh",
43330         "290"
43331       ],
43332       [
43333         "Saint Kitts and Nevis",
43334         "kn",
43335         "1869"
43336       ],
43337       [
43338         "Saint Lucia",
43339         "lc",
43340         "1758"
43341       ],
43342       [
43343         "Saint Martin (Saint-Martin (partie française))",
43344         "mf",
43345         "590",
43346         2
43347       ],
43348       [
43349         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43350         "pm",
43351         "508"
43352       ],
43353       [
43354         "Saint Vincent and the Grenadines",
43355         "vc",
43356         "1784"
43357       ],
43358       [
43359         "Samoa",
43360         "ws",
43361         "685"
43362       ],
43363       [
43364         "San Marino",
43365         "sm",
43366         "378"
43367       ],
43368       [
43369         "São Tomé and Príncipe (São Tomé e Príncipe)",
43370         "st",
43371         "239"
43372       ],
43373       [
43374         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43375         "sa",
43376         "966"
43377       ],
43378       [
43379         "Senegal (Sénégal)",
43380         "sn",
43381         "221"
43382       ],
43383       [
43384         "Serbia (Србија)",
43385         "rs",
43386         "381"
43387       ],
43388       [
43389         "Seychelles",
43390         "sc",
43391         "248"
43392       ],
43393       [
43394         "Sierra Leone",
43395         "sl",
43396         "232"
43397       ],
43398       [
43399         "Singapore",
43400         "sg",
43401         "65"
43402       ],
43403       [
43404         "Sint Maarten",
43405         "sx",
43406         "1721"
43407       ],
43408       [
43409         "Slovakia (Slovensko)",
43410         "sk",
43411         "421"
43412       ],
43413       [
43414         "Slovenia (Slovenija)",
43415         "si",
43416         "386"
43417       ],
43418       [
43419         "Solomon Islands",
43420         "sb",
43421         "677"
43422       ],
43423       [
43424         "Somalia (Soomaaliya)",
43425         "so",
43426         "252"
43427       ],
43428       [
43429         "South Africa",
43430         "za",
43431         "27"
43432       ],
43433       [
43434         "South Korea (대한민국)",
43435         "kr",
43436         "82"
43437       ],
43438       [
43439         "South Sudan (‫جنوب السودان‬‎)",
43440         "ss",
43441         "211"
43442       ],
43443       [
43444         "Spain (España)",
43445         "es",
43446         "34"
43447       ],
43448       [
43449         "Sri Lanka (ශ්‍රී ලංකාව)",
43450         "lk",
43451         "94"
43452       ],
43453       [
43454         "Sudan (‫السودان‬‎)",
43455         "sd",
43456         "249"
43457       ],
43458       [
43459         "Suriname",
43460         "sr",
43461         "597"
43462       ],
43463       [
43464         "Svalbard and Jan Mayen",
43465         "sj",
43466         "47",
43467         1
43468       ],
43469       [
43470         "Swaziland",
43471         "sz",
43472         "268"
43473       ],
43474       [
43475         "Sweden (Sverige)",
43476         "se",
43477         "46"
43478       ],
43479       [
43480         "Switzerland (Schweiz)",
43481         "ch",
43482         "41"
43483       ],
43484       [
43485         "Syria (‫سوريا‬‎)",
43486         "sy",
43487         "963"
43488       ],
43489       [
43490         "Taiwan (台灣)",
43491         "tw",
43492         "886"
43493       ],
43494       [
43495         "Tajikistan",
43496         "tj",
43497         "992"
43498       ],
43499       [
43500         "Tanzania",
43501         "tz",
43502         "255"
43503       ],
43504       [
43505         "Thailand (ไทย)",
43506         "th",
43507         "66"
43508       ],
43509       [
43510         "Timor-Leste",
43511         "tl",
43512         "670"
43513       ],
43514       [
43515         "Togo",
43516         "tg",
43517         "228"
43518       ],
43519       [
43520         "Tokelau",
43521         "tk",
43522         "690"
43523       ],
43524       [
43525         "Tonga",
43526         "to",
43527         "676"
43528       ],
43529       [
43530         "Trinidad and Tobago",
43531         "tt",
43532         "1868"
43533       ],
43534       [
43535         "Tunisia (‫تونس‬‎)",
43536         "tn",
43537         "216"
43538       ],
43539       [
43540         "Turkey (Türkiye)",
43541         "tr",
43542         "90"
43543       ],
43544       [
43545         "Turkmenistan",
43546         "tm",
43547         "993"
43548       ],
43549       [
43550         "Turks and Caicos Islands",
43551         "tc",
43552         "1649"
43553       ],
43554       [
43555         "Tuvalu",
43556         "tv",
43557         "688"
43558       ],
43559       [
43560         "U.S. Virgin Islands",
43561         "vi",
43562         "1340"
43563       ],
43564       [
43565         "Uganda",
43566         "ug",
43567         "256"
43568       ],
43569       [
43570         "Ukraine (Україна)",
43571         "ua",
43572         "380"
43573       ],
43574       [
43575         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43576         "ae",
43577         "971"
43578       ],
43579       [
43580         "United Kingdom",
43581         "gb",
43582         "44",
43583         0
43584       ],
43585       [
43586         "United States",
43587         "us",
43588         "1",
43589         0
43590       ],
43591       [
43592         "Uruguay",
43593         "uy",
43594         "598"
43595       ],
43596       [
43597         "Uzbekistan (Oʻzbekiston)",
43598         "uz",
43599         "998"
43600       ],
43601       [
43602         "Vanuatu",
43603         "vu",
43604         "678"
43605       ],
43606       [
43607         "Vatican City (Città del Vaticano)",
43608         "va",
43609         "39",
43610         1
43611       ],
43612       [
43613         "Venezuela",
43614         "ve",
43615         "58"
43616       ],
43617       [
43618         "Vietnam (Việt Nam)",
43619         "vn",
43620         "84"
43621       ],
43622       [
43623         "Wallis and Futuna (Wallis-et-Futuna)",
43624         "wf",
43625         "681"
43626       ],
43627       [
43628         "Western Sahara (‫الصحراء الغربية‬‎)",
43629         "eh",
43630         "212",
43631         1
43632       ],
43633       [
43634         "Yemen (‫اليمن‬‎)",
43635         "ye",
43636         "967"
43637       ],
43638       [
43639         "Zambia",
43640         "zm",
43641         "260"
43642       ],
43643       [
43644         "Zimbabwe",
43645         "zw",
43646         "263"
43647       ],
43648       [
43649         "Åland Islands",
43650         "ax",
43651         "358",
43652         1
43653       ]
43654   ];
43655   
43656   return d;
43657 }/**
43658 *    This script refer to:
43659 *    Title: International Telephone Input
43660 *    Author: Jack O'Connor
43661 *    Code version:  v12.1.12
43662 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43663 **/
43664
43665 /**
43666  * @class Roo.bootstrap.PhoneInput
43667  * @extends Roo.bootstrap.TriggerField
43668  * An input with International dial-code selection
43669  
43670  * @cfg {String} defaultDialCode default '+852'
43671  * @cfg {Array} preferedCountries default []
43672   
43673  * @constructor
43674  * Create a new PhoneInput.
43675  * @param {Object} config Configuration options
43676  */
43677
43678 Roo.bootstrap.PhoneInput = function(config) {
43679     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43680 };
43681
43682 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43683         /**
43684         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43685         */
43686         listWidth: undefined,
43687         
43688         selectedClass: 'active',
43689         
43690         invalidClass : "has-warning",
43691         
43692         validClass: 'has-success',
43693         
43694         allowed: '0123456789',
43695         
43696         max_length: 15,
43697         
43698         /**
43699          * @cfg {String} defaultDialCode The default dial code when initializing the input
43700          */
43701         defaultDialCode: '+852',
43702         
43703         /**
43704          * @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
43705          */
43706         preferedCountries: false,
43707         
43708         getAutoCreate : function()
43709         {
43710             var data = Roo.bootstrap.PhoneInputData();
43711             var align = this.labelAlign || this.parentLabelAlign();
43712             var id = Roo.id();
43713             
43714             this.allCountries = [];
43715             this.dialCodeMapping = [];
43716             
43717             for (var i = 0; i < data.length; i++) {
43718               var c = data[i];
43719               this.allCountries[i] = {
43720                 name: c[0],
43721                 iso2: c[1],
43722                 dialCode: c[2],
43723                 priority: c[3] || 0,
43724                 areaCodes: c[4] || null
43725               };
43726               this.dialCodeMapping[c[2]] = {
43727                   name: c[0],
43728                   iso2: c[1],
43729                   priority: c[3] || 0,
43730                   areaCodes: c[4] || null
43731               };
43732             }
43733             
43734             var cfg = {
43735                 cls: 'form-group',
43736                 cn: []
43737             };
43738             
43739             var input =  {
43740                 tag: 'input',
43741                 id : id,
43742                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43743                 maxlength: this.max_length,
43744                 cls : 'form-control tel-input',
43745                 autocomplete: 'new-password'
43746             };
43747             
43748             var hiddenInput = {
43749                 tag: 'input',
43750                 type: 'hidden',
43751                 cls: 'hidden-tel-input'
43752             };
43753             
43754             if (this.name) {
43755                 hiddenInput.name = this.name;
43756             }
43757             
43758             if (this.disabled) {
43759                 input.disabled = true;
43760             }
43761             
43762             var flag_container = {
43763                 tag: 'div',
43764                 cls: 'flag-box',
43765                 cn: [
43766                     {
43767                         tag: 'div',
43768                         cls: 'flag'
43769                     },
43770                     {
43771                         tag: 'div',
43772                         cls: 'caret'
43773                     }
43774                 ]
43775             };
43776             
43777             var box = {
43778                 tag: 'div',
43779                 cls: this.hasFeedback ? 'has-feedback' : '',
43780                 cn: [
43781                     hiddenInput,
43782                     input,
43783                     {
43784                         tag: 'input',
43785                         cls: 'dial-code-holder',
43786                         disabled: true
43787                     }
43788                 ]
43789             };
43790             
43791             var container = {
43792                 cls: 'roo-select2-container input-group',
43793                 cn: [
43794                     flag_container,
43795                     box
43796                 ]
43797             };
43798             
43799             if (this.fieldLabel.length) {
43800                 var indicator = {
43801                     tag: 'i',
43802                     tooltip: 'This field is required'
43803                 };
43804                 
43805                 var label = {
43806                     tag: 'label',
43807                     'for':  id,
43808                     cls: 'control-label',
43809                     cn: []
43810                 };
43811                 
43812                 var label_text = {
43813                     tag: 'span',
43814                     html: this.fieldLabel
43815                 };
43816                 
43817                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43818                 label.cn = [
43819                     indicator,
43820                     label_text
43821                 ];
43822                 
43823                 if(this.indicatorpos == 'right') {
43824                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43825                     label.cn = [
43826                         label_text,
43827                         indicator
43828                     ];
43829                 }
43830                 
43831                 if(align == 'left') {
43832                     container = {
43833                         tag: 'div',
43834                         cn: [
43835                             container
43836                         ]
43837                     };
43838                     
43839                     if(this.labelWidth > 12){
43840                         label.style = "width: " + this.labelWidth + 'px';
43841                     }
43842                     if(this.labelWidth < 13 && this.labelmd == 0){
43843                         this.labelmd = this.labelWidth;
43844                     }
43845                     if(this.labellg > 0){
43846                         label.cls += ' col-lg-' + this.labellg;
43847                         input.cls += ' col-lg-' + (12 - this.labellg);
43848                     }
43849                     if(this.labelmd > 0){
43850                         label.cls += ' col-md-' + this.labelmd;
43851                         container.cls += ' col-md-' + (12 - this.labelmd);
43852                     }
43853                     if(this.labelsm > 0){
43854                         label.cls += ' col-sm-' + this.labelsm;
43855                         container.cls += ' col-sm-' + (12 - this.labelsm);
43856                     }
43857                     if(this.labelxs > 0){
43858                         label.cls += ' col-xs-' + this.labelxs;
43859                         container.cls += ' col-xs-' + (12 - this.labelxs);
43860                     }
43861                 }
43862             }
43863             
43864             cfg.cn = [
43865                 label,
43866                 container
43867             ];
43868             
43869             var settings = this;
43870             
43871             ['xs','sm','md','lg'].map(function(size){
43872                 if (settings[size]) {
43873                     cfg.cls += ' col-' + size + '-' + settings[size];
43874                 }
43875             });
43876             
43877             this.store = new Roo.data.Store({
43878                 proxy : new Roo.data.MemoryProxy({}),
43879                 reader : new Roo.data.JsonReader({
43880                     fields : [
43881                         {
43882                             'name' : 'name',
43883                             'type' : 'string'
43884                         },
43885                         {
43886                             'name' : 'iso2',
43887                             'type' : 'string'
43888                         },
43889                         {
43890                             'name' : 'dialCode',
43891                             'type' : 'string'
43892                         },
43893                         {
43894                             'name' : 'priority',
43895                             'type' : 'string'
43896                         },
43897                         {
43898                             'name' : 'areaCodes',
43899                             'type' : 'string'
43900                         }
43901                     ]
43902                 })
43903             });
43904             
43905             if(!this.preferedCountries) {
43906                 this.preferedCountries = [
43907                     'hk',
43908                     'gb',
43909                     'us'
43910                 ];
43911             }
43912             
43913             var p = this.preferedCountries.reverse();
43914             
43915             if(p) {
43916                 for (var i = 0; i < p.length; i++) {
43917                     for (var j = 0; j < this.allCountries.length; j++) {
43918                         if(this.allCountries[j].iso2 == p[i]) {
43919                             var t = this.allCountries[j];
43920                             this.allCountries.splice(j,1);
43921                             this.allCountries.unshift(t);
43922                         }
43923                     } 
43924                 }
43925             }
43926             
43927             this.store.proxy.data = {
43928                 success: true,
43929                 data: this.allCountries
43930             };
43931             
43932             return cfg;
43933         },
43934         
43935         initEvents : function()
43936         {
43937             this.createList();
43938             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43939             
43940             this.indicator = this.indicatorEl();
43941             this.flag = this.flagEl();
43942             this.dialCodeHolder = this.dialCodeHolderEl();
43943             
43944             this.trigger = this.el.select('div.flag-box',true).first();
43945             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43946             
43947             var _this = this;
43948             
43949             (function(){
43950                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43951                 _this.list.setWidth(lw);
43952             }).defer(100);
43953             
43954             this.list.on('mouseover', this.onViewOver, this);
43955             this.list.on('mousemove', this.onViewMove, this);
43956             this.inputEl().on("keyup", this.onKeyUp, this);
43957             this.inputEl().on("keypress", this.onKeyPress, this);
43958             
43959             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43960
43961             this.view = new Roo.View(this.list, this.tpl, {
43962                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43963             });
43964             
43965             this.view.on('click', this.onViewClick, this);
43966             this.setValue(this.defaultDialCode);
43967         },
43968         
43969         onTriggerClick : function(e)
43970         {
43971             Roo.log('trigger click');
43972             if(this.disabled){
43973                 return;
43974             }
43975             
43976             if(this.isExpanded()){
43977                 this.collapse();
43978                 this.hasFocus = false;
43979             }else {
43980                 this.store.load({});
43981                 this.hasFocus = true;
43982                 this.expand();
43983             }
43984         },
43985         
43986         isExpanded : function()
43987         {
43988             return this.list.isVisible();
43989         },
43990         
43991         collapse : function()
43992         {
43993             if(!this.isExpanded()){
43994                 return;
43995             }
43996             this.list.hide();
43997             Roo.get(document).un('mousedown', this.collapseIf, this);
43998             Roo.get(document).un('mousewheel', this.collapseIf, this);
43999             this.fireEvent('collapse', this);
44000             this.validate();
44001         },
44002         
44003         expand : function()
44004         {
44005             Roo.log('expand');
44006
44007             if(this.isExpanded() || !this.hasFocus){
44008                 return;
44009             }
44010             
44011             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
44012             this.list.setWidth(lw);
44013             
44014             this.list.show();
44015             this.restrictHeight();
44016             
44017             Roo.get(document).on('mousedown', this.collapseIf, this);
44018             Roo.get(document).on('mousewheel', this.collapseIf, this);
44019             
44020             this.fireEvent('expand', this);
44021         },
44022         
44023         restrictHeight : function()
44024         {
44025             this.list.alignTo(this.inputEl(), this.listAlign);
44026             this.list.alignTo(this.inputEl(), this.listAlign);
44027         },
44028         
44029         onViewOver : function(e, t)
44030         {
44031             if(this.inKeyMode){
44032                 return;
44033             }
44034             var item = this.view.findItemFromChild(t);
44035             
44036             if(item){
44037                 var index = this.view.indexOf(item);
44038                 this.select(index, false);
44039             }
44040         },
44041
44042         // private
44043         onViewClick : function(view, doFocus, el, e)
44044         {
44045             var index = this.view.getSelectedIndexes()[0];
44046             
44047             var r = this.store.getAt(index);
44048             
44049             if(r){
44050                 this.onSelect(r, index);
44051             }
44052             if(doFocus !== false && !this.blockFocus){
44053                 this.inputEl().focus();
44054             }
44055         },
44056         
44057         onViewMove : function(e, t)
44058         {
44059             this.inKeyMode = false;
44060         },
44061         
44062         select : function(index, scrollIntoView)
44063         {
44064             this.selectedIndex = index;
44065             this.view.select(index);
44066             if(scrollIntoView !== false){
44067                 var el = this.view.getNode(index);
44068                 if(el){
44069                     this.list.scrollChildIntoView(el, false);
44070                 }
44071             }
44072         },
44073         
44074         createList : function()
44075         {
44076             this.list = Roo.get(document.body).createChild({
44077                 tag: 'ul',
44078                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44079                 style: 'display:none'
44080             });
44081             
44082             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44083         },
44084         
44085         collapseIf : function(e)
44086         {
44087             var in_combo  = e.within(this.el);
44088             var in_list =  e.within(this.list);
44089             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44090             
44091             if (in_combo || in_list || is_list) {
44092                 return;
44093             }
44094             this.collapse();
44095         },
44096         
44097         onSelect : function(record, index)
44098         {
44099             if(this.fireEvent('beforeselect', this, record, index) !== false){
44100                 
44101                 this.setFlagClass(record.data.iso2);
44102                 this.setDialCode(record.data.dialCode);
44103                 this.hasFocus = false;
44104                 this.collapse();
44105                 this.fireEvent('select', this, record, index);
44106             }
44107         },
44108         
44109         flagEl : function()
44110         {
44111             var flag = this.el.select('div.flag',true).first();
44112             if(!flag){
44113                 return false;
44114             }
44115             return flag;
44116         },
44117         
44118         dialCodeHolderEl : function()
44119         {
44120             var d = this.el.select('input.dial-code-holder',true).first();
44121             if(!d){
44122                 return false;
44123             }
44124             return d;
44125         },
44126         
44127         setDialCode : function(v)
44128         {
44129             this.dialCodeHolder.dom.value = '+'+v;
44130         },
44131         
44132         setFlagClass : function(n)
44133         {
44134             this.flag.dom.className = 'flag '+n;
44135         },
44136         
44137         getValue : function()
44138         {
44139             var v = this.inputEl().getValue();
44140             if(this.dialCodeHolder) {
44141                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44142             }
44143             return v;
44144         },
44145         
44146         setValue : function(v)
44147         {
44148             var d = this.getDialCode(v);
44149             
44150             //invalid dial code
44151             if(v.length == 0 || !d || d.length == 0) {
44152                 if(this.rendered){
44153                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44154                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44155                 }
44156                 return;
44157             }
44158             
44159             //valid dial code
44160             this.setFlagClass(this.dialCodeMapping[d].iso2);
44161             this.setDialCode(d);
44162             this.inputEl().dom.value = v.replace('+'+d,'');
44163             this.hiddenEl().dom.value = this.getValue();
44164             
44165             this.validate();
44166         },
44167         
44168         getDialCode : function(v)
44169         {
44170             v = v ||  '';
44171             
44172             if (v.length == 0) {
44173                 return this.dialCodeHolder.dom.value;
44174             }
44175             
44176             var dialCode = "";
44177             if (v.charAt(0) != "+") {
44178                 return false;
44179             }
44180             var numericChars = "";
44181             for (var i = 1; i < v.length; i++) {
44182               var c = v.charAt(i);
44183               if (!isNaN(c)) {
44184                 numericChars += c;
44185                 if (this.dialCodeMapping[numericChars]) {
44186                   dialCode = v.substr(1, i);
44187                 }
44188                 if (numericChars.length == 4) {
44189                   break;
44190                 }
44191               }
44192             }
44193             return dialCode;
44194         },
44195         
44196         reset : function()
44197         {
44198             this.setValue(this.defaultDialCode);
44199             this.validate();
44200         },
44201         
44202         hiddenEl : function()
44203         {
44204             return this.el.select('input.hidden-tel-input',true).first();
44205         },
44206         
44207         // after setting val
44208         onKeyUp : function(e){
44209             this.setValue(this.getValue());
44210         },
44211         
44212         onKeyPress : function(e){
44213             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44214                 e.stopEvent();
44215             }
44216         }
44217         
44218 });
44219 /**
44220  * @class Roo.bootstrap.MoneyField
44221  * @extends Roo.bootstrap.ComboBox
44222  * Bootstrap MoneyField class
44223  * 
44224  * @constructor
44225  * Create a new MoneyField.
44226  * @param {Object} config Configuration options
44227  */
44228
44229 Roo.bootstrap.MoneyField = function(config) {
44230     
44231     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44232     
44233 };
44234
44235 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44236     
44237     /**
44238      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44239      */
44240     allowDecimals : true,
44241     /**
44242      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44243      */
44244     decimalSeparator : ".",
44245     /**
44246      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44247      */
44248     decimalPrecision : 0,
44249     /**
44250      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44251      */
44252     allowNegative : true,
44253     /**
44254      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44255      */
44256     allowZero: true,
44257     /**
44258      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44259      */
44260     minValue : Number.NEGATIVE_INFINITY,
44261     /**
44262      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44263      */
44264     maxValue : Number.MAX_VALUE,
44265     /**
44266      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44267      */
44268     minText : "The minimum value for this field is {0}",
44269     /**
44270      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44271      */
44272     maxText : "The maximum value for this field is {0}",
44273     /**
44274      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44275      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44276      */
44277     nanText : "{0} is not a valid number",
44278     /**
44279      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44280      */
44281     castInt : true,
44282     /**
44283      * @cfg {String} defaults currency of the MoneyField
44284      * value should be in lkey
44285      */
44286     defaultCurrency : false,
44287     /**
44288      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44289      */
44290     thousandsDelimiter : false,
44291     /**
44292      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44293      */
44294     max_length: false,
44295     
44296     inputlg : 9,
44297     inputmd : 9,
44298     inputsm : 9,
44299     inputxs : 6,
44300     
44301     store : false,
44302     
44303     getAutoCreate : function()
44304     {
44305         var align = this.labelAlign || this.parentLabelAlign();
44306         
44307         var id = Roo.id();
44308
44309         var cfg = {
44310             cls: 'form-group',
44311             cn: []
44312         };
44313
44314         var input =  {
44315             tag: 'input',
44316             id : id,
44317             cls : 'form-control roo-money-amount-input',
44318             autocomplete: 'new-password'
44319         };
44320         
44321         var hiddenInput = {
44322             tag: 'input',
44323             type: 'hidden',
44324             id: Roo.id(),
44325             cls: 'hidden-number-input'
44326         };
44327         
44328         if(this.max_length) {
44329             input.maxlength = this.max_length; 
44330         }
44331         
44332         if (this.name) {
44333             hiddenInput.name = this.name;
44334         }
44335
44336         if (this.disabled) {
44337             input.disabled = true;
44338         }
44339
44340         var clg = 12 - this.inputlg;
44341         var cmd = 12 - this.inputmd;
44342         var csm = 12 - this.inputsm;
44343         var cxs = 12 - this.inputxs;
44344         
44345         var container = {
44346             tag : 'div',
44347             cls : 'row roo-money-field',
44348             cn : [
44349                 {
44350                     tag : 'div',
44351                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44352                     cn : [
44353                         {
44354                             tag : 'div',
44355                             cls: 'roo-select2-container input-group',
44356                             cn: [
44357                                 {
44358                                     tag : 'input',
44359                                     cls : 'form-control roo-money-currency-input',
44360                                     autocomplete: 'new-password',
44361                                     readOnly : 1,
44362                                     name : this.currencyName
44363                                 },
44364                                 {
44365                                     tag :'span',
44366                                     cls : 'input-group-addon',
44367                                     cn : [
44368                                         {
44369                                             tag: 'span',
44370                                             cls: 'caret'
44371                                         }
44372                                     ]
44373                                 }
44374                             ]
44375                         }
44376                     ]
44377                 },
44378                 {
44379                     tag : 'div',
44380                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44381                     cn : [
44382                         {
44383                             tag: 'div',
44384                             cls: this.hasFeedback ? 'has-feedback' : '',
44385                             cn: [
44386                                 input
44387                             ]
44388                         }
44389                     ]
44390                 }
44391             ]
44392             
44393         };
44394         
44395         if (this.fieldLabel.length) {
44396             var indicator = {
44397                 tag: 'i',
44398                 tooltip: 'This field is required'
44399             };
44400
44401             var label = {
44402                 tag: 'label',
44403                 'for':  id,
44404                 cls: 'control-label',
44405                 cn: []
44406             };
44407
44408             var label_text = {
44409                 tag: 'span',
44410                 html: this.fieldLabel
44411             };
44412
44413             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44414             label.cn = [
44415                 indicator,
44416                 label_text
44417             ];
44418
44419             if(this.indicatorpos == 'right') {
44420                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44421                 label.cn = [
44422                     label_text,
44423                     indicator
44424                 ];
44425             }
44426
44427             if(align == 'left') {
44428                 container = {
44429                     tag: 'div',
44430                     cn: [
44431                         container
44432                     ]
44433                 };
44434
44435                 if(this.labelWidth > 12){
44436                     label.style = "width: " + this.labelWidth + 'px';
44437                 }
44438                 if(this.labelWidth < 13 && this.labelmd == 0){
44439                     this.labelmd = this.labelWidth;
44440                 }
44441                 if(this.labellg > 0){
44442                     label.cls += ' col-lg-' + this.labellg;
44443                     input.cls += ' col-lg-' + (12 - this.labellg);
44444                 }
44445                 if(this.labelmd > 0){
44446                     label.cls += ' col-md-' + this.labelmd;
44447                     container.cls += ' col-md-' + (12 - this.labelmd);
44448                 }
44449                 if(this.labelsm > 0){
44450                     label.cls += ' col-sm-' + this.labelsm;
44451                     container.cls += ' col-sm-' + (12 - this.labelsm);
44452                 }
44453                 if(this.labelxs > 0){
44454                     label.cls += ' col-xs-' + this.labelxs;
44455                     container.cls += ' col-xs-' + (12 - this.labelxs);
44456                 }
44457             }
44458         }
44459
44460         cfg.cn = [
44461             label,
44462             container,
44463             hiddenInput
44464         ];
44465         
44466         var settings = this;
44467
44468         ['xs','sm','md','lg'].map(function(size){
44469             if (settings[size]) {
44470                 cfg.cls += ' col-' + size + '-' + settings[size];
44471             }
44472         });
44473         
44474         return cfg;
44475     },
44476     
44477     initEvents : function()
44478     {
44479         this.indicator = this.indicatorEl();
44480         
44481         this.initCurrencyEvent();
44482         
44483         this.initNumberEvent();
44484     },
44485     
44486     initCurrencyEvent : function()
44487     {
44488         if (!this.store) {
44489             throw "can not find store for combo";
44490         }
44491         
44492         this.store = Roo.factory(this.store, Roo.data);
44493         this.store.parent = this;
44494         
44495         this.createList();
44496         
44497         this.triggerEl = this.el.select('.input-group-addon', true).first();
44498         
44499         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44500         
44501         var _this = this;
44502         
44503         (function(){
44504             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44505             _this.list.setWidth(lw);
44506         }).defer(100);
44507         
44508         this.list.on('mouseover', this.onViewOver, this);
44509         this.list.on('mousemove', this.onViewMove, this);
44510         this.list.on('scroll', this.onViewScroll, this);
44511         
44512         if(!this.tpl){
44513             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44514         }
44515         
44516         this.view = new Roo.View(this.list, this.tpl, {
44517             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44518         });
44519         
44520         this.view.on('click', this.onViewClick, this);
44521         
44522         this.store.on('beforeload', this.onBeforeLoad, this);
44523         this.store.on('load', this.onLoad, this);
44524         this.store.on('loadexception', this.onLoadException, this);
44525         
44526         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44527             "up" : function(e){
44528                 this.inKeyMode = true;
44529                 this.selectPrev();
44530             },
44531
44532             "down" : function(e){
44533                 if(!this.isExpanded()){
44534                     this.onTriggerClick();
44535                 }else{
44536                     this.inKeyMode = true;
44537                     this.selectNext();
44538                 }
44539             },
44540
44541             "enter" : function(e){
44542                 this.collapse();
44543                 
44544                 if(this.fireEvent("specialkey", this, e)){
44545                     this.onViewClick(false);
44546                 }
44547                 
44548                 return true;
44549             },
44550
44551             "esc" : function(e){
44552                 this.collapse();
44553             },
44554
44555             "tab" : function(e){
44556                 this.collapse();
44557                 
44558                 if(this.fireEvent("specialkey", this, e)){
44559                     this.onViewClick(false);
44560                 }
44561                 
44562                 return true;
44563             },
44564
44565             scope : this,
44566
44567             doRelay : function(foo, bar, hname){
44568                 if(hname == 'down' || this.scope.isExpanded()){
44569                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44570                 }
44571                 return true;
44572             },
44573
44574             forceKeyDown: true
44575         });
44576         
44577         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44578         
44579     },
44580     
44581     initNumberEvent : function(e)
44582     {
44583         this.inputEl().on("keydown" , this.fireKey,  this);
44584         this.inputEl().on("focus", this.onFocus,  this);
44585         this.inputEl().on("blur", this.onBlur,  this);
44586         
44587         this.inputEl().relayEvent('keyup', this);
44588         
44589         if(this.indicator){
44590             this.indicator.addClass('invisible');
44591         }
44592  
44593         this.originalValue = this.getValue();
44594         
44595         if(this.validationEvent == 'keyup'){
44596             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44597             this.inputEl().on('keyup', this.filterValidation, this);
44598         }
44599         else if(this.validationEvent !== false){
44600             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44601         }
44602         
44603         if(this.selectOnFocus){
44604             this.on("focus", this.preFocus, this);
44605             
44606         }
44607         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44608             this.inputEl().on("keypress", this.filterKeys, this);
44609         } else {
44610             this.inputEl().relayEvent('keypress', this);
44611         }
44612         
44613         var allowed = "0123456789";
44614         
44615         if(this.allowDecimals){
44616             allowed += this.decimalSeparator;
44617         }
44618         
44619         if(this.allowNegative){
44620             allowed += "-";
44621         }
44622         
44623         if(this.thousandsDelimiter) {
44624             allowed += ",";
44625         }
44626         
44627         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44628         
44629         var keyPress = function(e){
44630             
44631             var k = e.getKey();
44632             
44633             var c = e.getCharCode();
44634             
44635             if(
44636                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44637                     allowed.indexOf(String.fromCharCode(c)) === -1
44638             ){
44639                 e.stopEvent();
44640                 return;
44641             }
44642             
44643             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44644                 return;
44645             }
44646             
44647             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44648                 e.stopEvent();
44649             }
44650         };
44651         
44652         this.inputEl().on("keypress", keyPress, this);
44653         
44654     },
44655     
44656     onTriggerClick : function(e)
44657     {   
44658         if(this.disabled){
44659             return;
44660         }
44661         
44662         this.page = 0;
44663         this.loadNext = false;
44664         
44665         if(this.isExpanded()){
44666             this.collapse();
44667             return;
44668         }
44669         
44670         this.hasFocus = true;
44671         
44672         if(this.triggerAction == 'all') {
44673             this.doQuery(this.allQuery, true);
44674             return;
44675         }
44676         
44677         this.doQuery(this.getRawValue());
44678     },
44679     
44680     getCurrency : function()
44681     {   
44682         var v = this.currencyEl().getValue();
44683         
44684         return v;
44685     },
44686     
44687     restrictHeight : function()
44688     {
44689         this.list.alignTo(this.currencyEl(), this.listAlign);
44690         this.list.alignTo(this.currencyEl(), this.listAlign);
44691     },
44692     
44693     onViewClick : function(view, doFocus, el, e)
44694     {
44695         var index = this.view.getSelectedIndexes()[0];
44696         
44697         var r = this.store.getAt(index);
44698         
44699         if(r){
44700             this.onSelect(r, index);
44701         }
44702     },
44703     
44704     onSelect : function(record, index){
44705         
44706         if(this.fireEvent('beforeselect', this, record, index) !== false){
44707         
44708             this.setFromCurrencyData(index > -1 ? record.data : false);
44709             
44710             this.collapse();
44711             
44712             this.fireEvent('select', this, record, index);
44713         }
44714     },
44715     
44716     setFromCurrencyData : function(o)
44717     {
44718         var currency = '';
44719         
44720         this.lastCurrency = o;
44721         
44722         if (this.currencyField) {
44723             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44724         } else {
44725             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44726         }
44727         
44728         this.lastSelectionText = currency;
44729         
44730         //setting default currency
44731         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44732             this.setCurrency(this.defaultCurrency);
44733             return;
44734         }
44735         
44736         this.setCurrency(currency);
44737     },
44738     
44739     setFromData : function(o)
44740     {
44741         var c = {};
44742         
44743         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44744         
44745         this.setFromCurrencyData(c);
44746         
44747         var value = '';
44748         
44749         if (this.name) {
44750             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44751         } else {
44752             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44753         }
44754         
44755         this.setValue(value);
44756         
44757     },
44758     
44759     setCurrency : function(v)
44760     {   
44761         this.currencyValue = v;
44762         
44763         if(this.rendered){
44764             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44765             this.validate();
44766         }
44767     },
44768     
44769     setValue : function(v)
44770     {
44771         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44772         
44773         this.value = v;
44774         
44775         if(this.rendered){
44776             
44777             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44778             
44779             this.inputEl().dom.value = (v == '') ? '' :
44780                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44781             
44782             if(!this.allowZero && v === '0') {
44783                 this.hiddenEl().dom.value = '';
44784                 this.inputEl().dom.value = '';
44785             }
44786             
44787             this.validate();
44788         }
44789     },
44790     
44791     getRawValue : function()
44792     {
44793         var v = this.inputEl().getValue();
44794         
44795         return v;
44796     },
44797     
44798     getValue : function()
44799     {
44800         return this.fixPrecision(this.parseValue(this.getRawValue()));
44801     },
44802     
44803     parseValue : function(value)
44804     {
44805         if(this.thousandsDelimiter) {
44806             value += "";
44807             r = new RegExp(",", "g");
44808             value = value.replace(r, "");
44809         }
44810         
44811         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44812         return isNaN(value) ? '' : value;
44813         
44814     },
44815     
44816     fixPrecision : function(value)
44817     {
44818         if(this.thousandsDelimiter) {
44819             value += "";
44820             r = new RegExp(",", "g");
44821             value = value.replace(r, "");
44822         }
44823         
44824         var nan = isNaN(value);
44825         
44826         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44827             return nan ? '' : value;
44828         }
44829         return parseFloat(value).toFixed(this.decimalPrecision);
44830     },
44831     
44832     decimalPrecisionFcn : function(v)
44833     {
44834         return Math.floor(v);
44835     },
44836     
44837     validateValue : function(value)
44838     {
44839         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44840             return false;
44841         }
44842         
44843         var num = this.parseValue(value);
44844         
44845         if(isNaN(num)){
44846             this.markInvalid(String.format(this.nanText, value));
44847             return false;
44848         }
44849         
44850         if(num < this.minValue){
44851             this.markInvalid(String.format(this.minText, this.minValue));
44852             return false;
44853         }
44854         
44855         if(num > this.maxValue){
44856             this.markInvalid(String.format(this.maxText, this.maxValue));
44857             return false;
44858         }
44859         
44860         return true;
44861     },
44862     
44863     validate : function()
44864     {
44865         if(this.disabled || this.allowBlank){
44866             this.markValid();
44867             return true;
44868         }
44869         
44870         var currency = this.getCurrency();
44871         
44872         if(this.validateValue(this.getRawValue()) && currency.length){
44873             this.markValid();
44874             return true;
44875         }
44876         
44877         this.markInvalid();
44878         return false;
44879     },
44880     
44881     getName: function()
44882     {
44883         return this.name;
44884     },
44885     
44886     beforeBlur : function()
44887     {
44888         if(!this.castInt){
44889             return;
44890         }
44891         
44892         var v = this.parseValue(this.getRawValue());
44893         
44894         if(v || v == 0){
44895             this.setValue(v);
44896         }
44897     },
44898     
44899     onBlur : function()
44900     {
44901         this.beforeBlur();
44902         
44903         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44904             //this.el.removeClass(this.focusClass);
44905         }
44906         
44907         this.hasFocus = false;
44908         
44909         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44910             this.validate();
44911         }
44912         
44913         var v = this.getValue();
44914         
44915         if(String(v) !== String(this.startValue)){
44916             this.fireEvent('change', this, v, this.startValue);
44917         }
44918         
44919         this.fireEvent("blur", this);
44920     },
44921     
44922     inputEl : function()
44923     {
44924         return this.el.select('.roo-money-amount-input', true).first();
44925     },
44926     
44927     currencyEl : function()
44928     {
44929         return this.el.select('.roo-money-currency-input', true).first();
44930     },
44931     
44932     hiddenEl : function()
44933     {
44934         return this.el.select('input.hidden-number-input',true).first();
44935     }
44936     
44937 });/**
44938  * @class Roo.bootstrap.BezierSignature
44939  * @extends Roo.bootstrap.Component
44940  * Bootstrap BezierSignature class
44941  * This script refer to:
44942  *    Title: Signature Pad
44943  *    Author: szimek
44944  *    Availability: https://github.com/szimek/signature_pad
44945  *
44946  * @constructor
44947  * Create a new BezierSignature
44948  * @param {Object} config The config object
44949  */
44950
44951 Roo.bootstrap.BezierSignature = function(config){
44952     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44953     this.addEvents({
44954         "resize" : true
44955     });
44956 };
44957
44958 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44959 {
44960      
44961     curve_data: [],
44962     
44963     is_empty: true,
44964     
44965     mouse_btn_down: true,
44966     
44967     /**
44968      * @cfg {int} canvas height
44969      */
44970     canvas_height: '200px',
44971     
44972     /**
44973      * @cfg {float|function} Radius of a single dot.
44974      */ 
44975     dot_size: false,
44976     
44977     /**
44978      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44979      */
44980     min_width: 0.5,
44981     
44982     /**
44983      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44984      */
44985     max_width: 2.5,
44986     
44987     /**
44988      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44989      */
44990     throttle: 16,
44991     
44992     /**
44993      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44994      */
44995     min_distance: 5,
44996     
44997     /**
44998      * @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.
44999      */
45000     bg_color: 'rgba(0, 0, 0, 0)',
45001     
45002     /**
45003      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
45004      */
45005     dot_color: 'black',
45006     
45007     /**
45008      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
45009      */ 
45010     velocity_filter_weight: 0.7,
45011     
45012     /**
45013      * @cfg {function} Callback when stroke begin. 
45014      */
45015     onBegin: false,
45016     
45017     /**
45018      * @cfg {function} Callback when stroke end.
45019      */
45020     onEnd: false,
45021     
45022     getAutoCreate : function()
45023     {
45024         var cls = 'roo-signature column';
45025         
45026         if(this.cls){
45027             cls += ' ' + this.cls;
45028         }
45029         
45030         var col_sizes = [
45031             'lg',
45032             'md',
45033             'sm',
45034             'xs'
45035         ];
45036         
45037         for(var i = 0; i < col_sizes.length; i++) {
45038             if(this[col_sizes[i]]) {
45039                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45040             }
45041         }
45042         
45043         var cfg = {
45044             tag: 'div',
45045             cls: cls,
45046             cn: [
45047                 {
45048                     tag: 'div',
45049                     cls: 'roo-signature-body',
45050                     cn: [
45051                         {
45052                             tag: 'canvas',
45053                             cls: 'roo-signature-body-canvas',
45054                             height: this.canvas_height,
45055                             width: this.canvas_width
45056                         }
45057                     ]
45058                 },
45059                 {
45060                     tag: 'input',
45061                     type: 'file',
45062                     style: 'display: none'
45063                 }
45064             ]
45065         };
45066         
45067         return cfg;
45068     },
45069     
45070     initEvents: function() 
45071     {
45072         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45073         
45074         var canvas = this.canvasEl();
45075         
45076         // mouse && touch event swapping...
45077         canvas.dom.style.touchAction = 'none';
45078         canvas.dom.style.msTouchAction = 'none';
45079         
45080         this.mouse_btn_down = false;
45081         canvas.on('mousedown', this._handleMouseDown, this);
45082         canvas.on('mousemove', this._handleMouseMove, this);
45083         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45084         
45085         if (window.PointerEvent) {
45086             canvas.on('pointerdown', this._handleMouseDown, this);
45087             canvas.on('pointermove', this._handleMouseMove, this);
45088             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45089         }
45090         
45091         if ('ontouchstart' in window) {
45092             canvas.on('touchstart', this._handleTouchStart, this);
45093             canvas.on('touchmove', this._handleTouchMove, this);
45094             canvas.on('touchend', this._handleTouchEnd, this);
45095         }
45096         
45097         Roo.EventManager.onWindowResize(this.resize, this, true);
45098         
45099         // file input event
45100         this.fileEl().on('change', this.uploadImage, this);
45101         
45102         this.clear();
45103         
45104         this.resize();
45105     },
45106     
45107     resize: function(){
45108         
45109         var canvas = this.canvasEl().dom;
45110         var ctx = this.canvasElCtx();
45111         var img_data = false;
45112         
45113         if(canvas.width > 0) {
45114             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45115         }
45116         // setting canvas width will clean img data
45117         canvas.width = 0;
45118         
45119         var style = window.getComputedStyle ? 
45120             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45121             
45122         var padding_left = parseInt(style.paddingLeft) || 0;
45123         var padding_right = parseInt(style.paddingRight) || 0;
45124         
45125         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45126         
45127         if(img_data) {
45128             ctx.putImageData(img_data, 0, 0);
45129         }
45130     },
45131     
45132     _handleMouseDown: function(e)
45133     {
45134         if (e.browserEvent.which === 1) {
45135             this.mouse_btn_down = true;
45136             this.strokeBegin(e);
45137         }
45138     },
45139     
45140     _handleMouseMove: function (e)
45141     {
45142         if (this.mouse_btn_down) {
45143             this.strokeMoveUpdate(e);
45144         }
45145     },
45146     
45147     _handleMouseUp: function (e)
45148     {
45149         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45150             this.mouse_btn_down = false;
45151             this.strokeEnd(e);
45152         }
45153     },
45154     
45155     _handleTouchStart: function (e) {
45156         
45157         e.preventDefault();
45158         if (e.browserEvent.targetTouches.length === 1) {
45159             // var touch = e.browserEvent.changedTouches[0];
45160             // this.strokeBegin(touch);
45161             
45162              this.strokeBegin(e); // assume e catching the correct xy...
45163         }
45164     },
45165     
45166     _handleTouchMove: function (e) {
45167         e.preventDefault();
45168         // var touch = event.targetTouches[0];
45169         // _this._strokeMoveUpdate(touch);
45170         this.strokeMoveUpdate(e);
45171     },
45172     
45173     _handleTouchEnd: function (e) {
45174         var wasCanvasTouched = e.target === this.canvasEl().dom;
45175         if (wasCanvasTouched) {
45176             e.preventDefault();
45177             // var touch = event.changedTouches[0];
45178             // _this._strokeEnd(touch);
45179             this.strokeEnd(e);
45180         }
45181     },
45182     
45183     reset: function () {
45184         this._lastPoints = [];
45185         this._lastVelocity = 0;
45186         this._lastWidth = (this.min_width + this.max_width) / 2;
45187         this.canvasElCtx().fillStyle = this.dot_color;
45188     },
45189     
45190     strokeMoveUpdate: function(e)
45191     {
45192         this.strokeUpdate(e);
45193         
45194         if (this.throttle) {
45195             this.throttleStroke(this.strokeUpdate, this.throttle);
45196         }
45197         else {
45198             this.strokeUpdate(e);
45199         }
45200     },
45201     
45202     strokeBegin: function(e)
45203     {
45204         var newPointGroup = {
45205             color: this.dot_color,
45206             points: []
45207         };
45208         
45209         if (typeof this.onBegin === 'function') {
45210             this.onBegin(e);
45211         }
45212         
45213         this.curve_data.push(newPointGroup);
45214         this.reset();
45215         this.strokeUpdate(e);
45216     },
45217     
45218     strokeUpdate: function(e)
45219     {
45220         var rect = this.canvasEl().dom.getBoundingClientRect();
45221         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45222         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45223         var lastPoints = lastPointGroup.points;
45224         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45225         var isLastPointTooClose = lastPoint
45226             ? point.distanceTo(lastPoint) <= this.min_distance
45227             : false;
45228         var color = lastPointGroup.color;
45229         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45230             var curve = this.addPoint(point);
45231             if (!lastPoint) {
45232                 this.drawDot({color: color, point: point});
45233             }
45234             else if (curve) {
45235                 this.drawCurve({color: color, curve: curve});
45236             }
45237             lastPoints.push({
45238                 time: point.time,
45239                 x: point.x,
45240                 y: point.y
45241             });
45242         }
45243     },
45244     
45245     strokeEnd: function(e)
45246     {
45247         this.strokeUpdate(e);
45248         if (typeof this.onEnd === 'function') {
45249             this.onEnd(e);
45250         }
45251     },
45252     
45253     addPoint:  function (point) {
45254         var _lastPoints = this._lastPoints;
45255         _lastPoints.push(point);
45256         if (_lastPoints.length > 2) {
45257             if (_lastPoints.length === 3) {
45258                 _lastPoints.unshift(_lastPoints[0]);
45259             }
45260             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45261             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45262             _lastPoints.shift();
45263             return curve;
45264         }
45265         return null;
45266     },
45267     
45268     calculateCurveWidths: function (startPoint, endPoint) {
45269         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45270             (1 - this.velocity_filter_weight) * this._lastVelocity;
45271
45272         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45273         var widths = {
45274             end: newWidth,
45275             start: this._lastWidth
45276         };
45277         
45278         this._lastVelocity = velocity;
45279         this._lastWidth = newWidth;
45280         return widths;
45281     },
45282     
45283     drawDot: function (_a) {
45284         var color = _a.color, point = _a.point;
45285         var ctx = this.canvasElCtx();
45286         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45287         ctx.beginPath();
45288         this.drawCurveSegment(point.x, point.y, width);
45289         ctx.closePath();
45290         ctx.fillStyle = color;
45291         ctx.fill();
45292     },
45293     
45294     drawCurve: function (_a) {
45295         var color = _a.color, curve = _a.curve;
45296         var ctx = this.canvasElCtx();
45297         var widthDelta = curve.endWidth - curve.startWidth;
45298         var drawSteps = Math.floor(curve.length()) * 2;
45299         ctx.beginPath();
45300         ctx.fillStyle = color;
45301         for (var i = 0; i < drawSteps; i += 1) {
45302         var t = i / drawSteps;
45303         var tt = t * t;
45304         var ttt = tt * t;
45305         var u = 1 - t;
45306         var uu = u * u;
45307         var uuu = uu * u;
45308         var x = uuu * curve.startPoint.x;
45309         x += 3 * uu * t * curve.control1.x;
45310         x += 3 * u * tt * curve.control2.x;
45311         x += ttt * curve.endPoint.x;
45312         var y = uuu * curve.startPoint.y;
45313         y += 3 * uu * t * curve.control1.y;
45314         y += 3 * u * tt * curve.control2.y;
45315         y += ttt * curve.endPoint.y;
45316         var width = curve.startWidth + ttt * widthDelta;
45317         this.drawCurveSegment(x, y, width);
45318         }
45319         ctx.closePath();
45320         ctx.fill();
45321     },
45322     
45323     drawCurveSegment: function (x, y, width) {
45324         var ctx = this.canvasElCtx();
45325         ctx.moveTo(x, y);
45326         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45327         this.is_empty = false;
45328     },
45329     
45330     clear: function()
45331     {
45332         var ctx = this.canvasElCtx();
45333         var canvas = this.canvasEl().dom;
45334         ctx.fillStyle = this.bg_color;
45335         ctx.clearRect(0, 0, canvas.width, canvas.height);
45336         ctx.fillRect(0, 0, canvas.width, canvas.height);
45337         this.curve_data = [];
45338         this.reset();
45339         this.is_empty = true;
45340     },
45341     
45342     fileEl: function()
45343     {
45344         return  this.el.select('input',true).first();
45345     },
45346     
45347     canvasEl: function()
45348     {
45349         return this.el.select('canvas',true).first();
45350     },
45351     
45352     canvasElCtx: function()
45353     {
45354         return this.el.select('canvas',true).first().dom.getContext('2d');
45355     },
45356     
45357     getImage: function(type)
45358     {
45359         if(this.is_empty) {
45360             return false;
45361         }
45362         
45363         // encryption ?
45364         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45365     },
45366     
45367     drawFromImage: function(img_src)
45368     {
45369         var img = new Image();
45370         
45371         img.onload = function(){
45372             this.canvasElCtx().drawImage(img, 0, 0);
45373         }.bind(this);
45374         
45375         img.src = img_src;
45376         
45377         this.is_empty = false;
45378     },
45379     
45380     selectImage: function()
45381     {
45382         this.fileEl().dom.click();
45383     },
45384     
45385     uploadImage: function(e)
45386     {
45387         var reader = new FileReader();
45388         
45389         reader.onload = function(e){
45390             var img = new Image();
45391             img.onload = function(){
45392                 this.reset();
45393                 this.canvasElCtx().drawImage(img, 0, 0);
45394             }.bind(this);
45395             img.src = e.target.result;
45396         }.bind(this);
45397         
45398         reader.readAsDataURL(e.target.files[0]);
45399     },
45400     
45401     // Bezier Point Constructor
45402     Point: (function () {
45403         function Point(x, y, time) {
45404             this.x = x;
45405             this.y = y;
45406             this.time = time || Date.now();
45407         }
45408         Point.prototype.distanceTo = function (start) {
45409             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45410         };
45411         Point.prototype.equals = function (other) {
45412             return this.x === other.x && this.y === other.y && this.time === other.time;
45413         };
45414         Point.prototype.velocityFrom = function (start) {
45415             return this.time !== start.time
45416             ? this.distanceTo(start) / (this.time - start.time)
45417             : 0;
45418         };
45419         return Point;
45420     }()),
45421     
45422     
45423     // Bezier Constructor
45424     Bezier: (function () {
45425         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45426             this.startPoint = startPoint;
45427             this.control2 = control2;
45428             this.control1 = control1;
45429             this.endPoint = endPoint;
45430             this.startWidth = startWidth;
45431             this.endWidth = endWidth;
45432         }
45433         Bezier.fromPoints = function (points, widths, scope) {
45434             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45435             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45436             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45437         };
45438         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45439             var dx1 = s1.x - s2.x;
45440             var dy1 = s1.y - s2.y;
45441             var dx2 = s2.x - s3.x;
45442             var dy2 = s2.y - s3.y;
45443             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45444             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45445             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45446             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45447             var dxm = m1.x - m2.x;
45448             var dym = m1.y - m2.y;
45449             var k = l2 / (l1 + l2);
45450             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45451             var tx = s2.x - cm.x;
45452             var ty = s2.y - cm.y;
45453             return {
45454                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45455                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45456             };
45457         };
45458         Bezier.prototype.length = function () {
45459             var steps = 10;
45460             var length = 0;
45461             var px;
45462             var py;
45463             for (var i = 0; i <= steps; i += 1) {
45464                 var t = i / steps;
45465                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45466                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45467                 if (i > 0) {
45468                     var xdiff = cx - px;
45469                     var ydiff = cy - py;
45470                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45471                 }
45472                 px = cx;
45473                 py = cy;
45474             }
45475             return length;
45476         };
45477         Bezier.prototype.point = function (t, start, c1, c2, end) {
45478             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45479             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45480             + (3.0 * c2 * (1.0 - t) * t * t)
45481             + (end * t * t * t);
45482         };
45483         return Bezier;
45484     }()),
45485     
45486     throttleStroke: function(fn, wait) {
45487       if (wait === void 0) { wait = 250; }
45488       var previous = 0;
45489       var timeout = null;
45490       var result;
45491       var storedContext;
45492       var storedArgs;
45493       var later = function () {
45494           previous = Date.now();
45495           timeout = null;
45496           result = fn.apply(storedContext, storedArgs);
45497           if (!timeout) {
45498               storedContext = null;
45499               storedArgs = [];
45500           }
45501       };
45502       return function wrapper() {
45503           var args = [];
45504           for (var _i = 0; _i < arguments.length; _i++) {
45505               args[_i] = arguments[_i];
45506           }
45507           var now = Date.now();
45508           var remaining = wait - (now - previous);
45509           storedContext = this;
45510           storedArgs = args;
45511           if (remaining <= 0 || remaining > wait) {
45512               if (timeout) {
45513                   clearTimeout(timeout);
45514                   timeout = null;
45515               }
45516               previous = now;
45517               result = fn.apply(storedContext, storedArgs);
45518               if (!timeout) {
45519                   storedContext = null;
45520                   storedArgs = [];
45521               }
45522           }
45523           else if (!timeout) {
45524               timeout = window.setTimeout(later, remaining);
45525           }
45526           return result;
45527       };
45528   }
45529   
45530 });
45531
45532  
45533
45534